From e5adfa2d64f8c92cbb397cd0af4203c19de8f939 Mon Sep 17 00:00:00 2001 From: Justine Pelletreau Date: Wed, 16 Mar 2022 15:02:53 +0100 Subject: [PATCH] WIP --- __pycache__/app.cpython-310.pyc | Bin 0 -> 327 bytes app.py | 84 + myvenv/bin/Activate.ps1 | 247 + myvenv/bin/activate | 69 + myvenv/bin/activate.csh | 26 + myvenv/bin/activate.fish | 66 + myvenv/bin/flask | 8 + myvenv/bin/pip | 8 + myvenv/bin/pip3 | 8 + myvenv/bin/pip3.10 | 8 + myvenv/bin/python | 1 + myvenv/bin/python3 | 1 + myvenv/bin/python3.10 | 1 + .../Flask-2.0.3.dist-info/INSTALLER | 1 + .../Flask-2.0.3.dist-info/LICENSE.rst | 28 + .../Flask-2.0.3.dist-info/METADATA | 125 + .../Flask-2.0.3.dist-info/RECORD | 52 + .../Flask-2.0.3.dist-info/REQUESTED | 0 .../site-packages/Flask-2.0.3.dist-info/WHEEL | 5 + .../Flask-2.0.3.dist-info/entry_points.txt | 2 + .../Flask-2.0.3.dist-info/top_level.txt | 1 + .../Jinja2-3.0.3.dist-info/INSTALLER | 1 + .../Jinja2-3.0.3.dist-info/LICENSE.rst | 28 + .../Jinja2-3.0.3.dist-info/METADATA | 113 + .../Jinja2-3.0.3.dist-info/RECORD | 58 + .../Jinja2-3.0.3.dist-info/WHEEL | 5 + .../Jinja2-3.0.3.dist-info/entry_points.txt | 3 + .../Jinja2-3.0.3.dist-info/top_level.txt | 1 + .../MarkupSafe-2.1.1.dist-info/INSTALLER | 1 + .../MarkupSafe-2.1.1.dist-info/LICENSE.rst | 28 + .../MarkupSafe-2.1.1.dist-info/METADATA | 101 + .../MarkupSafe-2.1.1.dist-info/RECORD | 14 + .../MarkupSafe-2.1.1.dist-info/WHEEL | 6 + .../MarkupSafe-2.1.1.dist-info/top_level.txt | 1 + .../Werkzeug-2.0.3.dist-info/INSTALLER | 1 + .../Werkzeug-2.0.3.dist-info/LICENSE.rst | 28 + .../Werkzeug-2.0.3.dist-info/METADATA | 129 + .../Werkzeug-2.0.3.dist-info/RECORD | 111 + .../Werkzeug-2.0.3.dist-info/WHEEL | 5 + .../Werkzeug-2.0.3.dist-info/top_level.txt | 1 + .../site-packages/_distutils_hack/__init__.py | 128 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 5140 bytes .../__pycache__/override.cpython-310.pyc | Bin 0 -> 246 bytes .../site-packages/_distutils_hack/override.py | 1 + .../click-8.0.4.dist-info/INSTALLER | 1 + .../click-8.0.4.dist-info/LICENSE.rst | 28 + .../click-8.0.4.dist-info/METADATA | 111 + .../click-8.0.4.dist-info/RECORD | 41 + .../site-packages/click-8.0.4.dist-info/WHEEL | 5 + .../click-8.0.4.dist-info/top_level.txt | 1 + .../site-packages/click/__init__.py | 75 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 2703 bytes .../click/__pycache__/_compat.cpython-310.pyc | Bin 0 -> 15764 bytes .../__pycache__/_termui_impl.cpython-310.pyc | Bin 0 -> 16088 bytes .../__pycache__/_textwrap.cpython-310.pyc | Bin 0 -> 1563 bytes .../__pycache__/_unicodefun.cpython-310.pyc | Bin 0 -> 2330 bytes .../__pycache__/_winconsole.cpython-310.pyc | Bin 0 -> 7681 bytes .../click/__pycache__/core.cpython-310.pyc | Bin 0 -> 89082 bytes .../__pycache__/decorators.cpython-310.pyc | Bin 0 -> 14326 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 10211 bytes .../__pycache__/formatting.cpython-310.pyc | Bin 0 -> 9476 bytes .../click/__pycache__/globals.cpython-310.pyc | Bin 0 -> 2448 bytes .../click/__pycache__/parser.cpython-310.pyc | Bin 0 -> 13691 bytes .../shell_completion.cpython-310.pyc | Bin 0 -> 16558 bytes .../click/__pycache__/termui.cpython-310.pyc | Bin 0 -> 26811 bytes .../click/__pycache__/testing.cpython-310.pyc | Bin 0 -> 15206 bytes .../click/__pycache__/types.cpython-310.pyc | Bin 0 -> 32892 bytes .../click/__pycache__/utils.cpython-310.pyc | Bin 0 -> 17996 bytes .../python3.10/site-packages/click/_compat.py | 626 ++ .../site-packages/click/_termui_impl.py | 717 ++ .../site-packages/click/_textwrap.py | 49 + .../site-packages/click/_unicodefun.py | 100 + .../site-packages/click/_winconsole.py | 279 + .../python3.10/site-packages/click/core.py | 2953 ++++++ .../site-packages/click/decorators.py | 436 + .../site-packages/click/exceptions.py | 287 + .../site-packages/click/formatting.py | 301 + .../python3.10/site-packages/click/globals.py | 68 + .../python3.10/site-packages/click/parser.py | 529 ++ .../python3.10/site-packages/click/py.typed | 0 .../site-packages/click/shell_completion.py | 581 ++ .../python3.10/site-packages/click/termui.py | 806 ++ .../python3.10/site-packages/click/testing.py | 479 + .../python3.10/site-packages/click/types.py | 1049 ++ .../python3.10/site-packages/click/utils.py | 588 ++ .../site-packages/distutils-precedence.pth | 1 + .../site-packages/flask/__init__.py | 46 + .../site-packages/flask/__main__.py | 3 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1894 bytes .../__pycache__/__main__.cpython-310.pyc | Bin 0 -> 225 bytes .../flask/__pycache__/app.cpython-310.pyc | Bin 0 -> 63165 bytes .../__pycache__/blueprints.cpython-310.pyc | Bin 0 -> 21629 bytes .../flask/__pycache__/cli.cpython-310.pyc | Bin 0 -> 27289 bytes .../flask/__pycache__/config.cpython-310.pyc | Bin 0 -> 11701 bytes .../flask/__pycache__/ctx.cpython-310.pyc | Bin 0 -> 15568 bytes .../__pycache__/debughelpers.cpython-310.pyc | Bin 0 -> 6500 bytes .../flask/__pycache__/globals.cpython-310.pyc | Bin 0 -> 1804 bytes .../flask/__pycache__/helpers.cpython-310.pyc | Bin 0 -> 27442 bytes .../flask/__pycache__/logging.cpython-310.pyc | Bin 0 -> 2465 bytes .../__pycache__/scaffold.cpython-310.pyc | Bin 0 -> 24768 bytes .../__pycache__/sessions.cpython-310.pyc | Bin 0 -> 13617 bytes .../flask/__pycache__/signals.cpython-310.pyc | Bin 0 -> 2404 bytes .../__pycache__/templating.cpython-310.pyc | Bin 0 -> 5545 bytes .../flask/__pycache__/testing.cpython-310.pyc | Bin 0 -> 9390 bytes .../flask/__pycache__/typing.cpython-310.pyc | Bin 0 -> 1428 bytes .../flask/__pycache__/views.cpython-310.pyc | Bin 0 -> 5035 bytes .../__pycache__/wrappers.cpython-310.pyc | Bin 0 -> 5045 bytes .../lib/python3.10/site-packages/flask/app.py | 2091 ++++ .../site-packages/flask/blueprints.py | 609 ++ .../lib/python3.10/site-packages/flask/cli.py | 999 ++ .../python3.10/site-packages/flask/config.py | 295 + .../lib/python3.10/site-packages/flask/ctx.py | 489 + .../site-packages/flask/debughelpers.py | 172 + .../python3.10/site-packages/flask/globals.py | 59 + .../python3.10/site-packages/flask/helpers.py | 836 ++ .../site-packages/flask/json/__init__.py | 363 + .../json/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 11584 bytes .../json/__pycache__/tag.cpython-310.pyc | Bin 0 -> 11003 bytes .../site-packages/flask/json/tag.py | 312 + .../python3.10/site-packages/flask/logging.py | 74 + .../python3.10/site-packages/flask/py.typed | 0 .../site-packages/flask/scaffold.py | 875 ++ .../site-packages/flask/sessions.py | 416 + .../python3.10/site-packages/flask/signals.py | 56 + .../site-packages/flask/templating.py | 165 + .../python3.10/site-packages/flask/testing.py | 298 + .../python3.10/site-packages/flask/typing.py | 49 + .../python3.10/site-packages/flask/views.py | 158 + .../site-packages/flask/wrappers.py | 167 + .../itsdangerous-2.1.1.dist-info/INSTALLER | 1 + .../itsdangerous-2.1.1.dist-info/LICENSE.rst | 28 + .../itsdangerous-2.1.1.dist-info/METADATA | 97 + .../itsdangerous-2.1.1.dist-info/RECORD | 23 + .../itsdangerous-2.1.1.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/itsdangerous/__init__.py | 19 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 878 bytes .../__pycache__/_json.cpython-310.pyc | Bin 0 -> 933 bytes .../__pycache__/encoding.cpython-310.pyc | Bin 0 -> 1882 bytes .../__pycache__/exc.cpython-310.pyc | Bin 0 -> 3421 bytes .../__pycache__/serializer.cpython-310.pyc | Bin 0 -> 9705 bytes .../__pycache__/signer.cpython-310.pyc | Bin 0 -> 8473 bytes .../__pycache__/timed.cpython-310.pyc | Bin 0 -> 6470 bytes .../__pycache__/url_safe.cpython-310.pyc | Bin 0 -> 2706 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 | 233 + .../site-packages/itsdangerous/url_safe.py | 80 + .../site-packages/jinja2/__init__.py | 45 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1919 bytes .../__pycache__/_identifier.cpython-310.pyc | Bin 0 -> 1906 bytes .../__pycache__/async_utils.cpython-310.pyc | Bin 0 -> 2503 bytes .../__pycache__/bccache.cpython-310.pyc | Bin 0 -> 13361 bytes .../__pycache__/compiler.cpython-310.pyc | Bin 0 -> 54608 bytes .../__pycache__/constants.cpython-310.pyc | Bin 0 -> 1550 bytes .../jinja2/__pycache__/debug.cpython-310.pyc | Bin 0 -> 5459 bytes .../__pycache__/defaults.cpython-310.pyc | Bin 0 -> 1350 bytes .../__pycache__/environment.cpython-310.pyc | Bin 0 -> 53151 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 5549 bytes .../jinja2/__pycache__/ext.cpython-310.pyc | Bin 0 -> 26452 bytes .../__pycache__/filters.cpython-310.pyc | Bin 0 -> 50783 bytes .../__pycache__/idtracking.cpython-310.pyc | Bin 0 -> 11125 bytes .../jinja2/__pycache__/lexer.cpython-310.pyc | Bin 0 -> 20504 bytes .../__pycache__/loaders.cpython-310.pyc | Bin 0 -> 20473 bytes .../jinja2/__pycache__/meta.cpython-310.pyc | Bin 0 -> 3830 bytes .../__pycache__/nativetypes.cpython-310.pyc | Bin 0 -> 4909 bytes .../jinja2/__pycache__/nodes.cpython-310.pyc | Bin 0 -> 40340 bytes .../__pycache__/optimizer.cpython-310.pyc | Bin 0 -> 1970 bytes .../jinja2/__pycache__/parser.cpython-310.pyc | Bin 0 -> 27713 bytes .../__pycache__/runtime.cpython-310.pyc | Bin 0 -> 33126 bytes .../__pycache__/sandbox.cpython-310.pyc | Bin 0 -> 11984 bytes .../jinja2/__pycache__/tests.cpython-310.pyc | Bin 0 -> 6704 bytes .../jinja2/__pycache__/utils.cpython-310.pyc | Bin 0 -> 27392 bytes .../__pycache__/visitor.cpython-310.pyc | Bin 0 -> 3960 bytes .../site-packages/jinja2/_identifier.py | 6 + .../site-packages/jinja2/async_utils.py | 75 + .../site-packages/jinja2/bccache.py | 364 + .../site-packages/jinja2/compiler.py | 1957 ++++ .../site-packages/jinja2/constants.py | 20 + .../python3.10/site-packages/jinja2/debug.py | 259 + .../site-packages/jinja2/defaults.py | 48 + .../site-packages/jinja2/environment.py | 1661 ++++ .../site-packages/jinja2/exceptions.py | 166 + .../python3.10/site-packages/jinja2/ext.py | 879 ++ .../site-packages/jinja2/filters.py | 1824 ++++ .../site-packages/jinja2/idtracking.py | 318 + .../python3.10/site-packages/jinja2/lexer.py | 869 ++ .../site-packages/jinja2/loaders.py | 652 ++ .../python3.10/site-packages/jinja2/meta.py | 111 + .../site-packages/jinja2/nativetypes.py | 124 + .../python3.10/site-packages/jinja2/nodes.py | 1204 +++ .../site-packages/jinja2/optimizer.py | 47 + .../python3.10/site-packages/jinja2/parser.py | 1040 ++ .../python3.10/site-packages/jinja2/py.typed | 0 .../site-packages/jinja2/runtime.py | 1104 +++ .../site-packages/jinja2/sandbox.py | 428 + .../python3.10/site-packages/jinja2/tests.py | 255 + .../python3.10/site-packages/jinja2/utils.py | 854 ++ .../site-packages/jinja2/visitor.py | 92 + .../site-packages/markupsafe/__init__.py | 295 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 10644 bytes .../__pycache__/_native.cpython-310.pyc | Bin 0 -> 2014 bytes .../site-packages/markupsafe/_native.py | 63 + .../site-packages/markupsafe/_speedups.c | 320 + .../_speedups.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 44224 bytes .../site-packages/markupsafe/_speedups.pyi | 9 + .../site-packages/markupsafe/py.typed | 0 .../pip-21.2.4.dist-info/INSTALLER | 1 + .../pip-21.2.4.dist-info/LICENSE.txt | 20 + .../pip-21.2.4.dist-info/METADATA | 92 + .../site-packages/pip-21.2.4.dist-info/RECORD | 795 ++ .../pip-21.2.4.dist-info/REQUESTED | 0 .../site-packages/pip-21.2.4.dist-info/WHEEL | 5 + .../pip-21.2.4.dist-info/entry_points.txt | 5 + .../pip-21.2.4.dist-info/top_level.txt | 1 + .../python3.10/site-packages/pip/__init__.py | 13 + .../python3.10/site-packages/pip/__main__.py | 31 + .../pip/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 639 bytes .../pip/__pycache__/__main__.cpython-310.pyc | Bin 0 -> 601 bytes .../site-packages/pip/_internal/__init__.py | 19 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 760 bytes .../__pycache__/build_env.cpython-310.pyc | Bin 0 -> 9143 bytes .../__pycache__/cache.cpython-310.pyc | Bin 0 -> 7764 bytes .../__pycache__/configuration.cpython-310.pyc | Bin 0 -> 10727 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 15299 bytes .../__pycache__/main.cpython-310.pyc | Bin 0 -> 584 bytes .../__pycache__/pyproject.cpython-310.pyc | Bin 0 -> 3457 bytes .../self_outdated_check.cpython-310.pyc | Bin 0 -> 4363 bytes .../__pycache__/wheel_builder.cpython-310.pyc | Bin 0 -> 8293 bytes .../site-packages/pip/_internal/build_env.py | 294 + .../site-packages/pip/_internal/cache.py | 287 + .../pip/_internal/cli/__init__.py | 4 + .../cli/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 280 bytes .../autocompletion.cpython-310.pyc | Bin 0 -> 5189 bytes .../__pycache__/base_command.cpython-310.pyc | Bin 0 -> 5987 bytes .../__pycache__/cmdoptions.cpython-310.pyc | Bin 0 -> 22346 bytes .../command_context.cpython-310.pyc | Bin 0 -> 1314 bytes .../cli/__pycache__/main.cpython-310.pyc | Bin 0 -> 1378 bytes .../__pycache__/main_parser.cpython-310.pyc | Bin 0 -> 2164 bytes .../cli/__pycache__/parser.cpython-310.pyc | Bin 0 -> 9951 bytes .../__pycache__/progress_bars.cpython-310.pyc | Bin 0 -> 7619 bytes .../__pycache__/req_command.cpython-310.pyc | Bin 0 -> 12148 bytes .../cli/__pycache__/spinners.cpython-310.pyc | Bin 0 -> 4954 bytes .../__pycache__/status_codes.cpython-310.pyc | Bin 0 -> 359 bytes .../pip/_internal/cli/autocompletion.py | 163 + .../pip/_internal/cli/base_command.py | 214 + .../pip/_internal/cli/cmdoptions.py | 1009 ++ .../pip/_internal/cli/command_context.py | 27 + .../site-packages/pip/_internal/cli/main.py | 70 + .../pip/_internal/cli/main_parser.py | 87 + .../site-packages/pip/_internal/cli/parser.py | 292 + .../pip/_internal/cli/progress_bars.py | 250 + .../pip/_internal/cli/req_command.py | 453 + .../pip/_internal/cli/spinners.py | 157 + .../pip/_internal/cli/status_codes.py | 6 + .../pip/_internal/commands/__init__.py | 112 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 3107 bytes .../__pycache__/cache.cpython-310.pyc | Bin 0 -> 6048 bytes .../__pycache__/check.cpython-310.pyc | Bin 0 -> 1579 bytes .../__pycache__/completion.cpython-310.pyc | Bin 0 -> 3142 bytes .../__pycache__/configuration.cpython-310.pyc | Bin 0 -> 8344 bytes .../__pycache__/debug.cpython-310.pyc | Bin 0 -> 6699 bytes .../__pycache__/download.cpython-310.pyc | Bin 0 -> 3996 bytes .../__pycache__/freeze.cpython-310.pyc | Bin 0 -> 2638 bytes .../commands/__pycache__/hash.cpython-310.pyc | Bin 0 -> 2154 bytes .../commands/__pycache__/help.cpython-310.pyc | Bin 0 -> 1317 bytes .../__pycache__/index.cpython-310.pyc | Bin 0 -> 4563 bytes .../__pycache__/install.cpython-310.pyc | Bin 0 -> 17769 bytes .../commands/__pycache__/list.cpython-310.pyc | Bin 0 -> 10068 bytes .../__pycache__/search.cpython-310.pyc | Bin 0 -> 5364 bytes .../commands/__pycache__/show.cpython-310.pyc | Bin 0 -> 8442 bytes .../__pycache__/uninstall.cpython-310.pyc | Bin 0 -> 3112 bytes .../__pycache__/wheel.cpython-310.pyc | Bin 0 -> 4857 bytes .../pip/_internal/commands/cache.py | 216 + .../pip/_internal/commands/check.py | 47 + .../pip/_internal/commands/completion.py | 91 + .../pip/_internal/commands/configuration.py | 266 + .../pip/_internal/commands/debug.py | 204 + .../pip/_internal/commands/download.py | 139 + .../pip/_internal/commands/freeze.py | 84 + .../pip/_internal/commands/hash.py | 55 + .../pip/_internal/commands/help.py | 41 + .../pip/_internal/commands/index.py | 139 + .../pip/_internal/commands/install.py | 750 ++ .../pip/_internal/commands/list.py | 337 + .../pip/_internal/commands/search.py | 164 + .../pip/_internal/commands/show.py | 234 + .../pip/_internal/commands/uninstall.py | 100 + .../pip/_internal/commands/wheel.py | 176 + .../pip/_internal/configuration.py | 403 + .../pip/_internal/distributions/__init__.py | 21 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 807 bytes .../__pycache__/base.cpython-310.pyc | Bin 0 -> 1916 bytes .../__pycache__/installed.cpython-310.pyc | Bin 0 -> 1254 bytes .../__pycache__/sdist.cpython-310.pyc | Bin 0 -> 3594 bytes .../__pycache__/wheel.cpython-310.pyc | Bin 0 -> 1591 bytes .../pip/_internal/distributions/base.py | 38 + .../pip/_internal/distributions/installed.py | 22 + .../pip/_internal/distributions/sdist.py | 95 + .../pip/_internal/distributions/wheel.py | 34 + .../site-packages/pip/_internal/exceptions.py | 397 + .../pip/_internal/index/__init__.py | 2 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 234 bytes .../__pycache__/collector.cpython-310.pyc | Bin 0 -> 15951 bytes .../package_finder.cpython-310.pyc | Bin 0 -> 28156 bytes .../index/__pycache__/sources.cpython-310.pyc | Bin 0 -> 7127 bytes .../pip/_internal/index/collector.py | 534 ++ .../pip/_internal/index/package_finder.py | 982 ++ .../pip/_internal/index/sources.py | 224 + .../pip/_internal/locations/__init__.py | 408 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 9958 bytes .../__pycache__/_distutils.cpython-310.pyc | Bin 0 -> 4662 bytes .../__pycache__/_sysconfig.cpython-310.pyc | Bin 0 -> 6245 bytes .../__pycache__/base.cpython-310.pyc | Bin 0 -> 1544 bytes .../pip/_internal/locations/_distutils.py | 169 + .../pip/_internal/locations/_sysconfig.py | 219 + .../pip/_internal/locations/base.py | 52 + .../site-packages/pip/_internal/main.py | 13 + .../pip/_internal/metadata/__init__.py | 48 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1845 bytes .../metadata/__pycache__/base.cpython-310.pyc | Bin 0 -> 9236 bytes .../__pycache__/pkg_resources.cpython-310.pyc | Bin 0 -> 6088 bytes .../pip/_internal/metadata/base.py | 242 + .../pip/_internal/metadata/pkg_resources.py | 153 + .../pip/_internal/models/__init__.py | 2 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 268 bytes .../__pycache__/candidate.cpython-310.pyc | Bin 0 -> 1421 bytes .../__pycache__/direct_url.cpython-310.pyc | Bin 0 -> 7119 bytes .../format_control.cpython-310.pyc | Bin 0 -> 2760 bytes .../models/__pycache__/index.cpython-310.pyc | Bin 0 -> 1246 bytes .../models/__pycache__/link.cpython-310.pyc | Bin 0 -> 10174 bytes .../models/__pycache__/scheme.cpython-310.pyc | Bin 0 -> 1036 bytes .../__pycache__/search_scope.cpython-310.pyc | Bin 0 -> 3497 bytes .../selection_prefs.cpython-310.pyc | Bin 0 -> 1698 bytes .../__pycache__/target_python.cpython-310.pyc | Bin 0 -> 3455 bytes .../models/__pycache__/wheel.cpython-310.pyc | Bin 0 -> 4352 bytes .../pip/_internal/models/candidate.py | 31 + .../pip/_internal/models/direct_url.py | 220 + .../pip/_internal/models/format_control.py | 84 + .../pip/_internal/models/index.py | 32 + .../pip/_internal/models/link.py | 288 + .../pip/_internal/models/scheme.py | 31 + .../pip/_internal/models/search_scope.py | 126 + .../pip/_internal/models/selection_prefs.py | 46 + .../pip/_internal/models/target_python.py | 111 + .../pip/_internal/models/wheel.py | 92 + .../pip/_internal/network/__init__.py | 2 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 256 bytes .../network/__pycache__/auth.cpython-310.pyc | Bin 0 -> 7501 bytes .../network/__pycache__/cache.cpython-310.pyc | Bin 0 -> 2903 bytes .../__pycache__/download.cpython-310.pyc | Bin 0 -> 5470 bytes .../__pycache__/lazy_wheel.cpython-310.pyc | Bin 0 -> 8376 bytes .../__pycache__/session.cpython-310.pyc | Bin 0 -> 10534 bytes .../network/__pycache__/utils.cpython-310.pyc | Bin 0 -> 1452 bytes .../__pycache__/xmlrpc.cpython-310.pyc | Bin 0 -> 2069 bytes .../pip/_internal/network/auth.py | 316 + .../pip/_internal/network/cache.py | 69 + .../pip/_internal/network/download.py | 184 + .../pip/_internal/network/lazy_wheel.py | 210 + .../pip/_internal/network/session.py | 454 + .../pip/_internal/network/utils.py | 96 + .../pip/_internal/network/xmlrpc.py | 60 + .../pip/_internal/operations/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 204 bytes .../__pycache__/check.cpython-310.pyc | Bin 0 -> 3963 bytes .../__pycache__/freeze.cpython-310.pyc | Bin 0 -> 6317 bytes .../__pycache__/prepare.cpython-310.pyc | Bin 0 -> 14276 bytes .../_internal/operations/build/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 210 bytes .../__pycache__/metadata.cpython-310.pyc | Bin 0 -> 1162 bytes .../metadata_legacy.cpython-310.pyc | Bin 0 -> 1954 bytes .../build/__pycache__/wheel.cpython-310.pyc | Bin 0 -> 1141 bytes .../__pycache__/wheel_legacy.cpython-310.pyc | Bin 0 -> 2539 bytes .../_internal/operations/build/metadata.py | 35 + .../operations/build/metadata_legacy.py | 74 + .../pip/_internal/operations/build/wheel.py | 38 + .../operations/build/wheel_legacy.py | 110 + .../pip/_internal/operations/check.py | 153 + .../pip/_internal/operations/freeze.py | 277 + .../_internal/operations/install/__init__.py | 2 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 268 bytes .../editable_legacy.cpython-310.pyc | Bin 0 -> 1337 bytes .../__pycache__/legacy.cpython-310.pyc | Bin 0 -> 3494 bytes .../install/__pycache__/wheel.cpython-310.pyc | Bin 0 -> 20435 bytes .../operations/install/editable_legacy.py | 47 + .../_internal/operations/install/legacy.py | 132 + .../pip/_internal/operations/install/wheel.py | 803 ++ .../pip/_internal/operations/prepare.py | 655 ++ .../site-packages/pip/_internal/pyproject.py | 183 + .../pip/_internal/req/__init__.py | 94 + .../req/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 2608 bytes .../__pycache__/constructors.cpython-310.pyc | Bin 0 -> 11715 bytes .../req/__pycache__/req_file.cpython-310.pyc | Bin 0 -> 13493 bytes .../__pycache__/req_install.cpython-310.pyc | Bin 0 -> 21469 bytes .../req/__pycache__/req_set.cpython-310.pyc | Bin 0 -> 5847 bytes .../__pycache__/req_tracker.cpython-310.pyc | Bin 0 -> 4342 bytes .../__pycache__/req_uninstall.cpython-310.pyc | Bin 0 -> 18843 bytes .../pip/_internal/req/constructors.py | 474 + .../pip/_internal/req/req_file.py | 528 ++ .../pip/_internal/req/req_install.py | 846 ++ .../pip/_internal/req/req_set.py | 190 + .../pip/_internal/req/req_tracker.py | 130 + .../pip/_internal/req/req_uninstall.py | 629 ++ .../pip/_internal/resolution/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 204 bytes .../__pycache__/base.cpython-310.pyc | Bin 0 -> 1029 bytes .../pip/_internal/resolution/base.py | 18 + .../_internal/resolution/legacy/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 211 bytes .../__pycache__/resolver.cpython-310.pyc | Bin 0 -> 12111 bytes .../_internal/resolution/legacy/resolver.py | 453 + .../resolution/resolvelib/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 215 bytes .../__pycache__/base.cpython-310.pyc | Bin 0 -> 6595 bytes .../__pycache__/candidates.cpython-310.pyc | Bin 0 -> 18899 bytes .../__pycache__/factory.cpython-310.pyc | Bin 0 -> 18568 bytes .../found_candidates.cpython-310.pyc | Bin 0 -> 4833 bytes .../__pycache__/provider.cpython-310.pyc | Bin 0 -> 6814 bytes .../__pycache__/reporter.cpython-310.pyc | Bin 0 -> 3242 bytes .../__pycache__/requirements.cpython-310.pyc | Bin 0 -> 7473 bytes .../__pycache__/resolver.cpython-310.pyc | Bin 0 -> 7814 bytes .../_internal/resolution/resolvelib/base.py | 144 + .../resolution/resolvelib/candidates.py | 555 ++ .../resolution/resolvelib/factory.py | 700 ++ .../resolution/resolvelib/found_candidates.py | 142 + .../resolution/resolvelib/provider.py | 197 + .../resolution/resolvelib/reporter.py | 69 + .../resolution/resolvelib/requirements.py | 166 + .../resolution/resolvelib/resolver.py | 272 + .../pip/_internal/self_outdated_check.py | 187 + .../pip/_internal/utils/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 199 bytes .../utils/__pycache__/_log.cpython-310.pyc | Bin 0 -> 1527 bytes .../utils/__pycache__/appdirs.cpython-310.pyc | Bin 0 -> 1331 bytes .../utils/__pycache__/compat.cpython-310.pyc | Bin 0 -> 1515 bytes .../compatibility_tags.cpython-310.pyc | Bin 0 -> 4131 bytes .../__pycache__/datetime.cpython-310.pyc | Bin 0 -> 522 bytes .../__pycache__/deprecation.cpython-310.pyc | Bin 0 -> 3047 bytes .../direct_url_helpers.cpython-310.pyc | Bin 0 -> 1839 bytes .../distutils_args.cpython-310.pyc | Bin 0 -> 1106 bytes .../__pycache__/encoding.cpython-310.pyc | Bin 0 -> 1312 bytes .../__pycache__/entrypoints.cpython-310.pyc | Bin 0 -> 1309 bytes .../__pycache__/filesystem.cpython-310.pyc | Bin 0 -> 5167 bytes .../__pycache__/filetypes.cpython-310.pyc | Bin 0 -> 811 bytes .../utils/__pycache__/glibc.cpython-310.pyc | Bin 0 -> 1621 bytes .../utils/__pycache__/hashes.cpython-310.pyc | Bin 0 -> 5023 bytes .../inject_securetransport.cpython-310.pyc | Bin 0 -> 976 bytes .../utils/__pycache__/logging.cpython-310.pyc | Bin 0 -> 9084 bytes .../utils/__pycache__/misc.cpython-310.pyc | Bin 0 -> 21688 bytes .../utils/__pycache__/models.cpython-310.pyc | Bin 0 -> 1841 bytes .../__pycache__/packaging.cpython-310.pyc | Bin 0 -> 2529 bytes .../__pycache__/parallel.cpython-310.pyc | Bin 0 -> 3040 bytes .../__pycache__/pkg_resources.cpython-310.pyc | Bin 0 -> 1709 bytes .../setuptools_build.cpython-310.pyc | Bin 0 -> 2975 bytes .../__pycache__/subprocess.cpython-310.pyc | Bin 0 -> 5731 bytes .../__pycache__/temp_dir.cpython-310.pyc | Bin 0 -> 6891 bytes .../__pycache__/unpacking.cpython-310.pyc | Bin 0 -> 6467 bytes .../utils/__pycache__/urls.cpython-310.pyc | Bin 0 -> 1543 bytes .../__pycache__/virtualenv.cpython-310.pyc | Bin 0 -> 3226 bytes .../utils/__pycache__/wheel.cpython-310.pyc | Bin 0 -> 5927 bytes .../site-packages/pip/_internal/utils/_log.py | 38 + .../pip/_internal/utils/appdirs.py | 35 + .../pip/_internal/utils/compat.py | 63 + .../pip/_internal/utils/compatibility_tags.py | 168 + .../pip/_internal/utils/datetime.py | 11 + .../pip/_internal/utils/deprecation.py | 104 + .../pip/_internal/utils/direct_url_helpers.py | 79 + .../pip/_internal/utils/distutils_args.py | 42 + .../pip/_internal/utils/encoding.py | 36 + .../pip/_internal/utils/entrypoints.py | 27 + .../pip/_internal/utils/filesystem.py | 182 + .../pip/_internal/utils/filetypes.py | 28 + .../pip/_internal/utils/glibc.py | 92 + .../pip/_internal/utils/hashes.py | 165 + .../_internal/utils/inject_securetransport.py | 36 + .../pip/_internal/utils/logging.py | 391 + .../site-packages/pip/_internal/utils/misc.py | 828 ++ .../pip/_internal/utils/models.py | 47 + .../pip/_internal/utils/packaging.py | 89 + .../pip/_internal/utils/parallel.py | 101 + .../pip/_internal/utils/pkg_resources.py | 40 + .../pip/_internal/utils/setuptools_build.py | 173 + .../pip/_internal/utils/subprocess.py | 281 + .../pip/_internal/utils/temp_dir.py | 260 + .../pip/_internal/utils/unpacking.py | 267 + .../site-packages/pip/_internal/utils/urls.py | 65 + .../pip/_internal/utils/virtualenv.py | 111 + .../pip/_internal/utils/wheel.py | 189 + .../pip/_internal/vcs/__init__.py | 15 + .../vcs/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 522 bytes .../vcs/__pycache__/bazaar.cpython-310.pyc | Bin 0 -> 3010 bytes .../vcs/__pycache__/git.cpython-310.pyc | Bin 0 -> 11733 bytes .../vcs/__pycache__/mercurial.cpython-310.pyc | Bin 0 -> 4655 bytes .../__pycache__/subversion.cpython-310.pyc | Bin 0 -> 7965 bytes .../versioncontrol.cpython-310.pyc | Bin 0 -> 19468 bytes .../site-packages/pip/_internal/vcs/bazaar.py | 96 + .../site-packages/pip/_internal/vcs/git.py | 506 + .../pip/_internal/vcs/mercurial.py | 158 + .../pip/_internal/vcs/subversion.py | 329 + .../pip/_internal/vcs/versioncontrol.py | 722 ++ .../pip/_internal/wheel_builder.py | 360 + .../site-packages/pip/_vendor/__init__.py | 111 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 2917 bytes .../__pycache__/appdirs.cpython-310.pyc | Bin 0 -> 21191 bytes .../__pycache__/distro.cpython-310.pyc | Bin 0 -> 36651 bytes .../__pycache__/pyparsing.cpython-310.pyc | Bin 0 -> 237859 bytes .../_vendor/__pycache__/six.cpython-310.pyc | Bin 0 -> 27597 bytes .../site-packages/pip/_vendor/appdirs.py | 633 ++ .../pip/_vendor/cachecontrol/__init__.py | 11 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 557 bytes .../__pycache__/_cmd.cpython-310.pyc | Bin 0 -> 1588 bytes .../__pycache__/adapter.cpython-310.pyc | Bin 0 -> 3105 bytes .../__pycache__/cache.cpython-310.pyc | Bin 0 -> 1836 bytes .../__pycache__/compat.cpython-310.pyc | Bin 0 -> 764 bytes .../__pycache__/controller.cpython-310.pyc | Bin 0 -> 7785 bytes .../__pycache__/filewrapper.cpython-310.pyc | Bin 0 -> 2185 bytes .../__pycache__/heuristics.cpython-310.pyc | Bin 0 -> 4724 bytes .../__pycache__/serialize.cpython-310.pyc | Bin 0 -> 4231 bytes .../__pycache__/wrapper.cpython-310.pyc | Bin 0 -> 695 bytes .../pip/_vendor/cachecontrol/_cmd.py | 57 + .../pip/_vendor/cachecontrol/adapter.py | 133 + .../pip/_vendor/cachecontrol/cache.py | 39 + .../_vendor/cachecontrol/caches/__init__.py | 2 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 301 bytes .../__pycache__/file_cache.cpython-310.pyc | Bin 0 -> 3359 bytes .../__pycache__/redis_cache.cpython-310.pyc | Bin 0 -> 1581 bytes .../_vendor/cachecontrol/caches/file_cache.py | 146 + .../cachecontrol/caches/redis_cache.py | 33 + .../pip/_vendor/cachecontrol/compat.py | 29 + .../pip/_vendor/cachecontrol/controller.py | 376 + .../pip/_vendor/cachecontrol/filewrapper.py | 80 + .../pip/_vendor/cachecontrol/heuristics.py | 135 + .../pip/_vendor/cachecontrol/serialize.py | 188 + .../pip/_vendor/cachecontrol/wrapper.py | 29 + .../pip/_vendor/certifi/__init__.py | 3 + .../pip/_vendor/certifi/__main__.py | 12 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 283 bytes .../__pycache__/__main__.cpython-310.pyc | Bin 0 -> 462 bytes .../certifi/__pycache__/core.cpython-310.pyc | Bin 0 -> 1557 bytes .../pip/_vendor/certifi/cacert.pem | 4257 +++++++++ .../site-packages/pip/_vendor/certifi/core.py | 76 + .../pip/_vendor/chardet/__init__.py | 83 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1907 bytes .../__pycache__/big5freq.cpython-310.pyc | Bin 0 -> 27186 bytes .../__pycache__/big5prober.cpython-310.pyc | Bin 0 -> 1137 bytes .../chardistribution.cpython-310.pyc | Bin 0 -> 5747 bytes .../charsetgroupprober.cpython-310.pyc | Bin 0 -> 2236 bytes .../__pycache__/charsetprober.cpython-310.pyc | Bin 0 -> 3490 bytes .../codingstatemachine.cpython-310.pyc | Bin 0 -> 2909 bytes .../__pycache__/compat.cpython-310.pyc | Bin 0 -> 408 bytes .../__pycache__/cp949prober.cpython-310.pyc | Bin 0 -> 1144 bytes .../chardet/__pycache__/enums.cpython-310.pyc | Bin 0 -> 2591 bytes .../__pycache__/escprober.cpython-310.pyc | Bin 0 -> 2638 bytes .../chardet/__pycache__/escsm.cpython-310.pyc | Bin 0 -> 8385 bytes .../__pycache__/eucjpprober.cpython-310.pyc | Bin 0 -> 2442 bytes .../__pycache__/euckrfreq.cpython-310.pyc | Bin 0 -> 12070 bytes .../__pycache__/euckrprober.cpython-310.pyc | Bin 0 -> 1145 bytes .../__pycache__/euctwfreq.cpython-310.pyc | Bin 0 -> 27190 bytes .../__pycache__/euctwprober.cpython-310.pyc | Bin 0 -> 1145 bytes .../__pycache__/gb2312freq.cpython-310.pyc | Bin 0 -> 19114 bytes .../__pycache__/gb2312prober.cpython-310.pyc | Bin 0 -> 1153 bytes .../__pycache__/hebrewprober.cpython-310.pyc | Bin 0 -> 3030 bytes .../__pycache__/jisfreq.cpython-310.pyc | Bin 0 -> 22142 bytes .../__pycache__/jpcntx.cpython-310.pyc | Bin 0 -> 37649 bytes .../langbulgarianmodel.cpython-310.pyc | Bin 0 -> 47930 bytes .../langgreekmodel.cpython-310.pyc | Bin 0 -> 46120 bytes .../langhebrewmodel.cpython-310.pyc | Bin 0 -> 44569 bytes .../langhungarianmodel.cpython-310.pyc | Bin 0 -> 47890 bytes .../langrussianmodel.cpython-310.pyc | Bin 0 -> 61023 bytes .../__pycache__/langthaimodel.cpython-310.pyc | Bin 0 -> 44745 bytes .../langturkishmodel.cpython-310.pyc | Bin 0 -> 44586 bytes .../__pycache__/latin1prober.cpython-310.pyc | Bin 0 -> 4436 bytes .../mbcharsetprober.cpython-310.pyc | Bin 0 -> 2257 bytes .../mbcsgroupprober.cpython-310.pyc | Bin 0 -> 1140 bytes .../__pycache__/mbcssm.cpython-310.pyc | Bin 0 -> 18767 bytes .../sbcharsetprober.cpython-310.pyc | Bin 0 -> 3086 bytes .../sbcsgroupprober.cpython-310.pyc | Bin 0 -> 1709 bytes .../__pycache__/sjisprober.cpython-310.pyc | Bin 0 -> 2480 bytes .../universaldetector.cpython-310.pyc | Bin 0 -> 5834 bytes .../__pycache__/utf8prober.cpython-310.pyc | Bin 0 -> 1989 bytes .../__pycache__/version.cpython-310.pyc | Bin 0 -> 446 bytes .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 233 + .../pip/_vendor/chardet/charsetgroupprober.py | 107 + .../pip/_vendor/chardet/charsetprober.py | 145 + .../pip/_vendor/chardet/cli/__init__.py | 1 + .../cli/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 203 bytes .../__pycache__/chardetect.cpython-310.pyc | Bin 0 -> 2702 bytes .../pip/_vendor/chardet/cli/chardetect.py | 84 + .../pip/_vendor/chardet/codingstatemachine.py | 88 + .../pip/_vendor/chardet/compat.py | 36 + .../pip/_vendor/chardet/cp949prober.py | 49 + .../pip/_vendor/chardet/enums.py | 76 + .../pip/_vendor/chardet/escprober.py | 101 + .../pip/_vendor/chardet/escsm.py | 246 + .../pip/_vendor/chardet/eucjpprober.py | 92 + .../pip/_vendor/chardet/euckrfreq.py | 195 + .../pip/_vendor/chardet/euckrprober.py | 47 + .../pip/_vendor/chardet/euctwfreq.py | 387 + .../pip/_vendor/chardet/euctwprober.py | 46 + .../pip/_vendor/chardet/gb2312freq.py | 283 + .../pip/_vendor/chardet/gb2312prober.py | 46 + .../pip/_vendor/chardet/hebrewprober.py | 292 + .../pip/_vendor/chardet/jisfreq.py | 325 + .../pip/_vendor/chardet/jpcntx.py | 233 + .../pip/_vendor/chardet/langbulgarianmodel.py | 4650 +++++++++ .../pip/_vendor/chardet/langgreekmodel.py | 4398 +++++++++ .../pip/_vendor/chardet/langhebrewmodel.py | 4383 +++++++++ .../pip/_vendor/chardet/langhungarianmodel.py | 4650 +++++++++ .../pip/_vendor/chardet/langrussianmodel.py | 5718 +++++++++++ .../pip/_vendor/chardet/langthaimodel.py | 4383 +++++++++ .../pip/_vendor/chardet/langturkishmodel.py | 4383 +++++++++ .../pip/_vendor/chardet/latin1prober.py | 145 + .../pip/_vendor/chardet/mbcharsetprober.py | 91 + .../pip/_vendor/chardet/mbcsgroupprober.py | 54 + .../pip/_vendor/chardet/mbcssm.py | 572 ++ .../pip/_vendor/chardet/metadata/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 208 bytes .../__pycache__/languages.cpython-310.pyc | Bin 0 -> 7970 bytes .../pip/_vendor/chardet/metadata/languages.py | 310 + .../pip/_vendor/chardet/sbcharsetprober.py | 145 + .../pip/_vendor/chardet/sbcsgroupprober.py | 83 + .../pip/_vendor/chardet/sjisprober.py | 92 + .../pip/_vendor/chardet/universaldetector.py | 286 + .../pip/_vendor/chardet/utf8prober.py | 82 + .../pip/_vendor/chardet/version.py | 9 + .../pip/_vendor/colorama/__init__.py | 6 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 451 bytes .../colorama/__pycache__/ansi.cpython-310.pyc | Bin 0 -> 3012 bytes .../__pycache__/ansitowin32.cpython-310.pyc | Bin 0 -> 7910 bytes .../__pycache__/initialise.cpython-310.pyc | Bin 0 -> 1698 bytes .../__pycache__/win32.cpython-310.pyc | Bin 0 -> 3958 bytes .../__pycache__/winterm.cpython-310.pyc | Bin 0 -> 4575 bytes .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 258 + .../pip/_vendor/colorama/initialise.py | 80 + .../pip/_vendor/colorama/win32.py | 152 + .../pip/_vendor/colorama/winterm.py | 169 + .../pip/_vendor/distlib/__init__.py | 23 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1070 bytes .../__pycache__/compat.cpython-310.pyc | Bin 0 -> 31881 bytes .../__pycache__/database.cpython-310.pyc | Bin 0 -> 42624 bytes .../distlib/__pycache__/index.cpython-310.pyc | Bin 0 -> 17325 bytes .../__pycache__/locators.cpython-310.pyc | Bin 0 -> 38391 bytes .../__pycache__/manifest.cpython-310.pyc | Bin 0 -> 10238 bytes .../__pycache__/markers.cpython-310.pyc | Bin 0 -> 4428 bytes .../__pycache__/metadata.cpython-310.pyc | Bin 0 -> 26570 bytes .../__pycache__/resources.cpython-310.pyc | Bin 0 -> 11044 bytes .../__pycache__/scripts.cpython-310.pyc | Bin 0 -> 11113 bytes .../distlib/__pycache__/util.cpython-310.pyc | Bin 0 -> 52572 bytes .../__pycache__/version.cpython-310.pyc | Bin 0 -> 20151 bytes .../distlib/__pycache__/wheel.cpython-310.pyc | Bin 0 -> 27373 bytes .../pip/_vendor/distlib/_backport/__init__.py | 6 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 491 bytes .../__pycache__/misc.cpython-310.pyc | Bin 0 -> 1113 bytes .../__pycache__/shutil.cpython-310.pyc | Bin 0 -> 21550 bytes .../__pycache__/sysconfig.cpython-310.pyc | Bin 0 -> 15917 bytes .../__pycache__/tarfile.cpython-310.pyc | Bin 0 -> 62490 bytes .../pip/_vendor/distlib/_backport/misc.py | 41 + .../pip/_vendor/distlib/_backport/shutil.py | 764 ++ .../_vendor/distlib/_backport/sysconfig.cfg | 84 + .../_vendor/distlib/_backport/sysconfig.py | 786 ++ .../pip/_vendor/distlib/_backport/tarfile.py | 2607 +++++ .../pip/_vendor/distlib/compat.py | 1120 +++ .../pip/_vendor/distlib/database.py | 1339 +++ .../pip/_vendor/distlib/index.py | 509 + .../pip/_vendor/distlib/locators.py | 1300 +++ .../pip/_vendor/distlib/manifest.py | 393 + .../pip/_vendor/distlib/markers.py | 130 + .../pip/_vendor/distlib/metadata.py | 1058 +++ .../pip/_vendor/distlib/resources.py | 358 + .../pip/_vendor/distlib/scripts.py | 423 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 96768 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 105984 bytes .../site-packages/pip/_vendor/distlib/util.py | 1965 ++++ .../pip/_vendor/distlib/version.py | 739 ++ .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 90112 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 99840 bytes .../pip/_vendor/distlib/wheel.py | 1056 +++ .../site-packages/pip/_vendor/distro.py | 1230 +++ .../pip/_vendor/html5lib/__init__.py | 35 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1310 bytes .../__pycache__/_ihatexml.cpython-310.pyc | Bin 0 -> 13867 bytes .../__pycache__/_inputstream.cpython-310.pyc | Bin 0 -> 21686 bytes .../__pycache__/_tokenizer.cpython-310.pyc | Bin 0 -> 37378 bytes .../__pycache__/_utils.cpython-310.pyc | Bin 0 -> 4804 bytes .../__pycache__/constants.cpython-310.pyc | Bin 0 -> 161269 bytes .../__pycache__/html5parser.cpython-310.pyc | Bin 0 -> 88529 bytes .../__pycache__/serializer.cpython-310.pyc | Bin 0 -> 10751 bytes .../pip/_vendor/html5lib/_ihatexml.py | 289 + .../pip/_vendor/html5lib/_inputstream.py | 918 ++ .../pip/_vendor/html5lib/_tokenizer.py | 1735 ++++ .../pip/_vendor/html5lib/_trie/__init__.py | 5 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 360 bytes .../_trie/__pycache__/_base.cpython-310.pyc | Bin 0 -> 1612 bytes .../_trie/__pycache__/py.cpython-310.pyc | Bin 0 -> 2275 bytes .../pip/_vendor/html5lib/_trie/_base.py | 40 + .../pip/_vendor/html5lib/_trie/py.py | 67 + .../pip/_vendor/html5lib/_utils.py | 159 + .../pip/_vendor/html5lib/constants.py | 2946 ++++++ .../pip/_vendor/html5lib/filters/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 208 bytes .../alphabeticalattributes.cpython-310.pyc | Bin 0 -> 1338 bytes .../filters/__pycache__/base.cpython-310.pyc | Bin 0 -> 878 bytes .../inject_meta_charset.cpython-310.pyc | Bin 0 -> 1876 bytes .../filters/__pycache__/lint.cpython-310.pyc | Bin 0 -> 2584 bytes .../__pycache__/optionaltags.cpython-310.pyc | Bin 0 -> 2737 bytes .../__pycache__/sanitizer.cpython-310.pyc | Bin 0 -> 20042 bytes .../__pycache__/whitespace.cpython-310.pyc | Bin 0 -> 1384 bytes .../filters/alphabeticalattributes.py | 29 + .../pip/_vendor/html5lib/filters/base.py | 12 + .../html5lib/filters/inject_meta_charset.py | 73 + .../pip/_vendor/html5lib/filters/lint.py | 93 + .../_vendor/html5lib/filters/optionaltags.py | 207 + .../pip/_vendor/html5lib/filters/sanitizer.py | 916 ++ .../_vendor/html5lib/filters/whitespace.py | 38 + .../pip/_vendor/html5lib/html5parser.py | 2795 ++++++ .../pip/_vendor/html5lib/serializer.py | 409 + .../_vendor/html5lib/treeadapters/__init__.py | 30 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 949 bytes .../__pycache__/genshi.cpython-310.pyc | Bin 0 -> 1561 bytes .../__pycache__/sax.cpython-310.pyc | Bin 0 -> 1468 bytes .../_vendor/html5lib/treeadapters/genshi.py | 54 + .../pip/_vendor/html5lib/treeadapters/sax.py | 50 + .../_vendor/html5lib/treebuilders/__init__.py | 88 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 3340 bytes .../__pycache__/base.cpython-310.pyc | Bin 0 -> 11333 bytes .../__pycache__/dom.cpython-310.pyc | Bin 0 -> 9420 bytes .../__pycache__/etree.cpython-310.pyc | Bin 0 -> 11722 bytes .../__pycache__/etree_lxml.cpython-310.pyc | Bin 0 -> 13039 bytes .../pip/_vendor/html5lib/treebuilders/base.py | 417 + .../pip/_vendor/html5lib/treebuilders/dom.py | 239 + .../_vendor/html5lib/treebuilders/etree.py | 343 + .../html5lib/treebuilders/etree_lxml.py | 392 + .../_vendor/html5lib/treewalkers/__init__.py | 154 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 3990 bytes .../__pycache__/base.cpython-310.pyc | Bin 0 -> 6951 bytes .../__pycache__/dom.cpython-310.pyc | Bin 0 -> 1722 bytes .../__pycache__/etree.cpython-310.pyc | Bin 0 -> 3480 bytes .../__pycache__/etree_lxml.cpython-310.pyc | Bin 0 -> 6567 bytes .../__pycache__/genshi.cpython-310.pyc | Bin 0 -> 1928 bytes .../pip/_vendor/html5lib/treewalkers/base.py | 252 + .../pip/_vendor/html5lib/treewalkers/dom.py | 43 + .../pip/_vendor/html5lib/treewalkers/etree.py | 131 + .../html5lib/treewalkers/etree_lxml.py | 215 + .../_vendor/html5lib/treewalkers/genshi.py | 69 + .../pip/_vendor/idna/__init__.py | 44 + .../idna/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 855 bytes .../idna/__pycache__/codec.cpython-310.pyc | Bin 0 -> 2617 bytes .../idna/__pycache__/compat.cpython-310.pyc | Bin 0 -> 676 bytes .../idna/__pycache__/core.cpython-310.pyc | Bin 0 -> 9061 bytes .../idna/__pycache__/idnadata.cpython-310.pyc | Bin 0 -> 35684 bytes .../__pycache__/intranges.cpython-310.pyc | Bin 0 -> 1866 bytes .../__pycache__/package_data.cpython-310.pyc | Bin 0 -> 219 bytes .../__pycache__/uts46data.cpython-310.pyc | Bin 0 -> 145393 bytes .../site-packages/pip/_vendor/idna/codec.py | 117 + .../site-packages/pip/_vendor/idna/compat.py | 16 + .../site-packages/pip/_vendor/idna/core.py | 409 + .../pip/_vendor/idna/idnadata.py | 2050 ++++ .../pip/_vendor/idna/intranges.py | 58 + .../pip/_vendor/idna/package_data.py | 2 + .../pip/_vendor/idna/uts46data.py | 8438 +++++++++++++++++ .../pip/_vendor/msgpack/__init__.py | 54 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1435 bytes .../__pycache__/_version.cpython-310.pyc | Bin 0 -> 226 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 1816 bytes .../msgpack/__pycache__/ext.cpython-310.pyc | Bin 0 -> 6324 bytes .../__pycache__/fallback.cpython-310.pyc | Bin 0 -> 26450 bytes .../pip/_vendor/msgpack/_version.py | 1 + .../pip/_vendor/msgpack/exceptions.py | 48 + .../site-packages/pip/_vendor/msgpack/ext.py | 193 + .../pip/_vendor/msgpack/fallback.py | 1087 +++ .../pip/_vendor/packaging/__about__.py | 26 + .../pip/_vendor/packaging/__init__.py | 25 + .../__pycache__/__about__.cpython-310.pyc | Bin 0 -> 598 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 454 bytes .../__pycache__/_manylinux.cpython-310.pyc | Bin 0 -> 7308 bytes .../__pycache__/_musllinux.cpython-310.pyc | Bin 0 -> 4620 bytes .../__pycache__/_structures.cpython-310.pyc | Bin 0 -> 2978 bytes .../__pycache__/markers.cpython-310.pyc | Bin 0 -> 9297 bytes .../__pycache__/requirements.cpython-310.pyc | Bin 0 -> 3983 bytes .../__pycache__/specifiers.cpython-310.pyc | Bin 0 -> 22196 bytes .../__pycache__/tags.cpython-310.pyc | Bin 0 -> 12239 bytes .../__pycache__/utils.cpython-310.pyc | Bin 0 -> 3583 bytes .../__pycache__/version.cpython-310.pyc | Bin 0 -> 12933 bytes .../pip/_vendor/packaging/_manylinux.py | 301 + .../pip/_vendor/packaging/_musllinux.py | 136 + .../pip/_vendor/packaging/_structures.py | 67 + .../pip/_vendor/packaging/markers.py | 304 + .../pip/_vendor/packaging/requirements.py | 146 + .../pip/_vendor/packaging/specifiers.py | 828 ++ .../pip/_vendor/packaging/tags.py | 484 + .../pip/_vendor/packaging/utils.py | 136 + .../pip/_vendor/packaging/version.py | 504 + .../pip/_vendor/pep517/__init__.py | 6 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 321 bytes .../pep517/__pycache__/build.cpython-310.pyc | Bin 0 -> 3621 bytes .../pep517/__pycache__/check.cpython-310.pyc | Bin 0 -> 4585 bytes .../__pycache__/colorlog.cpython-310.pyc | Bin 0 -> 2972 bytes .../pep517/__pycache__/compat.cpython-310.pyc | Bin 0 -> 1317 bytes .../__pycache__/dirtools.cpython-310.pyc | Bin 0 -> 1363 bytes .../__pycache__/envbuild.cpython-310.pyc | Bin 0 -> 4403 bytes .../pep517/__pycache__/meta.cpython-310.pyc | Bin 0 -> 2968 bytes .../__pycache__/wrappers.cpython-310.pyc | Bin 0 -> 12091 bytes .../site-packages/pip/_vendor/pep517/build.py | 127 + .../site-packages/pip/_vendor/pep517/check.py | 207 + .../pip/_vendor/pep517/colorlog.py | 115 + .../pip/_vendor/pep517/compat.py | 42 + .../pip/_vendor/pep517/dirtools.py | 44 + .../pip/_vendor/pep517/envbuild.py | 171 + .../pip/_vendor/pep517/in_process/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 929 bytes .../__pycache__/_in_process.cpython-310.pyc | Bin 0 -> 9738 bytes .../_vendor/pep517/in_process/_in_process.py | 349 + .../site-packages/pip/_vendor/pep517/meta.py | 92 + .../pip/_vendor/pep517/wrappers.py | 371 + .../pip/_vendor/pkg_resources/__init__.py | 3296 +++++++ .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 99893 bytes .../__pycache__/py31compat.cpython-310.pyc | Bin 0 -> 668 bytes .../pip/_vendor/pkg_resources/py31compat.py | 23 + .../pip/_vendor/progress/__init__.py | 177 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 5725 bytes .../progress/__pycache__/bar.cpython-310.pyc | Bin 0 -> 2615 bytes .../__pycache__/counter.cpython-310.pyc | Bin 0 -> 1443 bytes .../__pycache__/spinner.cpython-310.pyc | Bin 0 -> 1336 bytes .../site-packages/pip/_vendor/progress/bar.py | 91 + .../pip/_vendor/progress/counter.py | 41 + .../pip/_vendor/progress/spinner.py | 43 + .../site-packages/pip/_vendor/pyparsing.py | 7107 ++++++++++++++ .../pip/_vendor/requests/__init__.py | 154 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 4020 bytes .../__pycache__/__version__.cpython-310.pyc | Bin 0 -> 563 bytes .../_internal_utils.cpython-310.pyc | Bin 0 -> 1315 bytes .../__pycache__/adapters.cpython-310.pyc | Bin 0 -> 16914 bytes .../requests/__pycache__/api.cpython-310.pyc | Bin 0 -> 6662 bytes .../requests/__pycache__/auth.cpython-310.pyc | Bin 0 -> 8105 bytes .../__pycache__/certs.cpython-310.pyc | Bin 0 -> 647 bytes .../__pycache__/compat.cpython-310.pyc | Bin 0 -> 1626 bytes .../__pycache__/cookies.cpython-310.pyc | Bin 0 -> 18707 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 5021 bytes .../requests/__pycache__/help.cpython-310.pyc | Bin 0 -> 2915 bytes .../__pycache__/hooks.cpython-310.pyc | Bin 0 -> 1002 bytes .../__pycache__/models.cpython-310.pyc | Bin 0 -> 24434 bytes .../__pycache__/packages.cpython-310.pyc | Bin 0 -> 516 bytes .../__pycache__/sessions.cpython-310.pyc | Bin 0 -> 19788 bytes .../__pycache__/status_codes.cpython-310.pyc | Bin 0 -> 4679 bytes .../__pycache__/structures.cpython-310.pyc | Bin 0 -> 4461 bytes .../__pycache__/utils.cpython-310.pyc | Bin 0 -> 23441 bytes .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 42 + .../pip/_vendor/requests/adapters.py | 533 ++ .../site-packages/pip/_vendor/requests/api.py | 159 + .../pip/_vendor/requests/auth.py | 305 + .../pip/_vendor/requests/certs.py | 18 + .../pip/_vendor/requests/compat.py | 76 + .../pip/_vendor/requests/cookies.py | 549 ++ .../pip/_vendor/requests/exceptions.py | 127 + .../pip/_vendor/requests/help.py | 132 + .../pip/_vendor/requests/hooks.py | 34 + .../pip/_vendor/requests/models.py | 966 ++ .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 781 ++ .../pip/_vendor/requests/status_codes.py | 123 + .../pip/_vendor/requests/structures.py | 105 + .../pip/_vendor/requests/utils.py | 1013 ++ .../pip/_vendor/resolvelib/__init__.py | 26 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 614 bytes .../__pycache__/providers.cpython-310.pyc | Bin 0 -> 6486 bytes .../__pycache__/reporters.cpython-310.pyc | Bin 0 -> 2276 bytes .../__pycache__/resolvers.cpython-310.pyc | Bin 0 -> 14983 bytes .../__pycache__/structs.cpython-310.pyc | Bin 0 -> 7171 bytes .../pip/_vendor/resolvelib/compat/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 209 bytes .../collections_abc.cpython-310.pyc | Bin 0 -> 385 bytes .../resolvelib/compat/collections_abc.py | 6 + .../pip/_vendor/resolvelib/providers.py | 124 + .../pip/_vendor/resolvelib/reporters.py | 37 + .../pip/_vendor/resolvelib/resolvers.py | 473 + .../pip/_vendor/resolvelib/structs.py | 165 + .../site-packages/pip/_vendor/six.py | 998 ++ .../pip/_vendor/tenacity/__init__.py | 517 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 16382 bytes .../__pycache__/_asyncio.cpython-310.pyc | Bin 0 -> 2622 bytes .../__pycache__/_utils.cpython-310.pyc | Bin 0 -> 1235 bytes .../__pycache__/after.cpython-310.pyc | Bin 0 -> 1239 bytes .../__pycache__/before.cpython-310.pyc | Bin 0 -> 1117 bytes .../__pycache__/before_sleep.cpython-310.pyc | Bin 0 -> 1419 bytes .../tenacity/__pycache__/nap.cpython-310.pyc | Bin 0 -> 1207 bytes .../__pycache__/retry.cpython-310.pyc | Bin 0 -> 8437 bytes .../tenacity/__pycache__/stop.cpython-310.pyc | Bin 0 -> 4025 bytes .../__pycache__/tornadoweb.cpython-310.pyc | Bin 0 -> 1772 bytes .../tenacity/__pycache__/wait.cpython-310.pyc | Bin 0 -> 7969 bytes .../pip/_vendor/tenacity/_asyncio.py | 92 + .../pip/_vendor/tenacity/_utils.py | 68 + .../pip/_vendor/tenacity/after.py | 46 + .../pip/_vendor/tenacity/before.py | 41 + .../pip/_vendor/tenacity/before_sleep.py | 58 + .../site-packages/pip/_vendor/tenacity/nap.py | 43 + .../pip/_vendor/tenacity/retry.py | 213 + .../pip/_vendor/tenacity/stop.py | 96 + .../pip/_vendor/tenacity/tornadoweb.py | 59 + .../pip/_vendor/tenacity/wait.py | 191 + .../pip/_vendor/tomli/__init__.py | 6 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 387 bytes .../tomli/__pycache__/_parser.cpython-310.pyc | Bin 0 -> 16341 bytes .../tomli/__pycache__/_re.cpython-310.pyc | Bin 0 -> 2431 bytes .../pip/_vendor/tomli/_parser.py | 703 ++ .../site-packages/pip/_vendor/tomli/_re.py | 83 + .../pip/_vendor/urllib3/__init__.py | 85 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 2197 bytes .../__pycache__/_collections.cpython-310.pyc | Bin 0 -> 10871 bytes .../__pycache__/_version.cpython-310.pyc | Bin 0 -> 221 bytes .../__pycache__/connection.cpython-310.pyc | Bin 0 -> 13346 bytes .../connectionpool.cpython-310.pyc | Bin 0 -> 24488 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 11002 bytes .../__pycache__/fields.cpython-310.pyc | Bin 0 -> 8193 bytes .../__pycache__/filepost.cpython-310.pyc | Bin 0 -> 2758 bytes .../__pycache__/poolmanager.cpython-310.pyc | Bin 0 -> 15210 bytes .../__pycache__/request.cpython-310.pyc | Bin 0 -> 5634 bytes .../__pycache__/response.cpython-310.pyc | Bin 0 -> 20935 bytes .../pip/_vendor/urllib3/_collections.py | 337 + .../pip/_vendor/urllib3/_version.py | 2 + .../pip/_vendor/urllib3/connection.py | 539 ++ .../pip/_vendor/urllib3/connectionpool.py | 1067 +++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 207 bytes .../_appengine_environ.cpython-310.pyc | Bin 0 -> 1387 bytes .../__pycache__/appengine.cpython-310.pyc | Bin 0 -> 8203 bytes .../__pycache__/ntlmpool.cpython-310.pyc | Bin 0 -> 3642 bytes .../__pycache__/pyopenssl.cpython-310.pyc | Bin 0 -> 15537 bytes .../securetransport.cpython-310.pyc | Bin 0 -> 21934 bytes .../contrib/__pycache__/socks.cpython-310.pyc | Bin 0 -> 5609 bytes .../urllib3/contrib/_appengine_environ.py | 36 + .../contrib/_securetransport/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 224 bytes .../__pycache__/bindings.cpython-310.pyc | Bin 0 -> 10735 bytes .../__pycache__/low_level.cpython-310.pyc | Bin 0 -> 9111 bytes .../contrib/_securetransport/bindings.py | 519 + .../contrib/_securetransport/low_level.py | 396 + .../pip/_vendor/urllib3/contrib/appengine.py | 314 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 130 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 511 + .../urllib3/contrib/securetransport.py | 922 ++ .../pip/_vendor/urllib3/contrib/socks.py | 216 + .../pip/_vendor/urllib3/exceptions.py | 323 + .../pip/_vendor/urllib3/fields.py | 274 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 5 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 321 bytes .../packages/__pycache__/six.cpython-310.pyc | Bin 0 -> 27672 bytes .../urllib3/packages/backports/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 218 bytes .../__pycache__/makefile.cpython-310.pyc | Bin 0 -> 1318 bytes .../urllib3/packages/backports/makefile.py | 51 + .../pip/_vendor/urllib3/packages/six.py | 1077 +++ .../packages/ssl_match_hostname/__init__.py | 24 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 606 bytes .../_implementation.cpython-310.pyc | Bin 0 -> 3309 bytes .../ssl_match_hostname/_implementation.py | 160 + .../pip/_vendor/urllib3/poolmanager.py | 536 ++ .../pip/_vendor/urllib3/request.py | 170 + .../pip/_vendor/urllib3/response.py | 821 ++ .../pip/_vendor/urllib3/util/__init__.py | 49 + .../util/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1117 bytes .../__pycache__/connection.cpython-310.pyc | Bin 0 -> 3465 bytes .../util/__pycache__/proxy.cpython-310.pyc | Bin 0 -> 1352 bytes .../util/__pycache__/queue.cpython-310.pyc | Bin 0 -> 1072 bytes .../util/__pycache__/request.cpython-310.pyc | Bin 0 -> 3480 bytes .../util/__pycache__/response.cpython-310.pyc | Bin 0 -> 2365 bytes .../util/__pycache__/retry.cpython-310.pyc | Bin 0 -> 15790 bytes .../util/__pycache__/ssl_.cpython-310.pyc | Bin 0 -> 11319 bytes .../__pycache__/ssltransport.cpython-310.pyc | Bin 0 -> 7442 bytes .../util/__pycache__/timeout.cpython-310.pyc | Bin 0 -> 8949 bytes .../util/__pycache__/url.cpython-310.pyc | Bin 0 -> 10692 bytes .../util/__pycache__/wait.cpython-310.pyc | Bin 0 -> 3101 bytes .../pip/_vendor/urllib3/util/connection.py | 150 + .../pip/_vendor/urllib3/util/proxy.py | 56 + .../pip/_vendor/urllib3/util/queue.py | 22 + .../pip/_vendor/urllib3/util/request.py | 143 + .../pip/_vendor/urllib3/util/response.py | 107 + .../pip/_vendor/urllib3/util/retry.py | 602 ++ .../pip/_vendor/urllib3/util/ssl_.py | 495 + .../pip/_vendor/urllib3/util/ssltransport.py | 221 + .../pip/_vendor/urllib3/util/timeout.py | 268 + .../pip/_vendor/urllib3/util/url.py | 432 + .../pip/_vendor/urllib3/util/wait.py | 153 + .../site-packages/pip/_vendor/vendor.txt | 22 + .../pip/_vendor/webencodings/__init__.py | 342 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 9754 bytes .../__pycache__/labels.cpython-310.pyc | Bin 0 -> 5244 bytes .../__pycache__/mklabels.cpython-310.pyc | Bin 0 -> 1949 bytes .../__pycache__/tests.cpython-310.pyc | Bin 0 -> 5051 bytes .../x_user_defined.cpython-310.pyc | Bin 0 -> 2600 bytes .../pip/_vendor/webencodings/labels.py | 231 + .../pip/_vendor/webencodings/mklabels.py | 59 + .../pip/_vendor/webencodings/tests.py | 153 + .../_vendor/webencodings/x_user_defined.py | 325 + .../lib/python3.10/site-packages/pip/py.typed | 4 + .../site-packages/pkg_resources/__init__.py | 3288 +++++++ .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 100050 bytes .../pkg_resources/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 201 bytes .../__pycache__/appdirs.cpython-310.pyc | Bin 0 -> 20262 bytes .../__pycache__/pyparsing.cpython-310.pyc | Bin 0 -> 198835 bytes .../pkg_resources/_vendor/appdirs.py | 608 ++ .../_vendor/packaging/__about__.py | 27 + .../_vendor/packaging/__init__.py | 26 + .../__pycache__/__about__.cpython-310.pyc | Bin 0 -> 717 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 563 bytes .../__pycache__/_compat.cpython-310.pyc | Bin 0 -> 1163 bytes .../__pycache__/_structures.cpython-310.pyc | Bin 0 -> 2707 bytes .../__pycache__/_typing.cpython-310.pyc | Bin 0 -> 1519 bytes .../__pycache__/markers.cpython-310.pyc | Bin 0 -> 9204 bytes .../__pycache__/requirements.cpython-310.pyc | Bin 0 -> 4109 bytes .../__pycache__/specifiers.cpython-310.pyc | Bin 0 -> 20470 bytes .../__pycache__/tags.cpython-310.pyc | Bin 0 -> 17349 bytes .../__pycache__/utils.cpython-310.pyc | Bin 0 -> 1654 bytes .../__pycache__/version.cpython-310.pyc | Bin 0 -> 13034 bytes .../_vendor/packaging/_compat.py | 38 + .../_vendor/packaging/_structures.py | 86 + .../_vendor/packaging/_typing.py | 48 + .../_vendor/packaging/markers.py | 328 + .../_vendor/packaging/requirements.py | 145 + .../_vendor/packaging/specifiers.py | 863 ++ .../pkg_resources/_vendor/packaging/tags.py | 751 ++ .../pkg_resources/_vendor/packaging/utils.py | 65 + .../_vendor/packaging/version.py | 535 ++ .../pkg_resources/_vendor/pyparsing.py | 5742 +++++++++++ .../pkg_resources/extern/__init__.py | 73 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 2909 bytes .../__pycache__/setup.cpython-310.pyc | Bin 0 -> 329 bytes .../data/my-test-package-source/setup.py | 6 + .../setuptools-58.1.0.dist-info/INSTALLER | 1 + .../setuptools-58.1.0.dist-info/LICENSE | 19 + .../setuptools-58.1.0.dist-info/METADATA | 119 + .../setuptools-58.1.0.dist-info/RECORD | 296 + .../setuptools-58.1.0.dist-info/REQUESTED | 0 .../setuptools-58.1.0.dist-info/WHEEL | 5 + .../entry_points.txt | 56 + .../setuptools-58.1.0.dist-info/top_level.txt | 3 + .../site-packages/setuptools/__init__.py | 242 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 8605 bytes .../_deprecation_warning.cpython-310.pyc | Bin 0 -> 562 bytes .../__pycache__/_imp.cpython-310.pyc | Bin 0 -> 2088 bytes .../__pycache__/archive_util.cpython-310.pyc | Bin 0 -> 5858 bytes .../__pycache__/build_meta.cpython-310.pyc | Bin 0 -> 9130 bytes .../__pycache__/config.cpython-310.pyc | Bin 0 -> 20918 bytes .../__pycache__/dep_util.cpython-310.pyc | Bin 0 -> 869 bytes .../__pycache__/depends.cpython-310.pyc | Bin 0 -> 5282 bytes .../__pycache__/dist.cpython-310.pyc | Bin 0 -> 36265 bytes .../__pycache__/errors.cpython-310.pyc | Bin 0 -> 862 bytes .../__pycache__/extension.cpython-310.pyc | Bin 0 -> 1958 bytes .../__pycache__/glob.cpython-310.pyc | Bin 0 -> 3747 bytes .../__pycache__/installer.cpython-310.pyc | Bin 0 -> 2765 bytes .../__pycache__/launch.cpython-310.pyc | Bin 0 -> 919 bytes .../__pycache__/monkey.cpython-310.pyc | Bin 0 -> 4647 bytes .../__pycache__/msvc.cpython-310.pyc | Bin 0 -> 42651 bytes .../__pycache__/namespaces.cpython-310.pyc | Bin 0 -> 3630 bytes .../__pycache__/package_index.cpython-310.pyc | Bin 0 -> 32504 bytes .../__pycache__/py34compat.cpython-310.pyc | Bin 0 -> 494 bytes .../__pycache__/sandbox.cpython-310.pyc | Bin 0 -> 15770 bytes .../__pycache__/unicode_utils.cpython-310.pyc | Bin 0 -> 1124 bytes .../__pycache__/version.cpython-310.pyc | Bin 0 -> 336 bytes .../__pycache__/wheel.cpython-310.pyc | Bin 0 -> 7362 bytes .../windows_support.cpython-310.pyc | Bin 0 -> 1037 bytes .../setuptools/_deprecation_warning.py | 7 + .../setuptools/_distutils/__init__.py | 15 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 468 bytes .../__pycache__/_msvccompiler.cpython-310.pyc | Bin 0 -> 13847 bytes .../__pycache__/archive_util.cpython-310.pyc | Bin 0 -> 6575 bytes .../__pycache__/bcppcompiler.cpython-310.pyc | Bin 0 -> 6562 bytes .../__pycache__/ccompiler.cpython-310.pyc | Bin 0 -> 33289 bytes .../__pycache__/cmd.cpython-310.pyc | Bin 0 -> 13962 bytes .../__pycache__/config.cpython-310.pyc | Bin 0 -> 3603 bytes .../__pycache__/core.cpython-310.pyc | Bin 0 -> 6670 bytes .../cygwinccompiler.cpython-310.pyc | Bin 0 -> 8756 bytes .../__pycache__/debug.cpython-310.pyc | Bin 0 -> 264 bytes .../__pycache__/dep_util.cpython-310.pyc | Bin 0 -> 2785 bytes .../__pycache__/dir_util.cpython-310.pyc | Bin 0 -> 5896 bytes .../__pycache__/dist.cpython-310.pyc | Bin 0 -> 34065 bytes .../__pycache__/errors.cpython-310.pyc | Bin 0 -> 5006 bytes .../__pycache__/extension.cpython-310.pyc | Bin 0 -> 7020 bytes .../__pycache__/fancy_getopt.cpython-310.pyc | Bin 0 -> 10646 bytes .../__pycache__/file_util.cpython-310.pyc | Bin 0 -> 5990 bytes .../__pycache__/filelist.cpython-310.pyc | Bin 0 -> 10836 bytes .../__pycache__/log.cpython-310.pyc | Bin 0 -> 2321 bytes .../__pycache__/msvc9compiler.cpython-310.pyc | Bin 0 -> 17576 bytes .../__pycache__/msvccompiler.cpython-310.pyc | Bin 0 -> 14795 bytes .../__pycache__/py35compat.cpython-310.pyc | Bin 0 -> 640 bytes .../__pycache__/py38compat.cpython-310.pyc | Bin 0 -> 437 bytes .../__pycache__/spawn.cpython-310.pyc | Bin 0 -> 2907 bytes .../__pycache__/sysconfig.cpython-310.pyc | Bin 0 -> 12574 bytes .../__pycache__/text_file.cpython-310.pyc | Bin 0 -> 8483 bytes .../__pycache__/unixccompiler.cpython-310.pyc | Bin 0 -> 6841 bytes .../__pycache__/util.cpython-310.pyc | Bin 0 -> 14227 bytes .../__pycache__/version.cpython-310.pyc | Bin 0 -> 7371 bytes .../versionpredicate.cpython-310.pyc | Bin 0 -> 5201 bytes .../setuptools/_distutils/_msvccompiler.py | 561 ++ .../setuptools/_distutils/archive_util.py | 256 + .../setuptools/_distutils/bcppcompiler.py | 393 + .../setuptools/_distutils/ccompiler.py | 1123 +++ .../setuptools/_distutils/cmd.py | 403 + .../setuptools/_distutils/command/__init__.py | 31 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 543 bytes .../command/__pycache__/bdist.cpython-310.pyc | Bin 0 -> 3677 bytes .../__pycache__/bdist_dumb.cpython-310.pyc | Bin 0 -> 3658 bytes .../__pycache__/bdist_msi.cpython-310.pyc | Bin 0 -> 19734 bytes .../__pycache__/bdist_rpm.cpython-310.pyc | Bin 0 -> 12302 bytes .../__pycache__/bdist_wininst.cpython-310.pyc | Bin 0 -> 8643 bytes .../command/__pycache__/build.cpython-310.pyc | Bin 0 -> 3905 bytes .../__pycache__/build_clib.cpython-310.pyc | Bin 0 -> 4882 bytes .../__pycache__/build_ext.cpython-310.pyc | Bin 0 -> 16279 bytes .../__pycache__/build_py.cpython-310.pyc | Bin 0 -> 9901 bytes .../__pycache__/build_scripts.cpython-310.pyc | Bin 0 -> 4024 bytes .../command/__pycache__/check.cpython-310.pyc | Bin 0 -> 5021 bytes .../command/__pycache__/clean.cpython-310.pyc | Bin 0 -> 2160 bytes .../__pycache__/config.cpython-310.pyc | Bin 0 -> 10342 bytes .../__pycache__/install.cpython-310.pyc | Bin 0 -> 13876 bytes .../__pycache__/install_data.cpython-310.pyc | Bin 0 -> 2359 bytes .../install_egg_info.cpython-310.pyc | Bin 0 -> 3100 bytes .../install_headers.cpython-310.pyc | Bin 0 -> 1782 bytes .../__pycache__/install_lib.cpython-310.pyc | Bin 0 -> 5184 bytes .../install_scripts.cpython-310.pyc | Bin 0 -> 2211 bytes .../__pycache__/py37compat.cpython-310.pyc | Bin 0 -> 1056 bytes .../__pycache__/register.cpython-310.pyc | Bin 0 -> 8695 bytes .../command/__pycache__/sdist.cpython-310.pyc | Bin 0 -> 14511 bytes .../__pycache__/upload.cpython-310.pyc | Bin 0 -> 5387 bytes .../setuptools/_distutils/command/bdist.py | 143 + .../_distutils/command/bdist_dumb.py | 123 + .../_distutils/command/bdist_msi.py | 749 ++ .../_distutils/command/bdist_rpm.py | 579 ++ .../_distutils/command/bdist_wininst.py | 377 + .../setuptools/_distutils/command/build.py | 157 + .../_distutils/command/build_clib.py | 209 + .../_distutils/command/build_ext.py | 757 ++ .../setuptools/_distutils/command/build_py.py | 392 + .../_distutils/command/build_scripts.py | 152 + .../setuptools/_distutils/command/check.py | 148 + .../setuptools/_distutils/command/clean.py | 76 + .../setuptools/_distutils/command/config.py | 344 + .../setuptools/_distutils/command/install.py | 678 ++ .../_distutils/command/install_data.py | 79 + .../_distutils/command/install_egg_info.py | 77 + .../_distutils/command/install_headers.py | 47 + .../_distutils/command/install_lib.py | 217 + .../_distutils/command/install_scripts.py | 60 + .../_distutils/command/py37compat.py | 30 + .../setuptools/_distutils/command/register.py | 304 + .../setuptools/_distutils/command/sdist.py | 494 + .../setuptools/_distutils/command/upload.py | 214 + .../setuptools/_distutils/config.py | 130 + .../setuptools/_distutils/core.py | 234 + .../setuptools/_distutils/cygwinccompiler.py | 414 + .../setuptools/_distutils/debug.py | 5 + .../setuptools/_distutils/dep_util.py | 92 + .../setuptools/_distutils/dir_util.py | 210 + .../setuptools/_distutils/dist.py | 1257 +++ .../setuptools/_distutils/errors.py | 97 + .../setuptools/_distutils/extension.py | 240 + .../setuptools/_distutils/fancy_getopt.py | 457 + .../setuptools/_distutils/file_util.py | 238 + .../setuptools/_distutils/filelist.py | 355 + .../setuptools/_distutils/log.py | 77 + .../setuptools/_distutils/msvc9compiler.py | 788 ++ .../setuptools/_distutils/msvccompiler.py | 643 ++ .../setuptools/_distutils/py35compat.py | 19 + .../setuptools/_distutils/py38compat.py | 7 + .../setuptools/_distutils/spawn.py | 106 + .../setuptools/_distutils/sysconfig.py | 578 ++ .../setuptools/_distutils/text_file.py | 286 + .../setuptools/_distutils/unixccompiler.py | 332 + .../setuptools/_distutils/util.py | 535 ++ .../setuptools/_distutils/version.py | 347 + .../setuptools/_distutils/versionpredicate.py | 166 + .../site-packages/setuptools/_imp.py | 82 + .../setuptools/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 198 bytes .../__pycache__/ordered_set.cpython-310.pyc | Bin 0 -> 16334 bytes .../__pycache__/pyparsing.cpython-310.pyc | Bin 0 -> 198832 bytes .../_vendor/more_itertools/__init__.py | 4 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 279 bytes .../__pycache__/more.cpython-310.pyc | Bin 0 -> 110009 bytes .../__pycache__/recipes.cpython-310.pyc | Bin 0 -> 17979 bytes .../setuptools/_vendor/more_itertools/more.py | 3825 ++++++++ .../_vendor/more_itertools/recipes.py | 620 ++ .../setuptools/_vendor/ordered_set.py | 488 + .../setuptools/_vendor/packaging/__about__.py | 27 + .../setuptools/_vendor/packaging/__init__.py | 26 + .../__pycache__/__about__.cpython-310.pyc | Bin 0 -> 714 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 560 bytes .../__pycache__/_compat.cpython-310.pyc | Bin 0 -> 1160 bytes .../__pycache__/_structures.cpython-310.pyc | Bin 0 -> 2704 bytes .../__pycache__/_typing.cpython-310.pyc | Bin 0 -> 1516 bytes .../__pycache__/markers.cpython-310.pyc | Bin 0 -> 9198 bytes .../__pycache__/requirements.cpython-310.pyc | Bin 0 -> 4103 bytes .../__pycache__/specifiers.cpython-310.pyc | Bin 0 -> 20467 bytes .../__pycache__/tags.cpython-310.pyc | Bin 0 -> 17346 bytes .../__pycache__/utils.cpython-310.pyc | Bin 0 -> 1651 bytes .../__pycache__/version.cpython-310.pyc | Bin 0 -> 13031 bytes .../setuptools/_vendor/packaging/_compat.py | 38 + .../_vendor/packaging/_structures.py | 86 + .../setuptools/_vendor/packaging/_typing.py | 48 + .../setuptools/_vendor/packaging/markers.py | 328 + .../_vendor/packaging/requirements.py | 145 + .../_vendor/packaging/specifiers.py | 863 ++ .../setuptools/_vendor/packaging/tags.py | 751 ++ .../setuptools/_vendor/packaging/utils.py | 65 + .../setuptools/_vendor/packaging/version.py | 535 ++ .../setuptools/_vendor/pyparsing.py | 5742 +++++++++++ .../site-packages/setuptools/archive_util.py | 205 + .../site-packages/setuptools/build_meta.py | 281 + .../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 | 8 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 387 bytes .../command/__pycache__/alias.cpython-310.pyc | Bin 0 -> 2389 bytes .../__pycache__/bdist_egg.cpython-310.pyc | Bin 0 -> 13115 bytes .../__pycache__/bdist_rpm.cpython-310.pyc | Bin 0 -> 1602 bytes .../__pycache__/build_clib.cpython-310.pyc | Bin 0 -> 2478 bytes .../__pycache__/build_ext.cpython-310.pyc | Bin 0 -> 9905 bytes .../__pycache__/build_py.cpython-310.pyc | Bin 0 -> 7882 bytes .../__pycache__/develop.cpython-310.pyc | Bin 0 -> 6165 bytes .../__pycache__/dist_info.cpython-310.pyc | Bin 0 -> 1407 bytes .../__pycache__/easy_install.cpython-310.pyc | Bin 0 -> 63739 bytes .../__pycache__/egg_info.cpython-310.pyc | Bin 0 -> 22033 bytes .../__pycache__/install.cpython-310.pyc | Bin 0 -> 4064 bytes .../install_egg_info.cpython-310.pyc | Bin 0 -> 2441 bytes .../__pycache__/install_lib.cpython-310.pyc | Bin 0 -> 4184 bytes .../install_scripts.cpython-310.pyc | Bin 0 -> 2442 bytes .../__pycache__/py36compat.cpython-310.pyc | Bin 0 -> 4549 bytes .../__pycache__/register.cpython-310.pyc | Bin 0 -> 853 bytes .../__pycache__/rotate.cpython-310.pyc | Bin 0 -> 2520 bytes .../__pycache__/saveopts.cpython-310.pyc | Bin 0 -> 939 bytes .../command/__pycache__/sdist.cpython-310.pyc | Bin 0 -> 6540 bytes .../__pycache__/setopt.cpython-310.pyc | Bin 0 -> 4701 bytes .../command/__pycache__/test.cpython-310.pyc | Bin 0 -> 8145 bytes .../__pycache__/upload.cpython-310.pyc | Bin 0 -> 826 bytes .../__pycache__/upload_docs.cpython-310.pyc | Bin 0 -> 6195 bytes .../site-packages/setuptools/command/alias.py | 78 + .../setuptools/command/bdist_egg.py | 456 + .../setuptools/command/bdist_rpm.py | 40 + .../setuptools/command/build_clib.py | 101 + .../setuptools/command/build_ext.py | 328 + .../setuptools/command/build_py.py | 232 + .../setuptools/command/develop.py | 193 + .../setuptools/command/dist_info.py | 36 + .../setuptools/command/easy_install.py | 2290 +++++ .../setuptools/command/egg_info.py | 734 ++ .../setuptools/command/install.py | 125 + .../setuptools/command/install_egg_info.py | 62 + .../setuptools/command/install_lib.py | 122 + .../setuptools/command/install_scripts.py | 69 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/py36compat.py | 134 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 64 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 189 + .../setuptools/command/setopt.py | 149 + .../site-packages/setuptools/command/test.py | 252 + .../setuptools/command/upload.py | 17 + .../setuptools/command/upload_docs.py | 202 + .../site-packages/setuptools/config.py | 749 ++ .../site-packages/setuptools/dep_util.py | 25 + .../site-packages/setuptools/depends.py | 175 + .../site-packages/setuptools/dist.py | 1150 +++ .../site-packages/setuptools/errors.py | 16 + .../site-packages/setuptools/extension.py | 55 + .../setuptools/extern/__init__.py | 73 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 2948 bytes .../site-packages/setuptools/glob.py | 167 + .../site-packages/setuptools/gui-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/gui-64.exe | Bin 0 -> 75264 bytes .../site-packages/setuptools/gui.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/installer.py | 97 + .../site-packages/setuptools/launch.py | 36 + .../site-packages/setuptools/monkey.py | 177 + .../site-packages/setuptools/msvc.py | 1805 ++++ .../site-packages/setuptools/namespaces.py | 107 + .../site-packages/setuptools/package_index.py | 1119 +++ .../site-packages/setuptools/py34compat.py | 13 + .../site-packages/setuptools/sandbox.py | 530 ++ .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/unicode_utils.py | 42 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/wheel.py | 213 + .../setuptools/windows_support.py | 29 + .../site-packages/werkzeug/__init__.py | 6 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 362 bytes .../__pycache__/_internal.cpython-310.pyc | Bin 0 -> 18616 bytes .../__pycache__/_reloader.cpython-310.pyc | Bin 0 -> 12088 bytes .../datastructures.cpython-310.pyc | Bin 0 -> 104996 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 29844 bytes .../__pycache__/filesystem.cpython-310.pyc | Bin 0 -> 2090 bytes .../__pycache__/formparser.cpython-310.pyc | Bin 0 -> 14306 bytes .../werkzeug/__pycache__/http.cpython-310.pyc | Bin 0 -> 38667 bytes .../__pycache__/local.cpython-310.pyc | Bin 0 -> 22818 bytes .../__pycache__/routing.cpython-310.pyc | Bin 0 -> 73474 bytes .../__pycache__/security.cpython-310.pyc | Bin 0 -> 8229 bytes .../__pycache__/serving.cpython-310.pyc | Bin 0 -> 31162 bytes .../werkzeug/__pycache__/test.cpython-310.pyc | Bin 0 -> 39245 bytes .../__pycache__/testapp.cpython-310.pyc | Bin 0 -> 9636 bytes .../werkzeug/__pycache__/urls.cpython-310.pyc | Bin 0 -> 36543 bytes .../__pycache__/user_agent.cpython-310.pyc | Bin 0 -> 1848 bytes .../__pycache__/useragents.cpython-310.pyc | Bin 0 -> 6885 bytes .../__pycache__/utils.cpython-310.pyc | Bin 0 -> 33005 bytes .../werkzeug/__pycache__/wsgi.cpython-310.pyc | Bin 0 -> 30408 bytes .../site-packages/werkzeug/_internal.py | 626 ++ .../site-packages/werkzeug/_reloader.py | 430 + .../site-packages/werkzeug/datastructures.py | 3059 ++++++ .../site-packages/werkzeug/datastructures.pyi | 912 ++ .../site-packages/werkzeug/debug/__init__.py | 500 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 13127 bytes .../debug/__pycache__/console.cpython-310.pyc | Bin 0 -> 7926 bytes .../debug/__pycache__/repr.cpython-310.pyc | Bin 0 -> 8891 bytes .../debug/__pycache__/tbtools.cpython-310.pyc | Bin 0 -> 18179 bytes .../site-packages/werkzeug/debug/console.py | 214 + .../site-packages/werkzeug/debug/repr.py | 284 + .../werkzeug/debug/shared/FONT_LICENSE | 96 + .../werkzeug/debug/shared/ICON_LICENSE.md | 6 + .../werkzeug/debug/shared/console.png | Bin 0 -> 507 bytes .../werkzeug/debug/shared/debugger.js | 359 + .../werkzeug/debug/shared/less.png | Bin 0 -> 191 bytes .../werkzeug/debug/shared/more.png | Bin 0 -> 200 bytes .../werkzeug/debug/shared/source.png | Bin 0 -> 818 bytes .../werkzeug/debug/shared/style.css | 163 + .../werkzeug/debug/shared/ubuntu.ttf | Bin 0 -> 70220 bytes .../site-packages/werkzeug/debug/tbtools.py | 600 ++ .../site-packages/werkzeug/exceptions.py | 944 ++ .../site-packages/werkzeug/filesystem.py | 55 + .../site-packages/werkzeug/formparser.py | 495 + .../python3.10/site-packages/werkzeug/http.py | 1393 +++ .../site-packages/werkzeug/local.py | 690 ++ .../werkzeug/middleware/__init__.py | 22 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 707 bytes .../__pycache__/dispatcher.cpython-310.pyc | Bin 0 -> 2791 bytes .../__pycache__/http_proxy.cpython-310.pyc | Bin 0 -> 6881 bytes .../__pycache__/lint.cpython-310.pyc | Bin 0 -> 12748 bytes .../__pycache__/profiler.cpython-310.pyc | Bin 0 -> 4998 bytes .../__pycache__/proxy_fix.cpython-310.pyc | Bin 0 -> 6209 bytes .../__pycache__/shared_data.cpython-310.pyc | Bin 0 -> 9925 bytes .../werkzeug/middleware/dispatcher.py | 78 + .../werkzeug/middleware/http_proxy.py | 230 + .../site-packages/werkzeug/middleware/lint.py | 420 + .../werkzeug/middleware/profiler.py | 139 + .../werkzeug/middleware/proxy_fix.py | 187 + .../werkzeug/middleware/shared_data.py | 320 + .../site-packages/werkzeug/py.typed | 0 .../site-packages/werkzeug/routing.py | 2342 +++++ .../site-packages/werkzeug/sansio/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 195 bytes .../__pycache__/multipart.cpython-310.pyc | Bin 0 -> 6469 bytes .../__pycache__/request.cpython-310.pyc | Bin 0 -> 17119 bytes .../__pycache__/response.cpython-310.pyc | Bin 0 -> 22399 bytes .../sansio/__pycache__/utils.cpython-310.pyc | Bin 0 -> 3934 bytes .../werkzeug/sansio/multipart.py | 260 + .../site-packages/werkzeug/sansio/request.py | 548 ++ .../site-packages/werkzeug/sansio/response.py | 704 ++ .../site-packages/werkzeug/sansio/utils.py | 142 + .../site-packages/werkzeug/security.py | 247 + .../site-packages/werkzeug/serving.py | 1088 +++ .../python3.10/site-packages/werkzeug/test.py | 1331 +++ .../site-packages/werkzeug/testapp.py | 240 + .../python3.10/site-packages/werkzeug/urls.py | 1211 +++ .../site-packages/werkzeug/user_agent.py | 47 + .../site-packages/werkzeug/useragents.py | 215 + .../site-packages/werkzeug/utils.py | 1099 +++ .../werkzeug/wrappers/__init__.py | 16 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 918 bytes .../__pycache__/accept.cpython-310.pyc | Bin 0 -> 828 bytes .../wrappers/__pycache__/auth.cpython-310.pyc | Bin 0 -> 1222 bytes .../__pycache__/base_request.cpython-310.pyc | Bin 0 -> 1740 bytes .../__pycache__/base_response.cpython-310.pyc | Bin 0 -> 1751 bytes .../common_descriptors.cpython-310.pyc | Bin 0 -> 1299 bytes .../wrappers/__pycache__/cors.cpython-310.pyc | Bin 0 -> 1207 bytes .../wrappers/__pycache__/etag.cpython-310.pyc | Bin 0 -> 1207 bytes .../wrappers/__pycache__/json.cpython-310.pyc | Bin 0 -> 820 bytes .../__pycache__/request.cpython-310.pyc | Bin 0 -> 21400 bytes .../__pycache__/response.cpython-310.pyc | Bin 0 -> 29815 bytes .../__pycache__/user_agent.cpython-310.pyc | Bin 0 -> 841 bytes .../site-packages/werkzeug/wrappers/accept.py | 14 + .../site-packages/werkzeug/wrappers/auth.py | 26 + .../werkzeug/wrappers/base_request.py | 36 + .../werkzeug/wrappers/base_response.py | 36 + .../werkzeug/wrappers/common_descriptors.py | 26 + .../site-packages/werkzeug/wrappers/cors.py | 26 + .../site-packages/werkzeug/wrappers/etag.py | 26 + .../site-packages/werkzeug/wrappers/json.py | 13 + .../werkzeug/wrappers/request.py | 660 ++ .../werkzeug/wrappers/response.py | 891 ++ .../werkzeug/wrappers/user_agent.py | 14 + .../python3.10/site-packages/werkzeug/wsgi.py | 982 ++ myvenv/lib64 | 1 + myvenv/pyvenv.cfg | 3 + requirements.txt | 1 + static/styles/main.css | 44 + templates/homepage.html | 24 + 1404 files changed, 270042 insertions(+) create mode 100644 __pycache__/app.cpython-310.pyc create mode 100755 app.py create mode 100644 myvenv/bin/Activate.ps1 create mode 100644 myvenv/bin/activate create mode 100644 myvenv/bin/activate.csh create mode 100644 myvenv/bin/activate.fish create mode 100755 myvenv/bin/flask create mode 100755 myvenv/bin/pip create mode 100755 myvenv/bin/pip3 create mode 100755 myvenv/bin/pip3.10 create mode 120000 myvenv/bin/python create mode 120000 myvenv/bin/python3 create mode 120000 myvenv/bin/python3.10 create mode 100644 myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/INSTALLER create mode 100644 myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/LICENSE.rst create mode 100644 myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/METADATA create mode 100644 myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/RECORD create mode 100644 myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/REQUESTED create mode 100644 myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/WHEEL create mode 100644 myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/entry_points.txt create mode 100644 myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/top_level.txt create mode 100644 myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/INSTALLER create mode 100644 myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/LICENSE.rst create mode 100644 myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/METADATA create mode 100644 myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/RECORD create mode 100644 myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/WHEEL create mode 100644 myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/entry_points.txt create mode 100644 myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/top_level.txt create mode 100644 myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/INSTALLER create mode 100644 myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/LICENSE.rst create mode 100644 myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/METADATA create mode 100644 myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/RECORD create mode 100644 myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/WHEEL create mode 100644 myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/top_level.txt create mode 100644 myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/INSTALLER create mode 100644 myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/LICENSE.rst create mode 100644 myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/METADATA create mode 100644 myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/RECORD create mode 100644 myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/WHEEL create mode 100644 myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/top_level.txt create mode 100644 myvenv/lib/python3.10/site-packages/_distutils_hack/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/_distutils_hack/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/_distutils_hack/__pycache__/override.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/_distutils_hack/override.py create mode 100644 myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/INSTALLER create mode 100644 myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/LICENSE.rst create mode 100644 myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/METADATA create mode 100644 myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/RECORD create mode 100644 myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/WHEEL create mode 100644 myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/top_level.txt create mode 100644 myvenv/lib/python3.10/site-packages/click/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/_compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/_textwrap.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/_unicodefun.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/_winconsole.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/core.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/decorators.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/exceptions.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/formatting.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/globals.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/parser.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/shell_completion.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/termui.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/testing.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/types.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/__pycache__/utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/click/_compat.py create mode 100644 myvenv/lib/python3.10/site-packages/click/_termui_impl.py create mode 100644 myvenv/lib/python3.10/site-packages/click/_textwrap.py create mode 100644 myvenv/lib/python3.10/site-packages/click/_unicodefun.py create mode 100644 myvenv/lib/python3.10/site-packages/click/_winconsole.py create mode 100644 myvenv/lib/python3.10/site-packages/click/core.py create mode 100644 myvenv/lib/python3.10/site-packages/click/decorators.py create mode 100644 myvenv/lib/python3.10/site-packages/click/exceptions.py create mode 100644 myvenv/lib/python3.10/site-packages/click/formatting.py create mode 100644 myvenv/lib/python3.10/site-packages/click/globals.py create mode 100644 myvenv/lib/python3.10/site-packages/click/parser.py create mode 100644 myvenv/lib/python3.10/site-packages/click/py.typed create mode 100644 myvenv/lib/python3.10/site-packages/click/shell_completion.py create mode 100644 myvenv/lib/python3.10/site-packages/click/termui.py create mode 100644 myvenv/lib/python3.10/site-packages/click/testing.py create mode 100644 myvenv/lib/python3.10/site-packages/click/types.py create mode 100644 myvenv/lib/python3.10/site-packages/click/utils.py create mode 100644 myvenv/lib/python3.10/site-packages/distutils-precedence.pth create mode 100644 myvenv/lib/python3.10/site-packages/flask/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/__main__.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/__main__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/app.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/blueprints.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/cli.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/config.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/ctx.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/debughelpers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/globals.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/helpers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/logging.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/scaffold.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/sessions.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/signals.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/templating.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/testing.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/typing.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/views.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/__pycache__/wrappers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/app.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/blueprints.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/cli.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/config.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/ctx.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/debughelpers.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/globals.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/helpers.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/json/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/json/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/json/__pycache__/tag.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/flask/json/tag.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/logging.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/py.typed create mode 100644 myvenv/lib/python3.10/site-packages/flask/scaffold.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/sessions.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/signals.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/templating.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/testing.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/typing.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/views.py create mode 100644 myvenv/lib/python3.10/site-packages/flask/wrappers.py create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous-2.1.1.dist-info/INSTALLER create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous-2.1.1.dist-info/LICENSE.rst create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous-2.1.1.dist-info/METADATA create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous-2.1.1.dist-info/RECORD create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous-2.1.1.dist-info/WHEEL create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous-2.1.1.dist-info/top_level.txt create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/__pycache__/_json.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/__pycache__/encoding.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/__pycache__/exc.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/__pycache__/serializer.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/__pycache__/signer.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/__pycache__/timed.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/__pycache__/url_safe.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/_json.py create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/encoding.py create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/exc.py create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/py.typed create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/serializer.py create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/signer.py create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/timed.py create mode 100644 myvenv/lib/python3.10/site-packages/itsdangerous/url_safe.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/_identifier.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/async_utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/bccache.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/compiler.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/constants.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/debug.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/defaults.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/environment.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/exceptions.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/ext.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/filters.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/idtracking.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/lexer.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/loaders.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/meta.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/nativetypes.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/nodes.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/optimizer.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/parser.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/runtime.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/sandbox.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/tests.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/__pycache__/visitor.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/_identifier.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/async_utils.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/bccache.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/compiler.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/constants.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/debug.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/defaults.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/environment.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/exceptions.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/ext.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/filters.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/idtracking.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/lexer.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/loaders.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/meta.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/nativetypes.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/nodes.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/optimizer.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/parser.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/py.typed create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/runtime.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/sandbox.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/tests.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/utils.py create mode 100644 myvenv/lib/python3.10/site-packages/jinja2/visitor.py create mode 100644 myvenv/lib/python3.10/site-packages/markupsafe/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/markupsafe/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/markupsafe/__pycache__/_native.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/markupsafe/_native.py create mode 100644 myvenv/lib/python3.10/site-packages/markupsafe/_speedups.c create mode 100755 myvenv/lib/python3.10/site-packages/markupsafe/_speedups.cpython-310-x86_64-linux-gnu.so create mode 100644 myvenv/lib/python3.10/site-packages/markupsafe/_speedups.pyi create mode 100644 myvenv/lib/python3.10/site-packages/markupsafe/py.typed create mode 100644 myvenv/lib/python3.10/site-packages/pip-21.2.4.dist-info/INSTALLER create mode 100644 myvenv/lib/python3.10/site-packages/pip-21.2.4.dist-info/LICENSE.txt create mode 100644 myvenv/lib/python3.10/site-packages/pip-21.2.4.dist-info/METADATA create mode 100644 myvenv/lib/python3.10/site-packages/pip-21.2.4.dist-info/RECORD create mode 100644 myvenv/lib/python3.10/site-packages/pip-21.2.4.dist-info/REQUESTED create mode 100644 myvenv/lib/python3.10/site-packages/pip-21.2.4.dist-info/WHEEL create mode 100644 myvenv/lib/python3.10/site-packages/pip-21.2.4.dist-info/entry_points.txt create mode 100644 myvenv/lib/python3.10/site-packages/pip-21.2.4.dist-info/top_level.txt create mode 100644 myvenv/lib/python3.10/site-packages/pip/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/__main__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/__pycache__/__main__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/__pycache__/build_env.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/__pycache__/cache.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/__pycache__/configuration.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/__pycache__/exceptions.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/__pycache__/main.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/__pycache__/pyproject.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/build_env.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cache.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__pycache__/main.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__pycache__/parser.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/base_command.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/command_context.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/main.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/main_parser.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/parser.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/req_command.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/spinners.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/cli/status_codes.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/cache.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/check.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/completion.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/debug.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/download.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/hash.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/help.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/index.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/install.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/list.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/search.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/show.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/cache.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/check.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/completion.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/configuration.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/debug.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/download.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/freeze.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/hash.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/help.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/index.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/install.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/list.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/search.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/show.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/uninstall.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/commands/wheel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/configuration.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/distributions/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/distributions/__pycache__/base.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/distributions/base.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/distributions/installed.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/distributions/sdist.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/distributions/wheel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/exceptions.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/index/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/index/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/index/__pycache__/collector.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/index/__pycache__/sources.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/index/collector.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/index/package_finder.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/index/sources.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/locations/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/locations/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/locations/__pycache__/_distutils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/locations/__pycache__/base.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/locations/_distutils.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/locations/_sysconfig.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/locations/base.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/main.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/metadata/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/metadata/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/metadata/__pycache__/base.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/metadata/base.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/metadata/pkg_resources.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/__pycache__/candidate.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/__pycache__/format_control.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/__pycache__/index.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/__pycache__/link.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/__pycache__/scheme.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/__pycache__/target_python.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/__pycache__/wheel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/candidate.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/direct_url.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/format_control.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/index.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/link.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/scheme.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/search_scope.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/target_python.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/models/wheel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/__pycache__/auth.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/__pycache__/cache.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/__pycache__/download.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/__pycache__/session.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/__pycache__/utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/auth.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/cache.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/download.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/lazy_wheel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/session.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/utils.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/__pycache__/check.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/build/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata_legacy.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel_legacy.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/check.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/freeze.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/install/legacy.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/operations/prepare.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/pyproject.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/__pycache__/constructors.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/__pycache__/req_file.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/__pycache__/req_install.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/__pycache__/req_set.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/__pycache__/req_tracker.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/constructors.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/req_file.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/req_install.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/req_set.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/req_tracker.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/__pycache__/base.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/base.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/reporter.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/self_outdated_check.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/_log.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/logging.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/misc.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/models.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/parallel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/pkg_resources.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/urls.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/_log.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/appdirs.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/compat.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/datetime.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/deprecation.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/distutils_args.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/encoding.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/filesystem.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/filetypes.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/glibc.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/hashes.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/inject_securetransport.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/logging.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/misc.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/models.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/packaging.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/parallel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/pkg_resources.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/subprocess.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/unpacking.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/urls.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/utils/wheel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/vcs/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/vcs/__pycache__/git.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/vcs/git.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/vcs/subversion.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_internal/wheel_builder.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/__pycache__/appdirs.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/__pycache__/distro.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/__pycache__/pyparsing.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/__pycache__/six.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/appdirs.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__pycache__/compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/compat.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/certifi/core.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/langrussianmodel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/sjisprober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/compat.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/enums.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/langrussianmodel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/metadata/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/metadata/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/metadata/__pycache__/languages.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/metadata/languages.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/chardet/version.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/colorama/__pycache__/ansi.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/colorama/win32.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/__pycache__/misc.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/__pycache__/tarfile.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/misc.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/shutil.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/sysconfig.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/_backport/tarfile.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/compat.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/database.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/index.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/locators.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/markers.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/resources.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/t64.exe create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/util.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/version.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/w64.exe create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/distro.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/__pycache__/_inputstream.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/__pycache__/_tokenizer.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/__pycache__/_utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/__pycache__/constants.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/__pycache__/html5parser.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/__pycache__/serializer.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_ihatexml.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_inputstream.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_tokenizer.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/__pycache__/py.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/_base.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/py.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_utils.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/constants.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/__pycache__/base.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/__pycache__/inject_meta_charset.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/__pycache__/lint.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/__pycache__/whitespace.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/base.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/lint.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/optionaltags.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/sanitizer.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/whitespace.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/html5parser.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/serializer.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/genshi.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/sax.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treeadapters/sax.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/dom.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/etree_lxml.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/base.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/dom.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/etree.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/base.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/dom.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/etree.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/etree_lxml.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/genshi.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/base.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/dom.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/etree.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/__pycache__/core.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/codec.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/compat.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/core.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/intranges.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/package_data.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/msgpack/__pycache__/_version.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/msgpack/_version.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/msgpack/ext.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__pycache__/__about__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__pycache__/_manylinux.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__pycache__/_musllinux.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/_manylinux.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/_musllinux.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/markers.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/tags.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/utils.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/packaging/version.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/__pycache__/build.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/__pycache__/check.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/__pycache__/colorlog.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/__pycache__/compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/__pycache__/dirtools.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/__pycache__/envbuild.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/__pycache__/meta.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/__pycache__/wrappers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/build.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/check.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/colorlog.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/compat.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/dirtools.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/envbuild.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/in_process/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/in_process/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/in_process/__pycache__/_in_process.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/in_process/_in_process.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/meta.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pep517/wrappers.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pkg_resources/py31compat.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/progress/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/progress/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/progress/__pycache__/bar.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/progress/__pycache__/counter.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/progress/__pycache__/spinner.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/progress/bar.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/progress/counter.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/progress/spinner.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/pyparsing.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/api.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/help.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/models.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/__version__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/adapters.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/api.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/auth.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/certs.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/compat.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/cookies.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/help.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/hooks.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/models.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/packages.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/sessions.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/structures.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/requests/utils.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/__pycache__/resolvers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/compat/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/providers.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/reporters.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/resolvers.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/structs.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/six.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__pycache__/_asyncio.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__pycache__/_utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__pycache__/after.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__pycache__/before.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__pycache__/before_sleep.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__pycache__/nap.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__pycache__/retry.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__pycache__/stop.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__pycache__/tornadoweb.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__pycache__/wait.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/_asyncio.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/_utils.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/after.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/before.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/before_sleep.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/nap.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/retry.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/stop.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/tornadoweb.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tenacity/wait.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tomli/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tomli/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tomli/__pycache__/_parser.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tomli/__pycache__/_re.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tomli/_parser.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/tomli/_re.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__pycache__/_version.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/_version.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/request.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/response.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__pycache__/connection.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__pycache__/proxy.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__pycache__/queue.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__pycache__/wait.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/proxy.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/ssltransport.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/vendor.txt create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/webencodings/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/webencodings/__pycache__/labels.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/webencodings/__pycache__/tests.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 myvenv/lib/python3.10/site-packages/pip/py.typed create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/__pycache__/appdirs.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/__pycache__/pyparsing.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__pycache__/_typing.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__pycache__/tags.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/_compat.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/_typing.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/tags.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/_vendor/pyparsing.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/extern/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/tests/data/my-test-package-source/__pycache__/setup.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/pkg_resources/tests/data/my-test-package-source/setup.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools-58.1.0.dist-info/INSTALLER create mode 100644 myvenv/lib/python3.10/site-packages/setuptools-58.1.0.dist-info/LICENSE create mode 100644 myvenv/lib/python3.10/site-packages/setuptools-58.1.0.dist-info/METADATA create mode 100644 myvenv/lib/python3.10/site-packages/setuptools-58.1.0.dist-info/RECORD create mode 100644 myvenv/lib/python3.10/site-packages/setuptools-58.1.0.dist-info/REQUESTED create mode 100644 myvenv/lib/python3.10/site-packages/setuptools-58.1.0.dist-info/WHEEL create mode 100644 myvenv/lib/python3.10/site-packages/setuptools-58.1.0.dist-info/entry_points.txt create mode 100644 myvenv/lib/python3.10/site-packages/setuptools-58.1.0.dist-info/top_level.txt create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/_imp.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/archive_util.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/build_meta.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/config.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/dep_util.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/depends.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/dist.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/errors.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/extension.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/glob.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/installer.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/launch.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/monkey.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/msvc.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/namespaces.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/package_index.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/py34compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/sandbox.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/unicode_utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/version.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/wheel.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/__pycache__/windows_support.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_deprecation_warning.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/_msvccompiler.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/archive_util.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/bcppcompiler.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/ccompiler.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/cmd.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/config.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/core.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/cygwinccompiler.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/debug.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/dep_util.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/dir_util.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/dist.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/errors.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/extension.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/fancy_getopt.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/file_util.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/filelist.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/log.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/msvc9compiler.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/msvccompiler.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/py35compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/py38compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/spawn.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/sysconfig.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/text_file.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/unixccompiler.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/util.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/version.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/__pycache__/versionpredicate.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/_msvccompiler.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/archive_util.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/bcppcompiler.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/ccompiler.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/cmd.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/bdist.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/bdist_dumb.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/bdist_msi.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/bdist_rpm.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/bdist_wininst.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/build.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/build_clib.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/build_ext.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/build_py.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/build_scripts.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/check.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/clean.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/config.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/install.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/install_data.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/install_egg_info.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/install_headers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/install_lib.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/install_scripts.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/py37compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/register.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/sdist.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/__pycache__/upload.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/bdist.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/bdist_dumb.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/bdist_msi.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/bdist_rpm.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/bdist_wininst.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/build.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/build_clib.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/build_ext.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/build_py.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/build_scripts.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/check.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/clean.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/config.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/install.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/install_data.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/install_egg_info.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/install_headers.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/install_lib.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/install_scripts.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/py37compat.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/register.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/sdist.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/command/upload.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/config.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/core.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/cygwinccompiler.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/debug.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/dep_util.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/dir_util.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/dist.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/errors.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/extension.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/fancy_getopt.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/file_util.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/filelist.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/log.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/msvc9compiler.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/msvccompiler.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/py35compat.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/py38compat.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/spawn.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/sysconfig.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/text_file.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/unixccompiler.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/util.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/version.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_distutils/versionpredicate.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_imp.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/__pycache__/ordered_set.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/__pycache__/pyparsing.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/more_itertools/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/more_itertools/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/more_itertools/__pycache__/more.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/more_itertools/__pycache__/recipes.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/more_itertools/more.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/more_itertools/recipes.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/ordered_set.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__pycache__/_compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__pycache__/_structures.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__pycache__/_typing.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__pycache__/specifiers.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__pycache__/tags.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__pycache__/version.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/_compat.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/_typing.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/tags.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/_vendor/pyparsing.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/archive_util.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/build_meta.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/cli-32.exe create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/cli-64.exe create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/cli.exe create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/alias.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/build_clib.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/build_ext.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/build_py.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/develop.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/dist_info.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/easy_install.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/egg_info.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/install.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/install_lib.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/install_scripts.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/py36compat.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/register.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/rotate.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/saveopts.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/sdist.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/setopt.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/test.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/upload.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/__pycache__/upload_docs.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/alias.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/bdist_egg.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/bdist_rpm.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/build_clib.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/build_ext.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/build_py.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/develop.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/dist_info.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/easy_install.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/egg_info.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/install.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/install_egg_info.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/install_lib.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/install_scripts.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/launcher manifest.xml create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/py36compat.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/register.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/rotate.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/saveopts.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/sdist.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/setopt.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/test.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/upload.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/command/upload_docs.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/config.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/dep_util.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/depends.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/dist.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/errors.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/extension.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/extern/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/extern/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/glob.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/gui-32.exe create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/gui-64.exe create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/gui.exe create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/installer.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/launch.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/monkey.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/msvc.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/namespaces.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/package_index.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/py34compat.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/sandbox.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/script (dev).tmpl create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/script.tmpl create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/unicode_utils.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/version.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/wheel.py create mode 100644 myvenv/lib/python3.10/site-packages/setuptools/windows_support.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/_internal.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/_reloader.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/datastructures.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/exceptions.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/filesystem.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/formparser.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/http.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/local.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/routing.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/security.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/serving.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/test.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/testapp.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/urls.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/user_agent.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/useragents.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/__pycache__/wsgi.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/_internal.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/_reloader.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/datastructures.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/datastructures.pyi create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/__pycache__/console.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/__pycache__/repr.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/__pycache__/tbtools.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/console.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/repr.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/shared/FONT_LICENSE create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/shared/ICON_LICENSE.md create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/shared/console.png create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/shared/debugger.js create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/shared/less.png create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/shared/more.png create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/shared/source.png create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/shared/style.css create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/shared/ubuntu.ttf create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/debug/tbtools.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/exceptions.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/filesystem.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/formparser.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/http.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/local.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/lint.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/dispatcher.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/http_proxy.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/lint.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/profiler.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/proxy_fix.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/middleware/shared_data.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/py.typed create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/routing.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/sansio/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/request.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/response.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/utils.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/sansio/multipart.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/sansio/request.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/sansio/response.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/sansio/utils.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/security.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/serving.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/test.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/testapp.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/urls.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/user_agent.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/useragents.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/utils.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__init__.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/accept.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/auth.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/base_request.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/base_response.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/common_descriptors.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/cors.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/etag.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/json.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/request.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/response.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/user_agent.cpython-310.pyc create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/accept.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/auth.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/base_request.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/base_response.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/common_descriptors.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/cors.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/etag.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/json.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/request.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/response.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wrappers/user_agent.py create mode 100644 myvenv/lib/python3.10/site-packages/werkzeug/wsgi.py create mode 120000 myvenv/lib64 create mode 100644 myvenv/pyvenv.cfg create mode 100644 requirements.txt create mode 100644 static/styles/main.css create mode 100644 templates/homepage.html diff --git a/__pycache__/app.cpython-310.pyc b/__pycache__/app.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f100912142f2d2f24ed1dfacca6c724c504b2c2b GIT binary patch literal 327 zcmYjLO-sZu5S>YyQa@Jw8x_{zT|^MXQxSUW!P4DCx|%k%X}~?}57DE)$6KzR{0n<> zvL1FH%$u3Vn-4pi9RlIu`(kl`06($W2dCzm!krN*P-vjT3K2AF_J$SL{3B896BSp< zJCGujC?QXO$qzXOacdg)yg|8)!anF6+)%iLNFfceq7rxB82bfd6nC_l%hq;UK6y8- zdM(SQR}1?p@7Ggnds)8tRbeOXWHSus!#apo)0)$`oe?E$Oeqz48pLD#XN+pd>bh?_ mUDturTo9x6lioUam;WoVwO@8td84o5=Pn~UX4z53PVf%{zC!!} literal 0 HcmV?d00001 diff --git a/app.py b/app.py new file mode 100755 index 0000000..0c62931 --- /dev/null +++ b/app.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +#coding: utf-8 +from flask import Flask, render_template, Markup, request +import datetime, time + +#!---------- squiNotes.py ---------- +# My notes-taking app +#-----------------------------! +class note: + def __init__(self, createtime: int, modtime: int, title: str, text: str): + """ + createtime, modtime : epoch time of note writing / modfying + title and text are str + """ + self.modtime = modtime + self.createtime = createtime + self.title = title + self.text = text + + def rendertime(self, pretimestamp: int): + """ + Render the given timestamp as dd/mm/yyyy-hh:mm + """ + timestamp = datetime.datetime.fromtimestamp(pretimestamp) + timestamp = timestamp.strftime("%d/%m/%Y-%H:%M:%S") + return timestamp + + def flaskrender(self): + """ + Render the note as html for flask, using flask.Markup + """ + rendered = f""" +
+
{self.title}

+
Created : {self.rendertime(self.createtime)} Modified : {self.rendertime(self.modtime)}

+
{self.text}

""" + return Markup(rendered) + + def __str__(self): + return self.title + +def catnotes(notelist: list): + """ + Concatenate a list of notes into a str. + """ + final = "" + for note in notelist: + final += note.flaskrender() + + return final + + + +#----------! MAIN +app = Flask(__name__) + +note1 = note(createtime=1647435623, modtime=1647435682, title="I love coffee", text="I love coffee. It is hot and delicious.") + +note2 = note(createtime=1647435879, modtime=1647435999, title="I wanna go home", text="At first it was like dream, and then O I want to go home") + +#Sort the notes ! Oldest first ! +global notes +notes = [note1, note2] +notes = sorted(notes, key=lambda note: note.modtime, reverse=True) + +@app.route('/') +def homepage(): + return render_template("homepage.html", nr = catnotes(notes)) + +@app.route('/', methods=['POST']) +def newnote(): + notetitle = request.form['title'] + notetext = request.form['text'] + rightnow = int(time.time()) + newnote = note(createtime=rightnow, modtime=rightnow, title=notetitle, text=notetext) + notes.append(newnote) + notes = sorted(notes, key=lambda note: note.modtime, reverse=True) + homepage() + + + + +if __name__ == '__main__': + app.run() diff --git a/myvenv/bin/Activate.ps1 b/myvenv/bin/Activate.ps1 new file mode 100644 index 0000000..b49d77b --- /dev/null +++ b/myvenv/bin/Activate.ps1 @@ -0,0 +1,247 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/myvenv/bin/activate b/myvenv/bin/activate new file mode 100644 index 0000000..c77f8fb --- /dev/null +++ b/myvenv/bin/activate @@ -0,0 +1,69 @@ +# 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 + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/home/justine/Sandbox/Python/SquiNotes/myvenv" +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="(myvenv) ${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT="(myvenv) " + export VIRTUAL_ENV_PROMPT +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/myvenv/bin/activate.csh b/myvenv/bin/activate.csh new file mode 100644 index 0000000..509b25d --- /dev/null +++ b/myvenv/bin/activate.csh @@ -0,0 +1,26 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/home/justine/Sandbox/Python/SquiNotes/myvenv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "(myvenv) $prompt" + setenv VIRTUAL_ENV_PROMPT "(myvenv) " +endif + +alias pydoc python -m pydoc + +rehash diff --git a/myvenv/bin/activate.fish b/myvenv/bin/activate.fish new file mode 100644 index 0000000..e03808b --- /dev/null +++ b/myvenv/bin/activate.fish @@ -0,0 +1,66 @@ +# 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 + set -e VIRTUAL_ENV_PROMPT + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/home/justine/Sandbox/Python/SquiNotes/myvenv" + +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) "(myvenv) " (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT "(myvenv) " +end diff --git a/myvenv/bin/flask b/myvenv/bin/flask new file mode 100755 index 0000000..8b23315 --- /dev/null +++ b/myvenv/bin/flask @@ -0,0 +1,8 @@ +#!/home/justine/Sandbox/Python/SquiNotes/myvenv/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/myvenv/bin/pip b/myvenv/bin/pip new file mode 100755 index 0000000..400b618 --- /dev/null +++ b/myvenv/bin/pip @@ -0,0 +1,8 @@ +#!/home/justine/Sandbox/Python/SquiNotes/myvenv/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/myvenv/bin/pip3 b/myvenv/bin/pip3 new file mode 100755 index 0000000..400b618 --- /dev/null +++ b/myvenv/bin/pip3 @@ -0,0 +1,8 @@ +#!/home/justine/Sandbox/Python/SquiNotes/myvenv/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/myvenv/bin/pip3.10 b/myvenv/bin/pip3.10 new file mode 100755 index 0000000..400b618 --- /dev/null +++ b/myvenv/bin/pip3.10 @@ -0,0 +1,8 @@ +#!/home/justine/Sandbox/Python/SquiNotes/myvenv/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/myvenv/bin/python b/myvenv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/myvenv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/myvenv/bin/python3 b/myvenv/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/myvenv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/myvenv/bin/python3.10 b/myvenv/bin/python3.10 new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/myvenv/bin/python3.10 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/INSTALLER b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/LICENSE.rst b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.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/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/METADATA b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/METADATA new file mode 100644 index 0000000..d617f5f --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/METADATA @@ -0,0 +1,125 @@ +Metadata-Version: 2.1 +Name: Flask +Version: 2.0.3 +Summary: A simple framework for building complex web applications. +Home-page: https://palletsprojects.com/p/flask +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://flask.palletsprojects.com/ +Project-URL: Changes, https://flask.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/flask/ +Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Flask +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: Werkzeug (>=2.0) +Requires-Dist: Jinja2 (>=3.0) +Requires-Dist: itsdangerous (>=2.0) +Requires-Dist: click (>=7.1.2) +Provides-Extra: async +Requires-Dist: asgiref (>=3.2) ; extra == 'async' +Provides-Extra: dotenv +Requires-Dist: python-dotenv ; extra == 'dotenv' + +Flask +===== + +Flask is a lightweight `WSGI`_ web application framework. It is designed +to make getting started quick and easy, with the ability to scale up to +complex applications. It began as a simple wrapper around `Werkzeug`_ +and `Jinja`_ and has become one of the most popular Python web +application frameworks. + +Flask offers suggestions, but doesn't enforce any dependencies or +project layout. It is up to the developer to choose the tools and +libraries they want to use. There are many extensions provided by the +community that make adding new functionality easy. + +.. _WSGI: https://wsgi.readthedocs.io/ +.. _Werkzeug: https://werkzeug.palletsprojects.com/ +.. _Jinja: https://jinja.palletsprojects.com/ + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Flask + +.. _pip: https://pip.pypa.io/en/stable/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/ +- Website: https://palletsprojects.com/p/flask/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/RECORD b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/RECORD new file mode 100644 index 0000000..04004fd --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/RECORD @@ -0,0 +1,52 @@ +../../../bin/flask,sha256=6BbsyJvmjHsBPkzEhen_WsMUjsYDS4upOEjjMISdMUc,249 +Flask-2.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask-2.0.3.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +Flask-2.0.3.dist-info/METADATA,sha256=jK50YtxZfODLQP_GF1sNH6dOXRCI5bBLrAc7pWQwuXw,3839 +Flask-2.0.3.dist-info/RECORD,, +Flask-2.0.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Flask-2.0.3.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +Flask-2.0.3.dist-info/entry_points.txt,sha256=s3MqQpduU25y4dq3ftBYD6bMVdVnbMpZP-sUNw0zw0k,41 +Flask-2.0.3.dist-info/top_level.txt,sha256=dvi65F6AeGWVU0TBpYiC04yM60-FX1gJFkK31IKQr5c,6 +flask/__init__.py,sha256=ubQS5Xt6LMjPSwGO3Jksi5yx8AyuU0vT_VdHjt0j97A,2251 +flask/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30 +flask/__pycache__/__init__.cpython-310.pyc,, +flask/__pycache__/__main__.cpython-310.pyc,, +flask/__pycache__/app.cpython-310.pyc,, +flask/__pycache__/blueprints.cpython-310.pyc,, +flask/__pycache__/cli.cpython-310.pyc,, +flask/__pycache__/config.cpython-310.pyc,, +flask/__pycache__/ctx.cpython-310.pyc,, +flask/__pycache__/debughelpers.cpython-310.pyc,, +flask/__pycache__/globals.cpython-310.pyc,, +flask/__pycache__/helpers.cpython-310.pyc,, +flask/__pycache__/logging.cpython-310.pyc,, +flask/__pycache__/scaffold.cpython-310.pyc,, +flask/__pycache__/sessions.cpython-310.pyc,, +flask/__pycache__/signals.cpython-310.pyc,, +flask/__pycache__/templating.cpython-310.pyc,, +flask/__pycache__/testing.cpython-310.pyc,, +flask/__pycache__/typing.cpython-310.pyc,, +flask/__pycache__/views.cpython-310.pyc,, +flask/__pycache__/wrappers.cpython-310.pyc,, +flask/app.py,sha256=ectBbi9hGmVHAse5TNcFQZIDRkDAxYUAnLgfuKD0Xws,81975 +flask/blueprints.py,sha256=AkAVXZ_MMkjwjklzCAMdBNowTiM0wVQPynnUnXjTL2M,23781 +flask/cli.py,sha256=9v7FDIwWZ3QZsR6ka-qMYzMxSThfmQ4PEA4lkI38R6c,32287 +flask/config.py,sha256=70Uyjh1Jzb9MfTCT7NDhuZWAzyIEu-TIyk6-22MP3zQ,11285 +flask/ctx.py,sha256=Rmw5VOFQdbomLoCQPbU_0FbQkuB56CtpnQVU4yzXYB8,17589 +flask/debughelpers.py,sha256=W82-xrRmodjopBngI9roYH-q08EbQwN2HEGfDAi6SA0,6184 +flask/globals.py,sha256=cWd-R2hUH3VqPhnmQNww892tQS6Yjqg_wg8UvW1M7NM,1723 +flask/helpers.py,sha256=kstplLDtD0Isobilp87Lfmwq1tk2spnHjUf_O5-EhoE,30618 +flask/json/__init__.py,sha256=_YIqOsy8YOSyoLbplFtNcKvF5kwNKenmJ87Ub2Myc0k,12104 +flask/json/__pycache__/__init__.cpython-310.pyc,, +flask/json/__pycache__/tag.cpython-310.pyc,, +flask/json/tag.py,sha256=fys3HBLssWHuMAIJuTcf2K0bCtosePBKXIWASZEEjnU,8857 +flask/logging.py,sha256=1o_hirVGqdj7SBdETnhX7IAjklG89RXlrwz_2CjzQQE,2273 +flask/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask/scaffold.py,sha256=fM9mRy7QBh9fhJ0VTogVx900dDa5oxz8FOw6OK5F-TU,32796 +flask/sessions.py,sha256=46jK4JlcdeBiYbDWTZJn_6u8EqDV-ByRdhlKrbgFi5M,15714 +flask/signals.py,sha256=H7QwDciK-dtBxinjKpexpglP0E6k0MJILiFWTItfmqU,2136 +flask/templating.py,sha256=l96VD39JQ0nue4Bcj7wZ4-FWWs-ppLxvgBCpwDQ4KAk,5626 +flask/testing.py,sha256=T3mr2PLQEkfxoftSTxmGfTtb_FSX3PgfGT8DUGNPWuk,10840 +flask/typing.py,sha256=L5JMltVjj8fovGS1hrMpb13IPfsFDESCCnpRN5CPT4U,1844 +flask/views.py,sha256=nhq31TRB5Z-z2mjFGZACaaB2Et5XPCmWhWxJxOvLWww,5948 +flask/wrappers.py,sha256=VndbHPRBSUUOejmd2Y3ydkoCVUtsS2OJIdJEVIkBVD8,5604 diff --git a/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/REQUESTED b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/WHEEL b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Flask-2.0.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/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/entry_points.txt b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/entry_points.txt new file mode 100644 index 0000000..137232d --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +flask = flask.cli:main diff --git a/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/top_level.txt b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/top_level.txt new file mode 100644 index 0000000..7e10602 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Flask-2.0.3.dist-info/top_level.txt @@ -0,0 +1 @@ +flask diff --git a/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/INSTALLER b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/LICENSE.rst b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.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/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/METADATA b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/METADATA new file mode 100644 index 0000000..3b9355a --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/METADATA @@ -0,0 +1,113 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 3.0.3 +Summary: A very fast and expressive template engine. +Home-page: https://palletsprojects.com/p/jinja/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/jinja/ +Project-URL: Issue Tracker, https://github.com/pallets/jinja/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +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/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/RECORD b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/RECORD new file mode 100644 index 0000000..077ce0d --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/RECORD @@ -0,0 +1,58 @@ +Jinja2-3.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Jinja2-3.0.3.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Jinja2-3.0.3.dist-info/METADATA,sha256=uvKoBSMLvh0qHK-6khEqSe1yOV4jxFzbPSREOp-3BXk,3539 +Jinja2-3.0.3.dist-info/RECORD,, +Jinja2-3.0.3.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +Jinja2-3.0.3.dist-info/entry_points.txt,sha256=Qy_DkVo6Xj_zzOtmErrATe8lHZhOqdjpt3e4JJAGyi8,61 +Jinja2-3.0.3.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 +jinja2/__init__.py,sha256=V3JjnTV-nyIHN6rwj03N1M11fegjGvv-weiHMQwH1pk,2205 +jinja2/__pycache__/__init__.cpython-310.pyc,, +jinja2/__pycache__/_identifier.cpython-310.pyc,, +jinja2/__pycache__/async_utils.cpython-310.pyc,, +jinja2/__pycache__/bccache.cpython-310.pyc,, +jinja2/__pycache__/compiler.cpython-310.pyc,, +jinja2/__pycache__/constants.cpython-310.pyc,, +jinja2/__pycache__/debug.cpython-310.pyc,, +jinja2/__pycache__/defaults.cpython-310.pyc,, +jinja2/__pycache__/environment.cpython-310.pyc,, +jinja2/__pycache__/exceptions.cpython-310.pyc,, +jinja2/__pycache__/ext.cpython-310.pyc,, +jinja2/__pycache__/filters.cpython-310.pyc,, +jinja2/__pycache__/idtracking.cpython-310.pyc,, +jinja2/__pycache__/lexer.cpython-310.pyc,, +jinja2/__pycache__/loaders.cpython-310.pyc,, +jinja2/__pycache__/meta.cpython-310.pyc,, +jinja2/__pycache__/nativetypes.cpython-310.pyc,, +jinja2/__pycache__/nodes.cpython-310.pyc,, +jinja2/__pycache__/optimizer.cpython-310.pyc,, +jinja2/__pycache__/parser.cpython-310.pyc,, +jinja2/__pycache__/runtime.cpython-310.pyc,, +jinja2/__pycache__/sandbox.cpython-310.pyc,, +jinja2/__pycache__/tests.cpython-310.pyc,, +jinja2/__pycache__/utils.cpython-310.pyc,, +jinja2/__pycache__/visitor.cpython-310.pyc,, +jinja2/_identifier.py,sha256=EdgGJKi7O1yvr4yFlvqPNEqV6M1qHyQr8Gt8GmVTKVM,1775 +jinja2/async_utils.py,sha256=jBcJSmLoQa2PjJdNcOpwaUmBxFNE9rZNwMF7Ob3dP9I,1947 +jinja2/bccache.py,sha256=v5rKAlYxIvfJEa0uGzAC6yCYSS3KuXT5Eqi-n9qvNi8,12670 +jinja2/compiler.py,sha256=v7zKz-mgSYXmfXD9mRmi2BU0B6Z-1RGZmOXCrsPKzc0,72209 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=r0JL0vfO7HPlyKZEdr6eVlg7HoIg2OQGmJ7SeUEyAeI,8494 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=Vz20npBX5-SUH_eguQuxrSQDEsLFjho0qcHLdMhY3hA,60983 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=44SjDjeYkkxQTpmC2BetOTxEFMgQ42p2dfSwXmPFcSo,32122 +jinja2/filters.py,sha256=jusKTZbd0ddZMaibZkxMUVKNsOsaYtOq_Il8Imtx4BE,52609 +jinja2/idtracking.py,sha256=WekexMql3u5n3vDxFsQ_i8HW0j24AtjWTjrPBLWrHww,10721 +jinja2/lexer.py,sha256=qNEQqDQw_zO5EaH6rFQsER7Qwn2du0o22prB-TR11HE,29930 +jinja2/loaders.py,sha256=1MjXJOU6p4VywFqtpDZhtvtT_vIlmHnZKMKHHw4SZzA,22754 +jinja2/meta.py,sha256=GNPEvifmSaU3CMxlbheBOZjeZ277HThOPUTf1RkppKQ,4396 +jinja2/nativetypes.py,sha256=KCJl71MogrDih_BHBu6xV5p7Cr_jggAgu-shKTg6L28,3969 +jinja2/nodes.py,sha256=i34GPRAZexXMT6bwuf5SEyvdmS-bRCy9KMjwN5O6pjk,34550 +jinja2/optimizer.py,sha256=tHkMwXxfZkbfA1KmLcqmBMSaz7RLIvvItrJcPoXTyD8,1650 +jinja2/parser.py,sha256=kHnU8v92GwMYkfr0MVakWv8UlSf_kJPx8LUsgQMof70,39767 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=wVRlkEmAgNU67AIQDqLvI6UkNLkzDqpLA-z4Mi3vl3g,35054 +jinja2/sandbox.py,sha256=-8zxR6TO9kUkciAVFsIKu8Oq-C7PTeYEdZ5TtA55-gw,14600 +jinja2/tests.py,sha256=Am5Z6Lmfr2XaH_npIfJJ8MdXtWsbLjMULZJulTAj30E,5905 +jinja2/utils.py,sha256=udQxWIKaq4QDCZiXN31ngKOaGGdaMA5fl0JMaM-F6fg,26971 +jinja2/visitor.py,sha256=ZmeLuTj66ic35-uFH-1m0EKXiw4ObDDb_WuE6h5vPFg,3572 diff --git a/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/WHEEL b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/WHEEL new file mode 100644 index 0000000..5bad85f --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/entry_points.txt b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/entry_points.txt new file mode 100644 index 0000000..3619483 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2 = jinja2.ext:babel_extract [i18n] + diff --git a/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/top_level.txt b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/top_level.txt new file mode 100644 index 0000000..7f7afbf --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Jinja2-3.0.3.dist-info/top_level.txt @@ -0,0 +1 @@ +jinja2 diff --git a/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/INSTALLER b/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/LICENSE.rst b/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/METADATA b/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/METADATA new file mode 100644 index 0000000..485a5e0 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/METADATA @@ -0,0 +1,101 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 2.1.1 +Summary: Safely add untrusted strings to HTML/XML markup. +Home-page: https://palletsprojects.com/p/markupsafe/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/markupsafe/ +Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.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/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/RECORD b/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/RECORD new file mode 100644 index 0000000..2f4cfc9 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/RECORD @@ -0,0 +1,14 @@ +MarkupSafe-2.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-2.1.1.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +MarkupSafe-2.1.1.dist-info/METADATA,sha256=DC93VszmzjLQcrVChRUjtW4XbUwjTdbaplpgdlbFdbs,3242 +MarkupSafe-2.1.1.dist-info/RECORD,, +MarkupSafe-2.1.1.dist-info/WHEEL,sha256=6B0vZ-Dd34LpGZN_4Y_GbBSl1fSb7keGXRzuHUFcOBA,152 +MarkupSafe-2.1.1.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=xfaUQkKNRTdYWe6HnnJ2HjguFmS-C_0H6g8-Q9VAfkQ,9284 +markupsafe/__pycache__/__init__.cpython-310.pyc,, +markupsafe/__pycache__/_native.cpython-310.pyc,, +markupsafe/_native.py,sha256=GR86Qvo_GcgKmKreA1WmYN9ud17OFwkww8E-fiW-57s,1713 +markupsafe/_speedups.c,sha256=X2XvQVtIdcK4Usz70BvkzoOfjTCmQlDkkjYSn-swE0g,7083 +markupsafe/_speedups.cpython-310-x86_64-linux-gnu.so,sha256=MK7Cz4YNHiq95odgpldQy_64gBMHWU12vH7ZlN8KikE,44224 +markupsafe/_speedups.pyi,sha256=vfMCsOgbAXRNLUXkyuyonG8uEWKYU4PDqNuMaDELAYw,229 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/WHEEL b/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/WHEEL new file mode 100644 index 0000000..df5f4fa --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: false +Tag: cp310-cp310-manylinux_2_17_x86_64 +Tag: cp310-cp310-manylinux2014_x86_64 + diff --git a/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/top_level.txt b/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/top_level.txt new file mode 100644 index 0000000..75bf729 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/MarkupSafe-2.1.1.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/INSTALLER b/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/LICENSE.rst b/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/LICENSE.rst new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.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/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/METADATA b/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/METADATA new file mode 100644 index 0000000..551fa0b --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/METADATA @@ -0,0 +1,129 @@ +Metadata-Version: 2.1 +Name: Werkzeug +Version: 2.0.3 +Summary: The comprehensive WSGI web application library. +Home-page: https://palletsprojects.com/p/werkzeug/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://werkzeug.palletsprojects.com/ +Project-URL: Changes, https://werkzeug.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/werkzeug/ +Project-URL: Issue Tracker, https://github.com/pallets/werkzeug/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: dataclasses ; python_version < "3.7" +Provides-Extra: watchdog +Requires-Dist: watchdog ; extra == 'watchdog' + +Werkzeug +======== + +*werkzeug* German noun: "tool". Etymology: *werk* ("work"), *zeug* ("stuff") + +Werkzeug is a comprehensive `WSGI`_ web application library. It began as +a simple collection of various utilities for WSGI applications and has +become one of the most advanced WSGI utility libraries. + +It includes: + +- An interactive debugger that allows inspecting stack traces and + source code in the browser with an interactive interpreter for any + frame in the stack. +- A full-featured request object with objects to interact with + headers, query args, form data, files, and cookies. +- A response object that can wrap other WSGI applications and handle + streaming data. +- A routing system for matching URLs to endpoints and generating URLs + for endpoints, with an extensible system for capturing variables + from URLs. +- HTTP utilities to handle entity tags, cache control, dates, user + agents, cookies, files, and more. +- A threaded WSGI server for use while developing applications + locally. +- A test client for simulating HTTP requests during testing without + requiring running a server. + +Werkzeug doesn't enforce any dependencies. It is up to the developer to +choose a template engine, database adapter, and even how to handle +requests. It can be used to build all sorts of end user applications +such as blogs, wikis, or bulletin boards. + +`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while +providing more structure and patterns for defining powerful +applications. + +.. _WSGI: https://wsgi.readthedocs.io/en/latest/ +.. _Flask: https://www.palletsprojects.com/p/flask/ + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U Werkzeug + +.. _pip: https://pip.pypa.io/en/stable/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/ +- Website: https://palletsprojects.com/p/werkzeug/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/RECORD b/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/RECORD new file mode 100644 index 0000000..df23d62 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/RECORD @@ -0,0 +1,111 @@ +Werkzeug-2.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Werkzeug-2.0.3.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Werkzeug-2.0.3.dist-info/METADATA,sha256=Rxzda7JFgpyr7oqR42Z57bNxRp-pjna_KYhcivqvXY4,4452 +Werkzeug-2.0.3.dist-info/RECORD,, +Werkzeug-2.0.3.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +Werkzeug-2.0.3.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9 +werkzeug/__init__.py,sha256=2frslFsD2EbmZUTfzZ5njDmic66S5f6XMdT24AOGYhk,188 +werkzeug/__pycache__/__init__.cpython-310.pyc,, +werkzeug/__pycache__/_internal.cpython-310.pyc,, +werkzeug/__pycache__/_reloader.cpython-310.pyc,, +werkzeug/__pycache__/datastructures.cpython-310.pyc,, +werkzeug/__pycache__/exceptions.cpython-310.pyc,, +werkzeug/__pycache__/filesystem.cpython-310.pyc,, +werkzeug/__pycache__/formparser.cpython-310.pyc,, +werkzeug/__pycache__/http.cpython-310.pyc,, +werkzeug/__pycache__/local.cpython-310.pyc,, +werkzeug/__pycache__/routing.cpython-310.pyc,, +werkzeug/__pycache__/security.cpython-310.pyc,, +werkzeug/__pycache__/serving.cpython-310.pyc,, +werkzeug/__pycache__/test.cpython-310.pyc,, +werkzeug/__pycache__/testapp.cpython-310.pyc,, +werkzeug/__pycache__/urls.cpython-310.pyc,, +werkzeug/__pycache__/user_agent.cpython-310.pyc,, +werkzeug/__pycache__/useragents.cpython-310.pyc,, +werkzeug/__pycache__/utils.cpython-310.pyc,, +werkzeug/__pycache__/wsgi.cpython-310.pyc,, +werkzeug/_internal.py,sha256=_0GZM3B6gE4eoRTp9K6T7spvY5qJQ9Od9GRIp4lZpzU,18572 +werkzeug/_reloader.py,sha256=B1hEfgsUOz2IginBQM5Zak_eaIF7gr3GS5-0x2OHvAE,13950 +werkzeug/datastructures.py,sha256=m79A8rHQEt5B7qVqyrjARXzHL66Katn8S92urGscTw4,97929 +werkzeug/datastructures.pyi,sha256=uFOqffFoaOEa-43IPlK9otu1X4lDOoqIgG4ULS0ObiE,34119 +werkzeug/debug/__init__.py,sha256=Vn0WQfD9w6DGg1j_2gWpSKKTaFlwxhbCBwi7QQMz1s8,17917 +werkzeug/debug/__pycache__/__init__.cpython-310.pyc,, +werkzeug/debug/__pycache__/console.cpython-310.pyc,, +werkzeug/debug/__pycache__/repr.cpython-310.pyc,, +werkzeug/debug/__pycache__/tbtools.cpython-310.pyc,, +werkzeug/debug/console.py,sha256=jJjid1dIlCNWbDHXTtjJW5XqNfPjSOKbtUmEX5weNdY,5976 +werkzeug/debug/repr.py,sha256=QCSHENKsChEZDCIApkVi_UNjhJ77v8BMXK1OfxO189M,9483 +werkzeug/debug/shared/FONT_LICENSE,sha256=LwAVEI1oYnvXiNMT9SnCH_TaLCxCpeHziDrMg0gPkAI,4673 +werkzeug/debug/shared/ICON_LICENSE.md,sha256=DhA6Y1gUl5Jwfg0NFN9Rj4VWITt8tUx0IvdGf0ux9-s,222 +werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507 +werkzeug/debug/shared/debugger.js,sha256=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/source.png,sha256=RoGcBTE4CyCB85GBuDGTFlAnUqxwTBiIfDqW15EpnUQ,818 +werkzeug/debug/shared/style.css,sha256=h1ZSUVaKNpfbfcYzRb513WAhPySGDQom1uih3uEDxPw,6704 +werkzeug/debug/shared/ubuntu.ttf,sha256=1eaHFyepmy4FyDvjLVzpITrGEBu_CZYY94jE0nED1c0,70220 +werkzeug/debug/tbtools.py,sha256=khUCWQcpbxzeOs5NlT-E9n99BI-ELH9K9RY5exc-X_o,19362 +werkzeug/exceptions.py,sha256=WLCqXBEHm5Xj2d2sfON9XIneeRS3MlNXKH85k1AQIJU,28776 +werkzeug/filesystem.py,sha256=JS2Dv2QF98WILxY4_thHl-WMcUcwluF_4igkDPaP1l4,1956 +werkzeug/formparser.py,sha256=X-p3Ek4ji8XrKrbmaWxr8StLSc6iuksbpIeweaabs4s,17400 +werkzeug/http.py,sha256=Xm3WhYKRQKh_J12514F8y8prILldXceOceeO8EiQEZI,45222 +werkzeug/local.py,sha256=5HbGdD0vVNJgXH3SXfkMjdxIpzy7iqkHJMGCNjljFNo,23664 +werkzeug/middleware/__init__.py,sha256=qfqgdT5npwG9ses3-FXQJf3aB95JYP1zchetH_T3PUw,500 +werkzeug/middleware/__pycache__/__init__.cpython-310.pyc,, +werkzeug/middleware/__pycache__/dispatcher.cpython-310.pyc,, +werkzeug/middleware/__pycache__/http_proxy.cpython-310.pyc,, +werkzeug/middleware/__pycache__/lint.cpython-310.pyc,, +werkzeug/middleware/__pycache__/profiler.cpython-310.pyc,, +werkzeug/middleware/__pycache__/proxy_fix.cpython-310.pyc,, +werkzeug/middleware/__pycache__/shared_data.cpython-310.pyc,, +werkzeug/middleware/dispatcher.py,sha256=Fh_w-KyWnTSYF-Lfv5dimQ7THSS7afPAZMmvc4zF1gg,2580 +werkzeug/middleware/http_proxy.py,sha256=HE8VyhS7CR-E1O6_9b68huv8FLgGGR1DLYqkS3Xcp3Q,7558 +werkzeug/middleware/lint.py,sha256=sAg3GcOhICIkwYX5bJGG8n8iebX0Yipq_UH0HvrBvoU,13964 +werkzeug/middleware/profiler.py,sha256=QkXk7cqnaPnF8wQu-5SyPCIOT3_kdABUBorQOghVNOA,4899 +werkzeug/middleware/proxy_fix.py,sha256=l7LC_LDu0Yd4SvUxS5SFigAJMzcIOGm6LNKl9IXJBSU,6974 +werkzeug/middleware/shared_data.py,sha256=xydEqOhAGg0aQJEllPDVfz2-8jHwWvJpAxfPsfPCu7k,10960 +werkzeug/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/routing.py,sha256=rATL0ZkbTBgvdgJp6WgihuwKyivCF8K4a8kQ4hFgY6A,84581 +werkzeug/sansio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/sansio/__pycache__/__init__.cpython-310.pyc,, +werkzeug/sansio/__pycache__/multipart.cpython-310.pyc,, +werkzeug/sansio/__pycache__/request.cpython-310.pyc,, +werkzeug/sansio/__pycache__/response.cpython-310.pyc,, +werkzeug/sansio/__pycache__/utils.cpython-310.pyc,, +werkzeug/sansio/multipart.py,sha256=BRjBk_mCPjSJzwNVvBgmrJGk3QxA9pYfsgzFki28bxc,8751 +werkzeug/sansio/request.py,sha256=kt7fizz15HPuYKYU1_3TTEkNSuXeeaM4aLcjW84qvv4,20247 +werkzeug/sansio/response.py,sha256=zvCq9HSBBZGBd5Gg412BY9RZIwnKsJl5Kzfd3Kl9sSo,26098 +werkzeug/sansio/utils.py,sha256=V5v-UUnX8pm4RehP9Tt_NiUSOJGJGUvKjlW0eOIQldM,4164 +werkzeug/security.py,sha256=gPDRuCjkjWrcqj99tBMq8_nHFZLFQjgoW5Ga5XIw9jo,8158 +werkzeug/serving.py,sha256=6aV-RKbZm4rUHveQGuh4SY0wFZTmXyR43yD_kCQm8Wo,38287 +werkzeug/test.py,sha256=eUORFaeIDXcmncLdYxgFqYiVdolZkYRY67QV1_ATk20,48235 +werkzeug/testapp.py,sha256=f48prWSGJhbSrvYb8e1fnAah4BkrLb0enHSdChgsjBY,9471 +werkzeug/urls.py,sha256=Du2lreBHvgBh5c2_bcx72g3hzV2ZabXYZsp-picUIJs,41023 +werkzeug/user_agent.py,sha256=WclZhpvgLurMF45hsioSbS75H1Zb4iMQGKN3_yZ2oKo,1420 +werkzeug/useragents.py,sha256=G8tmv_6vxJaPrLQH3eODNgIYe0_V6KETROQlJI-WxDE,7264 +werkzeug/utils.py,sha256=D_dnCLUfodQ4k0GRSpnI6qDoVoaX7-Dza57bx7sabG0,37101 +werkzeug/wrappers/__init__.py,sha256=-s75nPbyXHzU_rwmLPDhoMuGbEUk0jZT_n0ZQAOFGf8,654 +werkzeug/wrappers/__pycache__/__init__.cpython-310.pyc,, +werkzeug/wrappers/__pycache__/accept.cpython-310.pyc,, +werkzeug/wrappers/__pycache__/auth.cpython-310.pyc,, +werkzeug/wrappers/__pycache__/base_request.cpython-310.pyc,, +werkzeug/wrappers/__pycache__/base_response.cpython-310.pyc,, +werkzeug/wrappers/__pycache__/common_descriptors.cpython-310.pyc,, +werkzeug/wrappers/__pycache__/cors.cpython-310.pyc,, +werkzeug/wrappers/__pycache__/etag.cpython-310.pyc,, +werkzeug/wrappers/__pycache__/json.cpython-310.pyc,, +werkzeug/wrappers/__pycache__/request.cpython-310.pyc,, +werkzeug/wrappers/__pycache__/response.cpython-310.pyc,, +werkzeug/wrappers/__pycache__/user_agent.cpython-310.pyc,, +werkzeug/wrappers/accept.py,sha256=NzyLfKH3qC5cSbkEc5azw5-lp_kU8JIrtc8AdGQ0HBs,413 +werkzeug/wrappers/auth.py,sha256=ArJiEn8HHzy1B7wUGuN7s3AHpnClKlaDY0F7N7QZSLA,824 +werkzeug/wrappers/base_request.py,sha256=saz9RyNQkvI_XLPYVm29KijNHmD1YzgxDqa0qHTbgss,1174 +werkzeug/wrappers/base_response.py,sha256=q_-TaYywT5G4zA-DWDRDJhJSat2_4O7gOPob6ye4_9A,1186 +werkzeug/wrappers/common_descriptors.py,sha256=aeVFTsTb0HJn5O8zF6WwELEDDULdOLFkWaUrvD1Huds,866 +werkzeug/wrappers/cors.py,sha256=9Ho7aXd64sB2Msz71jRXAdAI8UyqIJgv-CJsnlfUSzM,814 +werkzeug/wrappers/etag.py,sha256=7SI34rtlXJHyJlqe8B0dFu4ouo6L0DJmYyqwWoY79oc,814 +werkzeug/wrappers/json.py,sha256=h_XfBZV5ZETkHYgONuoSyB9KXR9W90mgBh_mFUysp6c,394 +werkzeug/wrappers/request.py,sha256=I77nwHgCzynmgwJVNw7bo7MfTU_CusNBO0b4TjpIRdQ,24790 +werkzeug/wrappers/response.py,sha256=c24tBeq8G5RwPCU5iCJvJPaKyUEIrfMiWO4yGtTOwmI,35214 +werkzeug/wrappers/user_agent.py,sha256=IMUJCFohZSMsBTmqyJZtjG5y4sB1zxQBE690bixb6uY,419 +werkzeug/wsgi.py,sha256=L7s5-Rlt7BRVEZ1m81MaenGfMDP7yL3p1Kxt9Yssqzg,33727 diff --git a/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/WHEEL b/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.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/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/top_level.txt b/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/top_level.txt new file mode 100644 index 0000000..6fe8da8 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/Werkzeug-2.0.3.dist-info/top_level.txt @@ -0,0 +1 @@ +werkzeug diff --git a/myvenv/lib/python3.10/site-packages/_distutils_hack/__init__.py b/myvenv/lib/python3.10/site-packages/_distutils_hack/__init__.py new file mode 100644 index 0000000..5f40996 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/_distutils_hack/__init__.py @@ -0,0 +1,128 @@ +import sys +import os +import re +import importlib +import warnings + + +is_pypy = '__pypy__' in sys.builtin_module_names + + +warnings.filterwarnings('ignore', + r'.+ distutils\b.+ deprecated', + DeprecationWarning) + + +def warn_distutils_present(): + if 'distutils' not in sys.modules: + return + if is_pypy and sys.version_info < (3, 7): + # PyPy for 3.6 unconditionally imports distutils, so bypass the warning + # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 + return + warnings.warn( + "Distutils was imported before Setuptools, but importing Setuptools " + "also replaces the `distutils` module in `sys.modules`. This may lead " + "to undesirable behaviors or errors. To avoid these issues, avoid " + "using distutils directly, ensure that setuptools is installed in the " + "traditional way (e.g. not an editable install), and/or make sure " + "that setuptools is always imported before distutils.") + + +def clear_distutils(): + if 'distutils' not in sys.modules: + return + warnings.warn("Setuptools is replacing distutils.") + mods = [name for name in sys.modules if re.match(r'distutils\b', name)] + for name in mods: + del sys.modules[name] + + +def enabled(): + """ + Allow selection of distutils by environment variable. + """ + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') + return which == 'local' + + +def ensure_local_distutils(): + clear_distutils() + distutils = importlib.import_module('setuptools._distutils') + distutils.__name__ = 'distutils' + sys.modules['distutils'] = distutils + + # sanity check that submodules load as expected + core = importlib.import_module('distutils.core') + assert '_distutils' in core.__file__, core.__file__ + + +def do_override(): + """ + Ensure that the local copy of distutils is preferred over stdlib. + + See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 + for more motivation. + """ + if enabled(): + warn_distutils_present() + ensure_local_distutils() + + +class DistutilsMetaFinder: + def find_spec(self, fullname, path, target=None): + if path is not None: + return + + method_name = 'spec_for_{fullname}'.format(**locals()) + method = getattr(self, method_name, lambda: None) + return method() + + def spec_for_distutils(self): + import importlib.abc + import importlib.util + + class DistutilsLoader(importlib.abc.Loader): + + def create_module(self, spec): + return importlib.import_module('setuptools._distutils') + + def exec_module(self, module): + pass + + return importlib.util.spec_from_loader('distutils', DistutilsLoader()) + + def spec_for_pip(self): + """ + Ensure stdlib distutils when running under pip. + See pypa/pip#8761 for rationale. + """ + if self.pip_imported_during_build(): + return + clear_distutils() + self.spec_for_distutils = lambda: None + + @staticmethod + def pip_imported_during_build(): + """ + Detect if pip is being imported in a build script. Ref #2355. + """ + import traceback + return any( + frame.f_globals['__file__'].endswith('setup.py') + for frame, line in traceback.walk_stack(None) + ) + + +DISTUTILS_FINDER = DistutilsMetaFinder() + + +def add_shim(): + sys.meta_path.insert(0, DISTUTILS_FINDER) + + +def remove_shim(): + try: + sys.meta_path.remove(DISTUTILS_FINDER) + except ValueError: + pass diff --git a/myvenv/lib/python3.10/site-packages/_distutils_hack/__pycache__/__init__.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/_distutils_hack/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b18a28c23ea8b61be3b6094b20ee3fbb2364472 GIT binary patch literal 5140 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/_distutils_hack/override.py b/myvenv/lib/python3.10/site-packages/_distutils_hack/override.py new file mode 100644 index 0000000..2cc433a --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/_distutils_hack/override.py @@ -0,0 +1 @@ +__import__('_distutils_hack').do_override() diff --git a/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/INSTALLER b/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/LICENSE.rst b/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/LICENSE.rst new file mode 100644 index 0000000..d12a849 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click-8.0.4.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/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/METADATA b/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/METADATA new file mode 100644 index 0000000..74471c7 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/METADATA @@ -0,0 +1,111 @@ +Metadata-Version: 2.1 +Name: click +Version: 8.0.4 +Summary: Composable command line interface toolkit +Home-page: https://palletsprojects.com/p/click/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Changes, https://click.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/click/ +Project-URL: Issue Tracker, https://github.com/pallets/click/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +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/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/RECORD b/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/RECORD new file mode 100644 index 0000000..2b7d742 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/RECORD @@ -0,0 +1,41 @@ +click-8.0.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.0.4.dist-info/LICENSE.rst,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click-8.0.4.dist-info/METADATA,sha256=zcuHOwCuWGOxyQO9MNOlHVHnEqfFAUi2lHHEpCMtLJw,3247 +click-8.0.4.dist-info/RECORD,, +click-8.0.4.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +click-8.0.4.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 +click/__init__.py,sha256=bOKrvMqmR9rN07vN_ycyrdF-EcTCl-EmuAjq-Fp4yPM,3243 +click/__pycache__/__init__.cpython-310.pyc,, +click/__pycache__/_compat.cpython-310.pyc,, +click/__pycache__/_termui_impl.cpython-310.pyc,, +click/__pycache__/_textwrap.cpython-310.pyc,, +click/__pycache__/_unicodefun.cpython-310.pyc,, +click/__pycache__/_winconsole.cpython-310.pyc,, +click/__pycache__/core.cpython-310.pyc,, +click/__pycache__/decorators.cpython-310.pyc,, +click/__pycache__/exceptions.cpython-310.pyc,, +click/__pycache__/formatting.cpython-310.pyc,, +click/__pycache__/globals.cpython-310.pyc,, +click/__pycache__/parser.cpython-310.pyc,, +click/__pycache__/shell_completion.cpython-310.pyc,, +click/__pycache__/termui.cpython-310.pyc,, +click/__pycache__/testing.cpython-310.pyc,, +click/__pycache__/types.cpython-310.pyc,, +click/__pycache__/utils.cpython-310.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/_unicodefun.py,sha256=JKSh1oSwG_zbjAu4TBCa9tQde2P9FiYcf4MBfy5NdT8,3201 +click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 +click/core.py,sha256=MYYmvqVa_dh4x4f-mr7VyNi6197ucYbJ0fsWW5EnCrA,111432 +click/decorators.py,sha256=5ZngOD72Flo8VLN29iarI0A5u_F-dxmqtUqx4KN8hOg,14879 +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=HHH0wMwlDW4WQhs8rRcS9M8MAo9gltGBN6V2PEbsTp0,18003 +click/termui.py,sha256=mZ12uc3-loFaV__vr8buxn9uIjAhy7QwVuZOQ8jDdjc,28873 +click/testing.py,sha256=ptpMYgRY7dVfE3UDgkgwayu9ePw98sQI3D7zZXiCpj4,16063 +click/types.py,sha256=rj2g3biAXKkNKV8vlwbIKSUlixhXybH84N84fwCYqUU,35092 +click/utils.py,sha256=M8tuplcFFHROha3vQ60ZRSakSB_ng6w9e8Uc1AugPZ0,18934 diff --git a/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/WHEEL b/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/WHEEL new file mode 100644 index 0000000..becc9a6 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click-8.0.4.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/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/top_level.txt b/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/top_level.txt new file mode 100644 index 0000000..dca9a90 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click-8.0.4.dist-info/top_level.txt @@ -0,0 +1 @@ +click diff --git a/myvenv/lib/python3.10/site-packages/click/__init__.py b/myvenv/lib/python3.10/site-packages/click/__init__.py new file mode 100644 index 0000000..35176c7 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click/__init__.py @@ -0,0 +1,75 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" +from .core import Argument as Argument +from .core import BaseCommand as BaseCommand +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import MultiCommand as MultiCommand +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .parser import OptionParser as OptionParser +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import get_terminal_size as get_terminal_size +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_os_args as get_os_args +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + +__version__ = "8.0.4" diff --git a/myvenv/lib/python3.10/site-packages/click/__pycache__/__init__.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/click/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7e303f6da9e5358f92c03ccebb257e4d3790596 GIT binary patch literal 2703 zcmc)M*-{%v6b4`clF-J!n4K{gV>XG^>}C;*vDwPN&8bvPjYf^sVYYOSSo{cilT=>2a|EU;(Mi?GO^LodM+JBwb1Wp)m|0xRr! z=cBs{tLz2z8mzGw(d)3zUP5oc274L337hN{^cHNfKccr`o4tzu1fSSz=pEQ$ucLQi zm%V}BgFW^pdLQ=LTj)IG+1uy?IADKr4&5U-V(*}j;h4RP{tTbld*~B5Veg~Az!!EN z{T05l574J@iavz1;?Y%P>iBy#m3O3lXGm>WhL&#N2;=f8Ec>3}`o#*iq^AR^MA0Zb z8DUu%IxIS}VE928SV{{c^bOa#6R8I!L+ON4{Lh-sK4{X@y477uxXrkR}(#gAB{KEUsIz%eZ(+ z+6JvCbqbFMzP1XE$fo{oz|V!LhH7F@l`5|ALUei`7s}VJ5Sz`f?09D0cN}4d()Vh| zy-+;T$k;JnT!6@&Rh&@14vb#}&u1quEoHeP6besESg)%#+b=72*}iuxm21%$6N^|| zC{}u&JzW+~@Y>%+r8NUfYqKb9Us<6~lgD2MX80IR`h1xQZu^Cw@vJuTgRhFO^R?a! zrT@F~Ucpy%gYElPv0kYgZIuM}wPIiG_Ub(u?D_FFTY38&KH8#wE6F42h(o3LLfg-z z*0?Kv9FjTrukeL?DO-x?u8(N>j0HIIsY10vCLfd)n9sM@RT80TSCqn+(|o<_Ol>k; zMRW0Ewj(Sm6jq+gP4^<~vTxo?3(HyJv!bFX>CQW7Qe4cHp5>TYey3Y+#!kD;lf)gX z;@M^Fj0ILj)6sOG{E`w{7iwo<(+zl=Xj+C&)!~!F6IHxAqx%;Zr+6%$FZ8fqG>!*N+Us@quWBls++sXMc zO>|wsw=*JrbAI{lB7b;wb$Ecy=*`Uu|9`&Jwp%W@d5i0NxPfT|mBb1HvnZ*IT%iJ% zM?IN3RKjxW=lI&hjjplTR;%J;_g7$KRhoM7D?voHP_$CCQM6NZP;^psQFK%EU{q70 zzE`#CrQSCbeH8r^0~CW4LlnalBN&jXRf#_1)F|~D7`ROwzhF69y`@$>;2o&OumN$} zQWdGjv7z2kOi;Y1n56hXF-0*=F+(v+F-MW5$YE4tA-=*|%~Qt$#UjNL#WKYT#Yc)& ziZzOLiVccQiYPm!lMz^JB8v;Jve{uWVZbb#WYpK@jV z7|#7%(O3+TyTZ@jg8!JSJvMSzzf|P89}1mwpYDZspTnm`F5pMXQg&fJr)4PS0?WR` rccISN^z_M@ru1ZJn%Ur~n%Js6c<>pDU+aFwh( literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/click/__pycache__/_compat.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/click/__pycache__/_compat.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d97cad84a8ed6d01f1daad06c0a66191917646b GIT binary patch literal 15764 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ef6d61f833fd0358ac7accb756a1a32d4766c92 GIT binary patch literal 16088 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/click/__pycache__/_textwrap.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/click/__pycache__/_textwrap.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfdc359ee24587b1cd1cc10f29d7cee03d6120b1 GIT binary patch literal 1563 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�I-x1RWtgL35_IjMpj2)M&H9sH&2dyqNt$n(4mvc^|FiS1>Y?SD8wtxT^u?z`3OfN3oo-H|1p-p^7T}BmAx}bPd?&YZ_ zwh5OmcmF`-72796o34{a(ERTpe4Yg$Cqdz|rkX#?nc}z5_0s)|U#IEY09|gy_kr)T zyCQ>8c~OQLuv=bUI>@i+(C|HW#bat7;j{k`pWa6BA4K%q1b!Fb|Hzv7k_OVnE%`y* z`TyewvUo-_N`8JH7#yF>>B!~E=qxmBBp%U2CA)xPO}v)kEAbJ0{&ckF-$U^f#*_urm}(dwwmd zyofCtk-bB;Sb1mOL*^#PK13P-NAVTma)pzMGbyL;JJo3D3s6e4q-uz!rPTq7_jZ$d zP8EMjvv>fZDdTwmP)I@!aPNMiGEaai(S|DCL{6`6qW(cKG~Q!~$#ELuT0l3nyqR%U zI`VU(9;ZAXcB^!Bs`8;v5tryzqr9KakMN@p9P2H)-A7oqi~a_o(ba55`L$&K56~Uz zIKJpOTr`nN%JZR}i9e(7Ms1^2m2vh_D>y5y0EJL0ob}GEt62D#mEz|y-ZZpbQ`3H7 z>e|nN|14`-uw|L#X#LNPxcEyD71Wu+y~s>;f6EtCN)1gE13#mj!&i$QH7YaHY!)9fFVbhBS*n!GzFsDds2 zm?#Dbp%8i8>lNbD6kWz{XJ3Xr8sH!Xa^q#Gn}EB%@vXCkE5#P8vsA|#znbcNP*#dYZd z4Er2MmW-r{QhkxkQ5s8Vm1TrjaihDG}>{ugqB@=gE% literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/click/__pycache__/core.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/click/__pycache__/core.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e07bf2f69b5d456fa5b0e1169014901adf85ded GIT binary patch literal 89082 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/click/__pycache__/exceptions.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/click/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30f90b37e2ea688d9ea3003a1d50556c0e5ff60a GIT binary patch literal 10211 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 Ga{dRM*(JmP literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/click/__pycache__/formatting.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/click/__pycache__/formatting.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0fc86c3b371e558b32cec11cbf9de46ad1f3f66 GIT binary patch literal 9476 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/click/__pycache__/globals.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/click/__pycache__/globals.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27809a19e3901b46db0505acc70bf22fc8c2bd08 GIT binary patch literal 2448 zcmZ`*UvC>l5WhX29mh%BCT(fb7FZ!X^u5XXs zJv)w4gxbFH9oon833%i)@OZC0^_jN{sm$!zkW^u%+uPlloBhpiXJ${Wt<^kezyEP# z@O;Vh{=~`g25a7z@R;AA;~po{>k@eT(jS+)CE^LPALxrtQtnoGi3f6NTTbnE;w z-{9w9bcSEy=h2E--SP^1I0AbY_#5!eSx$Dn#+xqyz1L}wyew772*m@RFq#EJuZ zE3cSXip0KLx1xDFwn8&`{}z)*{E%0)u$fMD0N;f8;iBOfpKW;lLMiulz2~I!96DWw zxkheX_ws%#Qe)BD&y0-|(b{DRAE<-YotceP(%Stoi#y5+(;CkvBAK*gJZPm(_DS=` z^_Brd*HRWfU?b28WgI?ejied?z-Btj&y9rbg_+jiK`$&^T0u}gbb+te;MM$J=(>t1 zm$7qr@yq{^>s6ehg|j%QLwCXwZh@a638n+xVa<9^2`ARyFL8g%d**)+v;>|FYNcgpSNji!f4sI*npj9mk`{g0s#@3~{W5vEX)`&@f`j zC{9K+wgxmJHi^OJfFUce@d*|S2AVZzW>3*HGtp@pS801JY}D@0bEauRm5Tmx#^*+w zO*#SW;3DH(@OGPi+Pq%aZ>LPNF)fmx} z-*kE|{a?I-cKFU7h6h3eKn%_jBH&~&b{b1Lv3U?OWAjSS)ttO+EM#zA|H@h&4>DVr z&ucpg7YA;ueD(Oo4c`@!oQTu1@dD=C26XQNo|L?pM+Jh5Gid6~= zluOc07+7zgJoPylL3%zd9eK|Pftn7LvUK>-9S8t{F`tO(g6~CyFLKYh($wd`RHqSW z#F1BfJsyX)*UM{gsB`M`3QJRw@CMPi?`xjqs~ByIT)74c2J6YDudl!>gy%#r-$G$IR8r{^3?sA>JPzK6FgOBO8PSy^D1H{W<)vT9Lnw7_b>oQa!@cBy z2Zz7zVDctR&a)oLhKlY$FCw_e1~qhxZhJbEYy>rBXE=}Ke-j4l57q(ufte>0%%JFC zQ4stpRltUxVhT7Fsni2hNF7x!*paTqQ7;Ot6Lj`8UJnqOcq8byi!r`VJ>CmYl9ZeX zU=j%H&7JuS)D5y+EkoG)Rlh>ulA^HCAh#fz+|^lusm8w#mv{}*1c8enjAJM0018y- zxVx*do>%NFg`%hNqRN+Tqy^(R$Fs@T_U_%e+1vi+=JvNcov*v=g>DZnf01CRGP!RB zWGvKqx8ppfA)vHWj5~kxV4#%DtKX$qznOGV>O#VyKQ)l5pN|#Kr1%1J&|p|sy;V~2 Ws|5R?R3|}jg3wU~|t(IzcfW7C7dydGeNvw*LA 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�ZJ}+U^l(Q+u7+%Om?BVa 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 Gmj4fv@nr-6 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/click/__pycache__/testing.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/click/__pycache__/testing.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b9366748ea23e07cc6e2c709713e0a35f58f898 GIT binary patch literal 15206 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/click/__pycache__/types.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/click/__pycache__/types.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92f76bfa9ec40221c6f2185eb4ac425aab1d9ba7 GIT binary patch literal 32892 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/click/__pycache__/utils.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/click/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dc602b9dc85a9319f5cdcd1c71315045c14416d7 GIT binary patch literal 17996 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 uXXfo0_e 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/myvenv/lib/python3.10/site-packages/click/_termui_impl.py b/myvenv/lib/python3.10/site-packages/click/_termui_impl.py new file mode 100644 index 0000000..4b979bc --- /dev/null +++ b/myvenv/lib/python3.10/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/myvenv/lib/python3.10/site-packages/click/_textwrap.py b/myvenv/lib/python3.10/site-packages/click/_textwrap.py new file mode 100644 index 0000000..b47dcbd --- /dev/null +++ b/myvenv/lib/python3.10/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/myvenv/lib/python3.10/site-packages/click/_unicodefun.py b/myvenv/lib/python3.10/site-packages/click/_unicodefun.py new file mode 100644 index 0000000..9cb30c3 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click/_unicodefun.py @@ -0,0 +1,100 @@ +import codecs +import os +from gettext import gettext as _ + + +def _verify_python_env() -> None: + """Ensures that the environment is good for Unicode.""" + try: + from locale import getpreferredencoding + + fs_enc = codecs.lookup(getpreferredencoding()).name + except Exception: + fs_enc = "ascii" + + if fs_enc != "ascii": + return + + extra = [ + _( + "Click will abort further execution because Python was" + " configured to use ASCII as encoding for the environment." + " Consult https://click.palletsprojects.com/unicode-support/" + " for mitigation steps." + ) + ] + + if os.name == "posix": + import subprocess + + try: + rv = subprocess.Popen( + ["locale", "-a"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="ascii", + errors="replace", + ).communicate()[0] + except OSError: + rv = "" + + good_locales = set() + has_c_utf8 = False + + for line in rv.splitlines(): + locale = line.strip() + + if locale.lower().endswith((".utf-8", ".utf8")): + good_locales.add(locale) + + if locale.lower() in ("c.utf8", "c.utf-8"): + has_c_utf8 = True + + if not good_locales: + extra.append( + _( + "Additional information: on this system no suitable" + " UTF-8 locales were discovered. This most likely" + " requires resolving by reconfiguring the locale" + " system." + ) + ) + elif has_c_utf8: + extra.append( + _( + "This system supports the C.UTF-8 locale which is" + " recommended. You might be able to resolve your" + " issue by exporting the following environment" + " variables:" + ) + ) + extra.append(" export LC_ALL=C.UTF-8\n export LANG=C.UTF-8") + else: + extra.append( + _( + "This system lists some UTF-8 supporting locales" + " that you can pick from. The following suitable" + " locales were discovered: {locales}" + ).format(locales=", ".join(sorted(good_locales))) + ) + + bad_locale = None + + for env_locale in os.environ.get("LC_ALL"), os.environ.get("LANG"): + if env_locale and env_locale.lower().endswith((".utf-8", ".utf8")): + bad_locale = env_locale + + if env_locale is not None: + break + + if bad_locale is not None: + extra.append( + _( + "Click discovered that you exported a UTF-8 locale" + " but the locale system could not pick up from it" + " because it does not exist. The exported locale is" + " {locale!r} but it is not supported." + ).format(locale=bad_locale) + ) + + raise RuntimeError("\n\n".join(extra)) diff --git a/myvenv/lib/python3.10/site-packages/click/_winconsole.py b/myvenv/lib/python3.10/site-packages/click/_winconsole.py new file mode 100644 index 0000000..6b20df3 --- /dev/null +++ b/myvenv/lib/python3.10/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/myvenv/lib/python3.10/site-packages/click/core.py b/myvenv/lib/python3.10/site-packages/click/core.py new file mode 100644 index 0000000..6d8cace --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click/core.py @@ -0,0 +1,2953 @@ +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 ._unicodefun import _verify_python_env +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _flag_needs_value +from .parser import OptionParser +from .parser import split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import echo +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper + +if t.TYPE_CHECKING: + import typing_extensions as te + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +V = t.TypeVar("V") + + +def _complete_visible_commands( + ctx: "Context", incomplete: str +) -> t.Iterator[t.Tuple[str, "Command"]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(MultiCommand, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_multicommand( + base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, MultiCommand): + return + if register: + hint = ( + "It is not possible to add multi commands as children to" + " another multi command that is in chain mode." + ) + else: + hint = ( + "Found a multi command as subcommand to a multi command" + " that is in chain mode. This is not supported." + ) + raise RuntimeError( + f"{hint}. Command {base_command.name!r} is set to chain and" + f" {cmd_name!r} was added as a subcommand but it in itself is a" + f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" + f" within a chained {type(base_command).__name__} named" + f" {base_command.name!r})." + ) + + +def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size))) + + +@contextmanager +def augment_usage_errors( + ctx: "Context", param: t.Optional["Parameter"] = None +) -> t.Iterator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: t.Sequence["Parameter"], + declaration_order: t.Sequence["Parameter"], +) -> t.List["Parameter"]: + """Given a sequence of parameters in the order as should be considered + for processing and an iterable of parameters that exist, this returns + a list in the correct order as they should be processed. + """ + + def sort_key(item: "Parameter") -> t.Tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.Enum): + """This is an :class:`~enum.Enum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show defaults for all options. If not set, + defaults to the value from a parent context. Overrides an + option's ``show_default`` argument. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: t.Type["HelpFormatter"] = HelpFormatter + + def __init__( + self, + command: "Command", + parent: t.Optional["Context"] = None, + info_name: t.Optional[str] = None, + obj: t.Optional[t.Any] = None, + auto_envvar_prefix: t.Optional[str] = None, + default_map: t.Optional[t.Dict[str, t.Any]] = None, + terminal_width: t.Optional[int] = None, + max_content_width: t.Optional[int] = None, + resilient_parsing: bool = False, + allow_extra_args: t.Optional[bool] = None, + allow_interspersed_args: t.Optional[bool] = None, + ignore_unknown_options: t.Optional[bool] = None, + help_option_names: t.Optional[t.List[str]] = None, + token_normalize_func: t.Optional[t.Callable[[str], str]] = None, + color: t.Optional[bool] = None, + show_default: t.Optional[bool] = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params: t.Dict[str, t.Any] = {} + #: the leftover arguments. + self.args: t.List[str] = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self.protected_args: t.List[str] = [] + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map: t.Optional[t.Dict[str, t.Any]] = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand: t.Optional[str] = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width: t.Optional[int] = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width: t.Optional[int] = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args: bool = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options: bool = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names: t.List[str] = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func: t.Optional[ + t.Callable[[str], str] + ] = token_normalize_func + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing: bool = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color: t.Optional[bool] = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default: t.Optional[bool] = show_default + + self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: t.Dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> "Context": + self._depth += 1 + push_context(self) + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> t.Dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: t.ContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._exit_stack.close() + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> "Context": + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: + """Finds the closest object of a given type.""" + node: t.Optional["Context"] = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: t.Type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + @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. + """ + # Verify that the environment is configured correctly, or reject + # further execution to avoid a broken script. + _verify_python_env() + + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt): + echo(file=sys.stderr) + raise Abort() 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. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + .. versionchanged:: 8.0 + Added repr showing the command name + .. versionchanged:: 7.1 + Added the `no_args_is_help` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + + :param deprecated: issues a message indicating that + the command is deprecated. + """ + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.Dict[str, t.Any]] = None, + callback: t.Optional[t.Callable[..., t.Any]] = None, + params: t.Optional[t.List["Parameter"]] = None, + help: t.Optional[str] = None, + epilog: t.Optional[str] = None, + short_help: t.Optional[str] = None, + options_metavar: t.Optional[str] = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool = False, + ) -> None: + super().__init__(name, context_settings) + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params: t.List["Parameter"] = params or [] + + # if a form feed (page break) is found in the help text, truncate help + # text to the content preceding the first form feed + if help and "\f" in help: + help = help.split("\f", 1)[0] + + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + info_dict.update( + params=[param.to_info_dict() for param in self.get_params(ctx)], + help=self.help, + epilog=self.epilog, + short_help=self.short_help, + hidden=self.hidden, + deprecated=self.deprecated, + ) + return info_dict + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> t.List["Parameter"]: + rv = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + rv = [*rv, help_option] + + return rv + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> t.List[str]: + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return list(all_names) + + def get_help_option(self, ctx: Context) -> t.Optional["Option"]: + """Returns the help option object.""" + help_options = self.get_help_option_names(ctx) + + if not help_options or not self.add_help_option: + return None + + def show_help(ctx: Context, param: "Parameter", value: str) -> None: + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + return Option( + help_options, + is_flag=True, + is_eager=True, + expose_value=False, + callback=show_help, + help=_("Show this message and exit."), + ) + + def make_parser(self, ctx: Context) -> OptionParser: + """Creates the underlying option parser for this command.""" + parser = OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + text = self.short_help or "" + + if not text and self.help: + text = make_default_short_help(self.help, limit) + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + text = self.help or "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + formatter.write_paragraph() + with formatter.indentation(): + formatter.write_text(self.epilog) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + value, args = param.handle_parse_result(ctx, opts, args) + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + message = _( + "DeprecationWarning: The command {name!r} is deprecated." + ).format(name=self.name) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) # type: ignore + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class MultiCommand(Command): + """A multi command is the basic implementation of a command that + dispatches to subcommands. The most common version is the + :class:`Group`. + + :param invoke_without_command: this controls how the multi command itself + is invoked. By default it's only invoked + if a subcommand is provided. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is enabled by default if + `invoke_without_command` is disabled or disabled + if it's enabled. If enabled this will add + ``--help`` as argument if no arguments are + passed. + :param subcommand_metavar: the string that is used in the documentation + to indicate the subcommand place. + :param chain: if this is set to `True` chaining of multiple subcommands + is enabled. This restricts the form of commands in that + they cannot have optional arguments but it allows + multiple commands to be chained together. + :param result_callback: The result callback to attach to this multi + command. This can be set or changed later with the + :meth:`result_callback` decorator. + """ + + allow_extra_args = True + allow_interspersed_args = False + + def __init__( + self, + name: t.Optional[str] = None, + invoke_without_command: bool = False, + no_args_is_help: t.Optional[bool] = None, + subcommand_metavar: t.Optional[str] = None, + chain: bool = False, + result_callback: t.Optional[t.Callable[..., t.Any]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + if chain: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "Multi commands in chain mode cannot have" + " optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(__value, *args, **kwargs): # type: ignore + inner = old_callback(__value, *args, **kwargs) # type: ignore + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv + + return decorator + + def resultcallback(self, replace: bool = False) -> t.Callable[[F], F]: + import warnings + + warnings.warn( + "'resultcallback' has been renamed to 'result_callback'." + " The old name will be removed in Click 8.1.", + DeprecationWarning, + stacklevel=2, + ) + return self.result_callback(replace=replace) + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx.protected_args = rest + ctx.args = [] + elif rest: + ctx.protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx.protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with None for regular groups, or an empty list + # for chained groups. + with ctx: + super().invoke(ctx) + return _process_result([] if self.chain else None) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx.protected_args, *ctx.args] + ctx.args = [] + ctx.protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: t.List[str] + ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if split_opt(cmd_name)[0]: + self.parse_args(ctx, ctx.args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + """Given a context and a command name, this returns a + :class:`Command` object if it exists or returns `None`. + """ + raise NotImplementedError + + def list_commands(self, ctx: Context) -> t.List[str]: + """Returns a list of subcommand names in the order they should + appear. + """ + return [] + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class Group(MultiCommand): + """A group allows a command to have subcommands attached. This is + the most common way to implement nesting in Click. + + :param name: The name of the group command. + :param commands: A dict mapping names to :class:`Command` objects. + Can also be a list of :class:`Command`, which will use + :attr:`Command.name` to create the dict. + :param attrs: Other command arguments described in + :class:`MultiCommand`, :class:`Command`, and + :class:`BaseCommand`. + + .. versionchanged:: 8.0 + The ``commmands`` argument can be a list of command objects. + """ + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: t.Optional[t.Type[Command]] = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: t.Optional[str] = None, + commands: t.Optional[t.Union[t.Dict[str, Command], t.Sequence[Command]]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands: t.Dict[str, Command] = commands + + def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_multicommand(self, name, cmd, register=True) + self.commands[name] = cmd + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + if self.command_class is not None and "cls" not in kwargs: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + return decorator + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + if self.group_class is not None and "cls" not in kwargs: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> "Group": + cmd = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> t.List[str]: + return sorted(self.commands) + + +class CommandCollection(MultiCommand): + """A command collection is a multi command that merges multiple multi + commands together into one. This is a straightforward implementation + that accepts a list of different multi commands as sources and + provides all the commands for each of them. + """ + + def __init__( + self, + name: t.Optional[str] = None, + sources: t.Optional[t.List[MultiCommand]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + #: The list of registered multi commands. + self.sources: t.List[MultiCommand] = sources or [] + + def add_source(self, multi_cmd: MultiCommand) -> None: + """Adds a new multi command to the chain dispatcher.""" + self.sources.append(multi_cmd) + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_multicommand(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> t.List[str]: + rv: t.Set[str] = set() + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> t.Iterator[t.Any]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter: + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The later is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: A function to further process or validate the value + after type conversion. It is called as ``f(ctx, param, value)`` + and must return the value. It is called for all sources, + including prompts. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). If ``nargs=-1``, all remaining + parameters are collected. + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: a string or list of strings that are environment variables + that should be checked. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + required: bool = False, + default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, + callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, + nargs: t.Optional[int] = None, + multiple: bool = False, + metavar: t.Optional[str] = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, + shell_complete: t.Optional[ + t.Callable[ + [Context, "Parameter", str], + t.Union[t.List["CompletionItem"], t.List[str]], + ] + ] = None, + autocompletion: t.Optional[ + t.Callable[ + [Context, t.List[str], str], t.List[t.Union[t.Tuple[str, str], str]] + ] + ] = None, + ) -> None: + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type = types.convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = multiple + self.expose_value = expose_value + self.default = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + + if autocompletion is not None: + import warnings + + warnings.warn( + "'autocompletion' is renamed to 'shell_complete'. The old name is" + " deprecated and will be removed in Click 8.1. See the docs about" + " 'Parameter' for information about new behavior.", + DeprecationWarning, + stacklevel=2, + ) + + def shell_complete( + ctx: Context, param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + from click.shell_completion import CompletionItem + + out = [] + + for c in autocompletion(ctx, [], incomplete): # type: ignore + if isinstance(c, tuple): + c = CompletionItem(c[0], help=c[1]) + elif isinstance(c, str): + c = CompletionItem(c) + + if c.value.startswith(incomplete): + out.append(c) + + return out + + self._custom_shell_complete = shell_complete + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + # Skip no default or callable default. + check_default = default if not callable(default) else None + + if check_default is not None: + if multiple: + try: + # Only check the first value against nargs. + check_default = next(_check_iter(check_default), None) + except TypeError: + raise ValueError( + "'default' must be a list when 'multiple' is true." + ) from None + + # Can be None for multiple with empty default. + if nargs != 1 and check_default is not None: + try: + _check_iter(check_default) + except TypeError: + if multiple: + message = ( + "'default' must be a list of lists when 'multiple' is" + " true and 'nargs' != 1." + ) + else: + message = "'default' must be a list when 'nargs' != 1." + + raise ValueError(message) from None + + if nargs > 1 and len(check_default) != nargs: + subject = "item length" if multiple else "length" + raise ValueError( + f"'default' {subject} must match nargs={nargs}." + ) + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + "default": self.default, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + raise NotImplementedError() + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(self) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @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: controls if the default value should be shown on the + help page. Normally, defaults are not shown. If this + value is a string, it shows the string instead of the + value. This is particularly useful for dynamic options. + :param show_envvar: controls if an environment variable should be shown on + the help page. Normally, environment variables + are not shown. + :param prompt: if set to `True` or a non empty string then the user will be + prompted for input. If set to `True` the prompt will be the + option name capitalized. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: if this is `True` then the input on the prompt will be + hidden from the user. This is useful for password + input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + show_default: t.Union[bool, str] = False, + prompt: t.Union[bool, str] = False, + confirmation_prompt: t.Union[bool, str] = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: t.Optional[bool] = None, + flag_value: t.Optional[t.Any] = None, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + help: t.Optional[str] = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + **attrs: t.Any, + ) -> None: + default_is_missing = "default" not in attrs + super().__init__(param_decls, type=type, multiple=multiple, **attrs) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # If prompt is enabled but not required, then the option can be + # used as a flag to indicate using prompt or flag_value. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + if is_flag is None: + if flag_value is not None: + # Implicitly a flag because flag_value was set. + is_flag = True + elif self._flag_needs_value: + # Not a flag, but when used as a flag it shows a prompt. + is_flag = False + else: + # Implicitly a flag because flag options were given. + is_flag = bool(self.secondary_opts) + elif is_flag is False and not self._flag_needs_value: + # Not a flag, and prompt is not enabled, can be used as a + # flag if flag_value is set. + self._flag_needs_value = flag_value is not None + + if is_flag and default_is_missing: + self.default: t.Union[t.Any, t.Callable[[], t.Any]] = False + + if flag_value is None: + flag_value = not self.default + + if is_flag and type is None: + # Re-guess the type from the flag value instead of the + # default. + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = is_flag + self.is_bool_flag = 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'.") + + 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_is_str = isinstance(self.show_default, str) + + if show_default_is_str or ( + default_value is not None and (self.show_default or ctx.show_default) + ): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif 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] + 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) + + return rv + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is None: + return None + + value_depth = (self.nargs != 1) + bool(self.multiple) + + if value_depth > 0: + rv = self.type.split_envvar_value(rv) + + if self.multiple and self.nargs != 1: + rv = batch(rv, self.nargs) + + return rv + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, "Parameter"] + ) -> t.Tuple[t.Any, ParameterSource]: + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option can be + # given as a flag without a value. This is different from None + # to distinguish from the flag not being given at all. + if value is _flag_needs_value: + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + 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/myvenv/lib/python3.10/site-packages/click/decorators.py b/myvenv/lib/python3.10/site-packages/click/decorators.py new file mode 100644 index 0000000..7930a16 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click/decorators.py @@ -0,0 +1,436 @@ +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 + + +def _make_command( + f: F, + name: t.Optional[str], + attrs: t.MutableMapping[str, t.Any], + cls: t.Type[Command], +) -> Command: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + try: + params = f.__click_params__ # type: ignore + params.reverse() + del f.__click_params__ # type: ignore + except AttributeError: + params = [] + + help = attrs.get("help") + + if help is None: + help = inspect.getdoc(f) + else: + help = inspect.cleandoc(help) + + attrs["help"] = help + return cls( + name=name or f.__name__.lower().replace("_", "-"), + callback=f, + params=params, + **attrs, + ) + + +def command( + name: t.Optional[str] = None, + cls: t.Optional[t.Type[Command]] = None, + **attrs: t.Any, +) -> t.Callable[[F], Command]: + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function with + underscores replaced by dashes. If you want to change that, you can + pass the intended name as the first argument. + + All keyword arguments are forwarded to the underlying command class. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: the name of the command. This defaults to the function + name with underscores replaced by dashes. + :param cls: the command class to instantiate. This defaults to + :class:`Command`. + """ + if cls is None: + cls = Command + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd = _make_command(f, name, attrs, cls) # type: ignore + cmd.__doc__ = f.__doc__ + return cmd + + return decorator + + +def group(name: t.Optional[str] = None, **attrs: t.Any) -> t.Callable[[F], Group]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + """ + attrs.setdefault("cls", Group) + return t.cast(Group, command(name, **attrs)) + + +def _param_memo(f: FC, param: Parameter) -> None: + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, "__click_params__"): + f.__click_params__ = [] # type: ignore + + f.__click_params__.append(param) # type: ignore + + +def argument(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]: + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + """ + + def decorator(f: FC) -> FC: + ArgumentClass = attrs.pop("cls", Argument) + _param_memo(f, ArgumentClass(param_decls, **attrs)) + return f + + return decorator + + +def option(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]: + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + """ + + def decorator(f: FC) -> FC: + # Issue 926, copy attrs, so pre-defined options can re-use the same cls= + option_attrs = attrs.copy() + + if "help" in option_attrs: + option_attrs["help"] = inspect.cleandoc(option_attrs["help"]) + OptionClass = option_attrs.pop("cls", Option) + _param_memo(f, OptionClass(param_decls, **option_attrs)) + return f + + return decorator + + +def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--yes`` option which shows a prompt before continuing if + not passed. If the prompt is declined, the program will exit. + + :param param_decls: One or more option names. Defaults to the single + value ``"--yes"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value: + ctx.abort() + + if not param_decls: + param_decls = ("--yes",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("callback", callback) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("prompt", "Do you want to continue?") + kwargs.setdefault("help", "Confirm the action without prompting.") + return option(*param_decls, **kwargs) + + +def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--password`` option which prompts for a password, hiding + input and asking to enter the value again for confirmation. + + :param param_decls: One or more option names. Defaults to the single + value ``"--password"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + if not param_decls: + param_decls = ("--password",) + + kwargs.setdefault("prompt", True) + kwargs.setdefault("confirmation_prompt", True) + kwargs.setdefault("hide_input", True) + return option(*param_decls, **kwargs) + + +def version_option( + version: t.Optional[str] = None, + *param_decls: str, + package_name: t.Optional[str] = None, + prog_name: t.Optional[str] = None, + message: t.Optional[str] = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. On Python < 3.8, the ``importlib_metadata`` + backport must be installed. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. This will be used to detect the + version, so it must match the name of the installed package. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + 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/myvenv/lib/python3.10/site-packages/click/exceptions.py b/myvenv/lib/python3.10/site-packages/click/exceptions.py new file mode 100644 index 0000000..9e20b3e --- /dev/null +++ b/myvenv/lib/python3.10/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/myvenv/lib/python3.10/site-packages/click/formatting.py b/myvenv/lib/python3.10/site-packages/click/formatting.py new file mode 100644 index 0000000..ddd2a2f --- /dev/null +++ b/myvenv/lib/python3.10/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/myvenv/lib/python3.10/site-packages/click/globals.py b/myvenv/lib/python3.10/site-packages/click/globals.py new file mode 100644 index 0000000..480058f --- /dev/null +++ b/myvenv/lib/python3.10/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/myvenv/lib/python3.10/site-packages/click/parser.py b/myvenv/lib/python3.10/site-packages/click/parser.py new file mode 100644 index 0000000..2d5a2ed --- /dev/null +++ b/myvenv/lib/python3.10/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/myvenv/lib/python3.10/site-packages/click/py.typed b/myvenv/lib/python3.10/site-packages/click/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/myvenv/lib/python3.10/site-packages/click/shell_completion.py b/myvenv/lib/python3.10/site-packages/click/shell_completion.py new file mode 100644 index 0000000..1e9df6f --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click/shell_completion.py @@ -0,0 +1,581 @@ +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(value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + # Allow "/" since that starts a path. + return not c.isalnum() and c != "/" + + +def _is_incomplete_option(args: t.List[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(arg): + last_option = arg + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: BaseCommand, ctx_args: t.Dict[str, t.Any], prog_name: str, args: t.List[str] +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + ctx = cli.make_context(prog_name, args.copy(), **ctx_args) + args = ctx.protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, MultiCommand): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) + args = ctx.protected_args + ctx.args + else: + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + sub_ctx = cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx.protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: t.List[str], incomplete: str +) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/myvenv/lib/python3.10/site-packages/click/termui.py b/myvenv/lib/python3.10/site-packages/click/termui.py new file mode 100644 index 0000000..07b5257 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click/termui.py @@ -0,0 +1,806 @@ +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) + if value2: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: t.Optional[bool] = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt.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 get_terminal_size() -> os.terminal_size: + """Returns the current size of the terminal as tuple in the form + ``(width, height)`` in columns and rows. + + .. deprecated:: 8.0 + Will be removed in Click 8.1. Use + :func:`shutil.get_terminal_size` instead. + """ + import shutil + import warnings + + warnings.warn( + "'click.get_terminal_size()' is deprecated and will be removed" + " in Click 8.1. Use 'shutil.get_terminal_size()' instead.", + DeprecationWarning, + stacklevel=2, + ) + return shutil.get_terminal_size() + + +def echo_via_pager( + text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], + color: t.Optional[bool] = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast(t.Iterable[str], text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + from ._termui_impl import pager + + return pager(itertools.chain(text_generator, "\n"), color) + + +def progressbar( + iterable: t.Optional[t.Iterable[V]] = None, + length: t.Optional[int] = None, + label: t.Optional[str] = None, + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, +) -> "ProgressBar[V]": + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + Added the ``update_min_steps`` parameter. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. Added the ``update`` method to + the object. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + if WIN: + os.system("cls") + else: + sys.stdout.write("\033[2J\033[1;1H") + + +def _interpret_color( + color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0 +) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +def style( + text: t.Any, + fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bold: t.Optional[bool] = None, + dim: t.Optional[bool] = None, + underline: t.Optional[bool] = None, + overline: t.Optional[bool] = None, + italic: t.Optional[bool] = None, + blink: t.Optional[bool] = None, + reverse: t.Optional[bool] = None, + strikethrough: t.Optional[bool] = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise TypeError(f"Unknown color {fg!r}") 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/myvenv/lib/python3.10/site-packages/click/testing.py b/myvenv/lib/python3.10/site-packages/click/testing.py new file mode 100644 index 0000000..e395c2e --- /dev/null +++ b/myvenv/lib/python3.10/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/myvenv/lib/python3.10/site-packages/click/types.py b/myvenv/lib/python3.10/site-packages/click/types.py new file mode 100644 index 0000000..550c6ff --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click/types.py @@ -0,0 +1,1049 @@ +import os +import stat +import typing as t +from datetime import datetime +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import _get_argv_encoding +from ._compat import get_filesystem_encoding +from ._compat import open_stream +from .exceptions import BadParameter +from .utils import LazyFile +from .utils import safecall + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + from .core import Parameter + from .shell_completion import CompletionItem + + +class ParamType: + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + """ + + is_composite: t.ClassVar[bool] = False + arity: t.ClassVar[int] = 1 + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[t.Optional[str]] = None + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + return {"param_type": param_type, "name": self.name} + + def __call__( + self, + value: t.Any, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: "Parameter") -> t.Optional[str]: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: "Parameter") -> t.Optional[str]: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + return value + + def split_envvar_value(self, rv: str) -> t.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> "t.NoReturn": + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self) -> int: # type: ignore + raise NotImplementedError() + + +class FuncParamType(ParamType): + def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: + self.name = func.__name__ + self.func = func + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["func"] = self.func + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self.func(value) + except ValueError: + try: + value = str(value) + except UnicodeError: + value = value.decode("utf-8", "replace") + + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = get_filesystem_encoding() + if fs_enc != enc: + try: + value = value.decode(fs_enc) + except UnicodeError: + value = value.decode("utf-8", "replace") + else: + value = value.decode("utf-8", "replace") + return value + return str(value) + + def __repr__(self) -> str: + return "STRING" + + +class Choice(ParamType): + """The choice type allows a value to be checked against a fixed set + of supported values. All of these values have to be strings. + + You should only pass a list or tuple of choices. Other iterables + (like generators) may lead to surprising results. + + The resulting value will always be one of the originally passed choices + regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` + being specified. + + See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + """ + + name = "choice" + + def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: + self.choices = choices + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["choices"] = self.choices + info_dict["case_sensitive"] = self.case_sensitive + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + choices_str = "|".join(self.choices) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: "Parameter") -> str: + return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + # Match through normalization and case sensitivity + # first do token_normalize_func, then lowercase + # preserve original `value` to produce an accurate message in + # `self.fail` + normed_value = value + normed_choices = {choice: choice for choice in self.choices} + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(value) + normed_choices = { + ctx.token_normalize_func(normed_choice): original + for normed_choice, original in normed_choices.items() + } + + if not self.case_sensitive: + normed_value = normed_value.casefold() + normed_choices = { + normed_choice.casefold(): original + for normed_choice, original in normed_choices.items() + } + + if normed_value in normed_choices: + return normed_choices[normed_value] + + choices_str = ", ".join(map(repr, self.choices)) + self.fail( + ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = map(str, self.choices) + + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + def __init__(self, formats: t.Optional[t.Sequence[str]] = None): + self.formats = formats or ["%Y-%m-%d", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase(ParamType): + _number_class: t.ClassVar[t.Type] + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +class _NumberRangeBase(_NumberParamTypeBase): + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + min=self.min, + max=self.max, + min_open=self.min_open, + max_open=self.max_open, + clamp=self.clamp, + ) + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import operator + + rv = super().convert(value, param, ctx) + lt_min: bool = self.min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, self.min) + gt_max: bool = self.max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, self.max) + + if self.clamp: + if lt_min: + return self._clamp(self.min, 1, self.min_open) # type: ignore + + if gt_max: + return self._clamp(self.max, -1, self.max_open) # type: ignore + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + raise NotImplementedError + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase, IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp( # type: ignore + self, bound: int, dir: "te.Literal[1, -1]", open: bool + ) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase, FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + if not open: + return bound + + # Could use Python 3.9's math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType): + name = "boolean" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if value in {False, True}: + return bool(value) + + norm = value.strip().lower() + + if norm in {"1", "true", "t", "yes", "y", "on"}: + return True + + if norm in {"0", "false", "f", "no", "n", "off"}: + return False + + self.fail( + _("{value!r} is not a valid boolean.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import uuid + + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Starting with Click 2.0, files can also be opened atomically in which + case all writes go into a separate file in the same folder and upon + completion the file will be moved over to the original location. This + is useful if a file regularly read by other users is modified. + + See :ref:`file-args` for more information. + """ + + name = "filename" + envvar_list_splitter = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: t.Optional[bool] = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(mode=self.mode, encoding=self.encoding) + return info_dict + + def resolve_lazy_flag(self, value: t.Any) -> bool: + if self.lazy is not None: + return self.lazy + if value == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + if hasattr(value, "read") or hasattr(value, "write"): + return value + + lazy = self.resolve_lazy_flag(value) + + if lazy: + f: t.IO = t.cast( + t.IO, + LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ), + ) + + if ctx is not None: + ctx.call_on_close(f.close_intelligently) # type: ignore + + return f + + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(safecall(f.close)) + else: + ctx.call_on_close(safecall(f.flush)) + + return f + except OSError as e: # noqa: B014 + self.fail(f"{os.fsdecode(value)!r}: {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +class Path(ParamType): + """The ``Path`` type is similar to the :class:`File` type, but + 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 writable: The file or directory must be writable. + :param readable: The file or directory must be readable. + :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.0 + Allow passing ``type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter = os.path.pathsep + + def __init__( + self, + exists: bool = False, + file_okay: bool = True, + dir_okay: bool = True, + writable: bool = False, + readable: bool = True, + resolve_path: bool = False, + allow_dash: bool = False, + path_type: t.Optional[t.Type] = None, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.writable = writable + self.readable = readable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + exists=self.exists, + file_okay=self.file_okay, + dir_okay=self.dir_okay, + writable=self.writable, + readable=self.readable, + allow_dash=self.allow_dash, + ) + return info_dict + + def coerce_path_result(self, rv: t.Any) -> t.Any: + if self.type is not None and not isinstance(rv, self.type): + if self.type is str: + rv = os.fsdecode(rv) + elif self.type is bytes: + rv = os.fsencode(rv) + else: + rv = self.type(rv) + + return rv + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + # 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!r} is a directory.").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.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, + ) + + 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/myvenv/lib/python3.10/site-packages/click/utils.py b/myvenv/lib/python3.10/site-packages/click/utils.py new file mode 100644 index 0000000..8dd3a00 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/click/utils.py @@ -0,0 +1,588 @@ +import os +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType + +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import _find_binary_writer +from ._compat import auto_wrap_for_ansi +from ._compat import binary_streams +from ._compat import get_filesystem_encoding +from ._compat import open_stream +from ._compat import should_strip_ansi +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import WIN +from .globals import resolve_color_default + +if t.TYPE_CHECKING: + import typing_extensions as te + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: F) -> F: + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args, **kwargs): # type: ignore + try: + return func(*args, **kwargs) + except Exception: + pass + + return update_wrapper(t.cast(F, wrapper), func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(get_filesystem_encoding()) + except UnicodeError: + return value.decode("utf-8", "replace") + return str(value) + + +def make_default_short_help(help: str, max_length: int = 45) -> str: + """Returns a condensed version of help string.""" + # Consider only the first paragraph. + paragraph_end = help.find("\n\n") + + if paragraph_end != -1: + help = help[:paragraph_end] + + # Collapse newlines, tabs, and spaces. + words = help.split() + + if not words: + return "" + + # The first paragraph started with a "no rewrap" marker, ignore it. + if words[0] == "\b": + words = words[1:] + + total_length = 0 + last_index = len(words) - 1 + + for i, word in enumerate(words): + total_length += len(word) + (i > 0) + + if total_length > max_length: # too long, truncate + break + + if word[-1] == ".": # sentence end, truncate without "..." + return " ".join(words[: i + 1]) + + if total_length == max_length and i != last_index: + break # not at sentence end, truncate with "..." + else: + return " ".join(words) # no truncation needed + + # Account for the length of the suffix. + total_length += len("...") + + # remove words until the length is short enough + while i > 0: + total_length -= len(words[i]) + (i > 0) + + if total_length <= max_length: + break + + i -= 1 + + return " ".join(words[:i]) + "..." + + +class LazyFile: + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + """ + + def __init__( + self, + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, + ): + self.name = filename + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.Optional[t.IO] + + if filename == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) + else: + if "r" in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self.open(), name) + + def __repr__(self) -> str: + if self._f is not None: + return repr(self._f) + return f"" + + def open(self) -> t.IO: + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + except OSError as e: # noqa: E402 + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) 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 get_os_args() -> t.Sequence[str]: + """Returns the argument part of ``sys.argv``, removing the first + value which is the name of the script. + + .. deprecated:: 8.0 + Will be removed in Click 8.1. Access ``sys.argv[1:]`` directly + instead. + """ + import warnings + + warnings.warn( + "'get_os_args' is deprecated and will be removed in Click 8.1." + " Access 'sys.argv[1:]' directly instead.", + DeprecationWarning, + stacklevel=2, + ) + return sys.argv[1:] + + +def format_filename( + filename: t.Union[str, bytes, os.PathLike], shorten: bool = False +) -> str: + """Formats a filename for user display. The main purpose of this + function is to ensure that the filename can be displayed at all. This + will decode the filename to unicode if necessary in a way that it will + not fail. Optionally, it can shorten the filename to not include the + full path to the filename. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + + return os.fsdecode(filename) + + +def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Windows (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Windows (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no affect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = "APPDATA" if roaming else "LOCALAPPDATA" + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser("~") + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) + return os.path.join( + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) + + +class PacifyFlushWrapper: + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped: t.IO) -> None: + self.wrapped = wrapped + + def flush(self) -> None: + try: + self.wrapped.flush() + except OSError as e: + import errno + + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr: str) -> t.Any: + return getattr(self.wrapped, attr) + + +def _detect_program_name( + path: t.Optional[str] = None, _main: ModuleType = sys.modules["__main__"] +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + if getattr(_main, "__package__", None) is None or ( + os.name == "nt" + and _main.__package__ == "" + and not os.path.exists(path) + and os.path.exists(f"{path}.exe") + ): + # Executed a file, like "python app.py". + return os.path.basename(path) + + # Executed a module, like "python -m example". + # Rewritten by Python from "-m script" to "/path/to/script.py". + # Need to look at main module to determine how it was executed. + py_module = t.cast(str, _main.__package__) + name = os.path.splitext(os.path.basename(path))[0] + + # A submodule like "example.cli". + if name != "__main__": + py_module = f"{py_module}.{name}" + + return f"python -m {py_module.lstrip('.')}" + + +def _expand_args( + args: t.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> t.List[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + matches = glob(arg, recursive=glob_recursive) + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/myvenv/lib/python3.10/site-packages/distutils-precedence.pth b/myvenv/lib/python3.10/site-packages/distutils-precedence.pth new file mode 100644 index 0000000..6de4198 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/distutils-precedence.pth @@ -0,0 +1 @@ +import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'stdlib') == 'local'; enabled and __import__('_distutils_hack').add_shim(); diff --git a/myvenv/lib/python3.10/site-packages/flask/__init__.py b/myvenv/lib/python3.10/site-packages/flask/__init__.py new file mode 100644 index 0000000..feb5334 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/flask/__init__.py @@ -0,0 +1,46 @@ +from markupsafe import escape +from markupsafe import Markup +from werkzeug.exceptions import abort as abort +from werkzeug.utils import redirect as redirect + +from . import json as json +from .app import Flask as Flask +from .app import Request as Request +from .app import Response as Response +from .blueprints import Blueprint as Blueprint +from .config import Config as Config +from .ctx import after_this_request as after_this_request +from .ctx import copy_current_request_context as copy_current_request_context +from .ctx import has_app_context as has_app_context +from .ctx import has_request_context as has_request_context +from .globals import _app_ctx_stack as _app_ctx_stack +from .globals import _request_ctx_stack as _request_ctx_stack +from .globals import current_app as current_app +from .globals import g as g +from .globals import request as request +from .globals import session as session +from .helpers import flash as flash +from .helpers import get_flashed_messages as get_flashed_messages +from .helpers import get_template_attribute as get_template_attribute +from .helpers import make_response as make_response +from .helpers import safe_join as safe_join +from .helpers import send_file as send_file +from .helpers import send_from_directory as send_from_directory +from .helpers import stream_with_context as stream_with_context +from .helpers import url_for as url_for +from .json import jsonify as jsonify +from .signals import appcontext_popped as appcontext_popped +from .signals import appcontext_pushed as appcontext_pushed +from .signals import appcontext_tearing_down as appcontext_tearing_down +from .signals import before_render_template as before_render_template +from .signals import got_request_exception as got_request_exception +from .signals import message_flashed as message_flashed +from .signals import request_finished as request_finished +from .signals import request_started as request_started +from .signals import request_tearing_down as request_tearing_down +from .signals import signals_available as signals_available +from .signals import template_rendered as template_rendered +from .templating import render_template as render_template +from .templating import render_template_string as render_template_string + +__version__ = "2.0.3" diff --git a/myvenv/lib/python3.10/site-packages/flask/__main__.py b/myvenv/lib/python3.10/site-packages/flask/__main__.py new file mode 100644 index 0000000..4e28416 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/flask/__main__.py @@ -0,0 +1,3 @@ +from .cli import main + +main() diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/__init__.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7e13b1bc8ee3e3710abaf6d095e72cdb56bd77f GIT binary patch literal 1894 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%0AQat7{4ebG2BU2M#U_XyL+R2J2 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/blueprints.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/blueprints.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d45e379a253e38e759412cdf9dbb2b133c020205 GIT binary patch literal 21629 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 Xa#{{?E%-xh%<#XA)PSC;ski?(2)`=& literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/cli.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/cli.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7bffd19a86804513df676c36a074103515852525 GIT binary patch literal 27289 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 I7cO4-A2y8Q1^@s6 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/ctx.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/ctx.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..60ebd8881ec9429e500533c35bc7f14812327562 GIT binary patch literal 15568 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 N&y*`=yIfkh@ITnh36}r> literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/debughelpers.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/debughelpers.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a4443e99024276cac626089a069a873160c884d GIT binary patch literal 6500 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/globals.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/globals.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..952a741bfe8dc27a759343d0bcb843661545d692 GIT binary patch literal 1804 zcmcIl%WvaE829AqIFG*4=PocDkchM-EL&_`s%p~}!W^uLXOg%bd%WXuo23^@;i{{7PmN&mP=qS$x>K$%LH|0SqZCdHRmh9*W4Ph1Uawkk9d{Wq#ZWg23V`S zE}gLHHgn#Vt+4I3bG{)v;hMV!yyLF(rn|vgy#1r)Zt@OagMEvy^9|U$qIPY$+d174 zRnfgB?k?XHdwlEA0=Xm+I_wa+U`oblWPz7E@*d_+AY49Yaj4^(C!^Vc0;Ro z@CtIZPI@G($4r}mNno9?l+Wa;RN=*`QLDiV*!bW`+88I_`$2pq@2{rRL z0044ML%|}Dr_9i~N+~l&gs~Bv8b#AY&~Y012BI+;n0etB3Bd(-SVS3^E(Lvhc6NFj zsZZ&Vijq_sa2Av+KMh4>7@k4l&j3=yI9A#TBA{VmVk1CJr|Jp=vr82K0z?{XHIgFi zvv;8~|B@h!V+pC`$NvvW0+;|Qr^n0}^eQmZJS#ud8j_;-gR9ZG@C|s=M1|nX=6!mm zK#-Th!Big6L>QXJ6btuH)Wzvw=)>}ssl4BVdJcuasLq?UgwoMaX#o*eRqXpXKDSQ^ zm@=#*Sg$D%W1W-l$pXH&)Y45;GUaRX12L5aSy;z}dev;xTMmuqo0*s3RAyzP z;;i*7jZ6@VW383Wx^K7%t-UarWc7)FUK@Q=$|?~Hg>FOK8%uA&_Q!XFDU@b#p5h3I z!H`9Kq-KNDxtXeHFno~)Ckp0c5Y8_}bUBd0Xb|VNkNb~593+7eAH;x$O~7Z2-^XB5 zd=>q84zQ(C7io;JyrLOzL(tbYkQ%Y!=McRO!hhCC_yT z-s04|;CBa3?}3<=AzY6X+P@)cc;g`An872Aa#x%h(GTz_xVEjVxg2ox%j(N<&7+05*3&qX8vhk!?Xz!B zkG-R($46g&aq>hTpdklPhSAGU&g^BpqzGtVX}~0OlB3aU_y*>jcQ!9+kwy!3d2O(e z*_!9EC{o2Wlz5(g3+*@``W+;9Rm!=%dG=wbcq+vw*p~#$vp|n*T_2AAte~J)}IIEzW^Y;?0EnH literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/helpers.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/helpers.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..771ec27e6d5e4b521dc58ef6211881941f90bb78 GIT binary patch literal 27442 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 Lq5I269<}}-<8Gi# literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/logging.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/logging.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f07742e71a951751657755d41c1eec484a7ef223 GIT binary patch literal 2465 zcmZuz-EJF26rNeHe-k%NNUN42H2hSprC_(J;KGU`M4Ge}QK*yv!c~>Z=KZZH3HYK zzuoTsULoWUoSeOiF!>m6{WA=L2pW-2_Deg|&YeyHex1l27CS{sL_xSwX;|)*DUpRi zMZE(n#i%;0b!xCu6eU=l2^>)tmFMm=+L;wqNd|RTzYw^hCT5k-$xX-P>lRA8N_QaC@Gf=%zLT3_Pz{vr!=IBpEY5iG#2=RuX55CHpaJ z`%trPdl0-A#=Yj2P3}IAMI;pjN4FuLuzemI*xg3MZA>3Bo0gDOWV1(m4jYU$L}Ni1 zNd|R_&^M>M+fo_qV1xNQzF}B693=`iz^Ecr(l^m0+Z^5lAb|w15*7lU+PZn3JTUE6 zE5IXbPP0d3b~0pkvZN3K#PM+E3~CKmxp0PQS;l*zO5(Htn5Cr*v=>An@$K-}qgFo| z%GO}4O&H787G&K`4q6WTuY2Loo(xH(bjJUtc zdthTzZB0`hP11$)>DG~7NVLV8n#B}LXfKtIY@J^C9`i52t=C~VBFE%}`~bgS(<7?h zG4wkU05yg4ymCYb&Rz11KBMZg0FuJIpx>$z;o4^xJ#+lqdS~h#qtW9k;&}1b)z;g&-^>+|~H(~e|YJXh8S_b5MvP-`plss_+v}S{DzJS)Y z+clijY2Hf9c}401cxHz^15asnxhG?JFj6b;VqApS`Q{2ae8122jt#Jrdp4IN6cWAC zJayS#Zxqw{bLpoA9#7Ir?x)i#ssZnT8l1z<%l=s5JWI%?X0w_uWB zFT+rAUFtels7p(ZLtVG@xBI7CRafEF;YW|I-{cyw9?FJ(%&y6F+Cy!=Y#_2WKd8G0KKbWJT8JRmQavQ`3PvI zufbZyRB`19<(TqcfX(+Lw!36jb5R6=BpF*!8cxLmI0)(xuvkhVMJ{LC;FsHCK^gzL zC`yuPT*yVD&5coQ4}v!{f!VR<%b94{otwCV9J8eTvJafiy`G-660$q)dDss!=G{n& zC8qNN@?v<6Av&jcl%6EfiA$V|HmIsvI|+u@Z4 zE87KiA3mB!?qq0uh%`ifoUP9~v$W#Wpbyl0u+X@qu3-%L#{%MMCL<^?rKWRXU#i_h zIqt!?0yTg*JFEBCRzH2Pc`r)^8zC)i!?)(aXSpCx3+^UK zl+{#!4Q++uklQX6Ra!lLv`L;R&VFiag_Y&3Ay-i9_1iF1NQG7?6zG(oNT;ws P7iVjiTv&7exl;TWtl7eU literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/scaffold.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/scaffold.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2240b8f3983515f8849b7ceb39427fb45356e088 GIT binary patch literal 24768 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 sS^XPPx# literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/sessions.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/sessions.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76f092e75eb349c4825e63a5d4491cd066cd6618 GIT binary patch literal 13617 zcmb7L%X1sadB+R}4}#!Z@73Dl^*R#Q66no3DHr{SWSX*=T~V^g^=4rwzz{tohXgR# zo`EPLP~uoynoBBGsgw`7B*z@*>XUD&+DpnwdGq^q&kP12xUol? z#!Pol_t)S1`?~4R%~f)^e)*qo@9ljvm-`i8vR45wcW?*)g+k zui3Ubg?hnYTfSZF6zfIxY_&_Ba=omc3wWNX OyV9Ah&*Hfx%I#`ru0E%pXWH|f zh5CY#bIqeg`L3E_slJR}mG(;KLj8j3HQT<}xm3T@xm>?&|O{VHk}#S&_k+^gtu4e!fh1@9}*&H8KZ!t-4HI_fTni>SNkzJ@-p z3u7m@dg<3#b#8mrh~^*kLuaq;K5%+HuX})+vT#B-^g1q{tKW9Sj(5;?!oKADrS~7d zx8v-)yJ!|WuJoL?H*h6;ZaD4sp3^%1j@Jr*ZD4d%7QCS0_3&_E*Eu+F#h>mx+#dU? zaIftI$E#*kdhCAGcY`o0KX!wj-woUrAaKJcD;-N8GXPkT;R1h!-E zIf2)*1y+MrN1oazUU+B+y0`6wq4f6pp&QhqqI7|tZi|afEr~?%K{reQ(#4SPYHxpKE$~A3b`L~!4$xm5 zyF`U2PP^}Jx7aPY_?*YB@Un=9FgG-w8$;mc8xS$z#@v<*sK@%OJ>PGynsOPh@)8R! zQ5P5ZbP>gM+zOCI^WN_2Y*cPEx=zP!G=Qx}#~1xJpR0|=M}4Oqx0D(U;kQs-?nxi; zhNqJ2QR79$zzsuJM)|IL5|uH6x9_Z# z7*^3LlnavWE!<;QDSiunhKDRe3$qTEt`{KOmS$y<-?x5}+c!i}ls=vV6PMi?FgVye zuAdQ=kE>wnMb$DJ3jo`T>N_fux%%w6?Nlst_3BT+wDmbqVJTYKaa$1j4c|ZZ+?c`+ zbX@M+PT<-t@J3+o`!bP;fCA$R+wa-|6b7DJ>dT;3so1y_zbDc|J>hl*M5^7!0HNaX zb;{IwGr@S%2Jfm*I2vggGCOfr5iQ(;⋘sdZAXof%nYFe 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 bQ&o+k$(luDF~59$@vZr#++P=ea3TMHM$@3q literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/signals.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/signals.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..067acbce0d6584cf50b57926f74fec83510ebe63 GIT binary patch literal 2404 zcmZuy&5k2A5O)8hlj$Tg$?QK`Aqx&LXeVLC6`@6lMSGb=1PSmStWMjR>79-{YdbTW zXiv;M2psn%5YND|ubg-UkPuY4(@nyzBUj4ha+T|=DtFQE#{|Z&zdf9O-y!68Tx<`J zkw-AiFF*tlUQU>|j)C)Ye;%;FBO+kocSPL_iEOPt@Cg1y$ilCPXo={Wi0H~^0j%1h z1FH_Kq92IzE{P1T2y4ssPaU!Mv&Z%v7XueNt_xh(aXsLAj_U*0cicX3`;I#R?!a*a z;0BHx0ylKrA#jJnn~d9VB{e?ne+a8n(D>~&CvM&>h(nmR-Y(Wv~_v+-Fmm*eM4V>2bk zlSGMW@pAn2%AOT!JbAIqP6{i{c)q%j>SCN{)A7RXeKLCZ@z`Wm9s>yH$tmc}^2D5j z4DV=0i&Ys_d>0%Uj4bxng9f)zwW$%TqjuTh9HZvEj5(hdVwoe~$Uy`Jd9UM%B~urACV%sv!f3}uJ10UJJx1?24Lz$Gj> z^ayz}qtiT7=Tg(m(4|T)k}OZAxqLuVEfXtgLT6AFGzH)dz0B+xwP%tVz%izqktr+n z4C)lN5=)ajFD?x=Qi)8R(qcw8`J&UM1rvD=CbPUUmZ-*z)Vk2LP&84Hemp93BS(}z zQO3$dpe0eDyiovUbn}2_wlU)zV6joCl0xI9APRlDoJ(a5oE2b&s-kj{RklaX@ss}w zShpZ!Z*&4ynd^iBFwh5tlUF`a=)ei7O3imR$~NcFUz}@vpZ-@pSd5v!&cCmF5=C;p;%}!Hz~J`Zf}**ot5gW6vsa z4>;F7A#~671#t5XcL;!US3L#7yMoYv^pE_L_0x&#OsJ?+YL<&dp&<)9bw#H#O_rw0 zN?m%;@;cEgRE4}y%n1~c6eD;Ad*3XT&F1n;tPXvrxxI_Sh0x+yPWLee6#5?g{_z9< z@AYsy7N=F^=TP_2&OVX@BsY*?KJ^gEA(ERw%HVUg(syzB4ldhip_EK5>uwl+ktM9R zSxCWc!R}f)hrkn^H^mEfTk9M=l#11(=HrLo5VxVE4;s0A}9 zPICs5o3#X5P(xmiMRBRtw@wQS=BvZ2$N0>H^P?MOWLFFD>hi+cn}Trw6tuM)J`M$5h9 zticAbudrboY&HU3W5a4}_y!wRW5a7~Sd9%FaW=+vm(6h~1UE_LpPHbbA=zBJW;iCsqjD@@QIAInn$BI`>xX{AK`#{aZk*hV2NC-UY}PO^ z=lT7<*Ge9HGVxmv(Np&XKOAtGO!cnp^L8va#v&Z%C1+bN7W8P0Gi*}28TyiTbM{(( zyB&utvjtBEB5Kjyi_Zi>WgA7lk1El+`cO*@)Ow%|jZ|m)Ko^Ut-ZfK$8P~KF*g((a zRO?#kF{!6p0%d_Rz@5^`-VNUN2VvqCH*;HYl<>!i+ZS<*OBss|*S#Hec`K3bgVA#z zT&=qJ9!N|G;^=|vN6dZDd9b0ncQd_{l}A&v@@P`S76jbPOya&MgA#L8%~Q32s$n+q z>_f1(X@k%B%?xq$r6$f{@ax}i9>hJq*&WCvi1_9n4&09)Z+>!|9K_M)-r*pCw7A^t z9Ut-NXfq7 z5|S$0vUN*$^r|XV&;mNDO^FI^%|_Wmkv~Mmm4Y@6rZ-InK`@J3-4ZJ?yA3sD4&Z7t z8v>~a67k7D?#1Gvbi?2wcYRmz&LH%K`(Y4u{dZivuRyN5KS^aec;u1mf^R zC5DGl{3ztC!`-;;9>kAag|j!MyB`jCUj$L&K2pcX*i8=n#0?U+4H+sqP5#HP!}J) z+d}yWMZSfK>8Wv~i)yMr)lLCdreD(%y=$ae8*opJzv*A-x|Zsx=9x?b?~L8d4s-ps zn(HmWq0U|>jQ9P}>k{j{d^DRUOE97Tnd5mu6eOM}R&a2`%q+>nwrYs;=ur^Nt0ii# zqk0oX5}+Nurq=<7lck9*H;Nq_W!jxs=y_Sy^LjBGghbao?{MIUqmi=bvABh9J;}`L z(Xl#W;#krFq0S~gU7@oHV2)<#7fT8=Ei|V-@*!I&^53YY0Ju!HACR+{!%M6J`)l!X z*Wos5wAM8BTkck2b4?VRRoM*Y&wTIsH8zX+weLMY_Z73_yUfvj*aDwdHXn+j?qRV- zR>!^z>b{F~Q}~&4YzcjJRssZ8vXvWw?E6XU0D7(TCAat|pI&!!>E}=n;~4q{D?N$a zNI@K64TF|XI8|oN4|8>zhCi8UE@Y1I94UnG7XX_;PGuWKl28<6;iLDoFLgMk9CL=a zfT4UNCG0ba6WZRT#k31>cKO9m978UfnXnLbQ(kW+j_leVc#y#@k~0q%t9YnrH~ z+EX}_)HsEU0BsgDG_1M0^V?7ET)(~R-QD>mh`T%9osX~IxV?L;VX4b!HvC6B=w!=Y zhbNwl2cpG2{us^`cAI4jlih7!CJi%xIbA}$PSy1Dv+CJCKgF=Zea);v>rSq`gcD~g zrXF3vaw_W4Ux4UnLzs@Cg-61moa#5U-@WmvF)R)3leyG>s^|5nIjyIiR*QS|>qs*3~74LV&Dy<-&m#>|ciy#B|a}-IE zvrNM>zO7pZ#NxmwTA$Ic@vLstj5)(G$YY*deTldxAu&eEl;r4HW65oyF(I)PNX#5s zCzaGHBz6x9r$fJ#oG#>Yy00CU9*ub|et=B?y&vP}jB}0B^PDMrle~D21LWE7p-2j3 z>ZSoVP{Z4eXO405=J$9j!C%y{cFGZwT9SIUP~?ZG{xDKbM0kcm%{(b64{&L)r5kqHtiD?md25RggPsOq%@L%kX6Qm&uOS) zqEELa2W5QbnB)@Y3|mD=#%IBj<7o70N%3M?@#1Xe)qEDX1RcgIJGGRKzIJ#%DX01q+%G zmXT!x6OEJCU&6HMdyMR^im!6-(>NzC(z<b&0CWRFNA|kpdo=2;ricmF^DuAAFZXeY(TtUO9jasiRcoiY@pJY(3<*_0%~ z=)&nT{m*20m2W%Mhh_p*nHn)EhCFHfN@ZsI zUp9Jf{+1(O1av&X1BsM8hzB8~mHv?*D1CAR6@y)0a`OjC%w@~(b2bgjeD_>-qmx=O8)vzN1YQou|0_fYa+1nWh#R1qGo-fo z9~H38g+n!c)w572I0%IxI0k6o4bXxa*D@8k}F z?2lNZimXr`h?ev0TjmI+WVu<+ZKqV`QW?l>_U`>pcD(HmcD6r49&u*an+`;${vW1- zq|CYzv=Z?%+WiVu^lumC88VB}$QIgJ>DLhwH$fgp;Zt3F;>~04lsVV(T>KXRX8s=s jN2?j+eepN?wbk#sxiG&lyS{5w>TpRE;2CgB7nlDFuGUNq literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/testing.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/testing.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b2b86e0f7a17db32b0cc66ef42e2c84d8864ed58 GIT binary patch literal 9390 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 Rboy1+)#Z1kKVJXs{{@x!`C9-0 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/typing.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/typing.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a547cd1fd14d9ae2f8e8295955d1eac66d378fb9 GIT binary patch literal 1428 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/__pycache__/wrappers.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/flask/__pycache__/wrappers.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5b6ca9bb80950f69f19d46bb4894c2009d3c5fe GIT binary patch literal 5045 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/flask/app.py b/myvenv/lib/python3.10/site-packages/flask/app.py new file mode 100644 index 0000000..23b99e2 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/flask/app.py @@ -0,0 +1,2091 @@ +import functools +import inspect +import logging +import os +import sys +import typing as t +import weakref +from datetime import timedelta +from itertools import chain +from threading import Lock +from types import TracebackType + +from werkzeug.datastructures import Headers +from werkzeug.datastructures import ImmutableDict +from werkzeug.exceptions import BadRequest +from werkzeug.exceptions import BadRequestKeyError +from werkzeug.exceptions import HTTPException +from werkzeug.exceptions import InternalServerError +from werkzeug.local import ContextVar +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.wrappers import Response as BaseResponse + +from . import cli +from . import json +from .config import Config +from .config import ConfigAttribute +from .ctx import _AppCtxGlobals +from .ctx import AppContext +from .ctx import RequestContext +from .globals import _request_ctx_stack +from .globals import g +from .globals import request +from .globals import session +from .helpers import _split_blueprint_path +from .helpers import get_debug_flag +from .helpers import get_env +from .helpers import get_flashed_messages +from .helpers import get_load_dotenv +from .helpers import locked_cached_property +from .helpers import url_for +from .json import jsonify +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 .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 .typing import BeforeFirstRequestCallable +from .typing import ResponseReturnValue +from .typing import TeardownCallable +from .typing import TemplateFilterCallable +from .typing import TemplateGlobalCallable +from .typing import TemplateTestCallable +from .wrappers import Request +from .wrappers import Response + +if t.TYPE_CHECKING: + import typing_extensions as te + from .blueprints import Blueprint + from .testing import FlaskClient + from .testing import FlaskCliRunner + from .typing import ErrorHandlerCallable + +if sys.version_info >= (3, 8): + iscoroutinefunction = inspect.iscoroutinefunction +else: + + def iscoroutinefunction(func: t.Any) -> bool: + while inspect.ismethod(func): + func = func.__func__ + + while isinstance(func, functools.partial): + func = func.func + + return inspect.iscoroutinefunction(func) + + +def _make_timedelta(value: t.Optional[timedelta]) -> t.Optional[timedelta]: + if value is None or isinstance(value, timedelta): + return value + + return timedelta(seconds=value) + + +class Flask(Scaffold): + """The flask object implements a WSGI application and acts as the central + object. It is passed the name of the module or package of the + application. Once it is created it will act as a central registry for + the view functions, the URL rules, template configuration and much more. + + The name of the package is used to resolve resources from inside the + package or the folder the module is contained in depending on if the + package parameter resolves to an actual python package (a folder with + an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file). + + For more information about resource loading, see :func:`open_resource`. + + Usually you create a :class:`Flask` instance in your main module or + in the :file:`__init__.py` file of your package like this:: + + from flask import Flask + app = Flask(__name__) + + .. admonition:: About the First Parameter + + The idea of the first parameter is to give Flask an idea of what + belongs to your application. This name is used to find resources + on the filesystem, can be used by extensions to improve debugging + information and a lot more. + + So it's important what you provide there. If you are using a single + module, `__name__` is always the correct value. If you however are + using a package, it's usually recommended to hardcode the name of + your package there. + + For example if your application is defined in :file:`yourapplication/app.py` + you should create it with one of the two versions below:: + + app = Flask('yourapplication') + app = Flask(__name__.split('.')[0]) + + Why is that? The application will work even with `__name__`, thanks + to how resources are looked up. However it will make debugging more + painful. Certain extensions can make assumptions based on the + import name of your application. For example the Flask-SQLAlchemy + extension will look for the code in your application that triggered + an SQL query in debug mode. If the import name is not properly set + up, that debugging information is lost. (For example it would only + pick up SQL queries in `yourapplication.app` and not + `yourapplication.views.frontend`) + + .. versionadded:: 0.7 + The `static_url_path`, `static_folder`, and `template_folder` + parameters were added. + + .. versionadded:: 0.8 + The `instance_path` and `instance_relative_config` parameters were + added. + + .. versionadded:: 0.11 + The `root_path` parameter was added. + + .. versionadded:: 1.0 + The ``host_matching`` and ``static_host`` parameters were added. + + .. versionadded:: 1.0 + The ``subdomain_matching`` parameter was added. Subdomain + matching needs to be enabled manually now. Setting + :data:`SERVER_NAME` does not implicitly enable it. + + :param import_name: the name of the application package + :param static_url_path: can be used to specify a different path for the + static files on the web. Defaults to the name + of the `static_folder` folder. + :param static_folder: The folder with static files that is served at + ``static_url_path``. Relative to the application ``root_path`` + or an absolute path. Defaults to ``'static'``. + :param static_host: the host to use when adding the static route. + Defaults to None. Required when using ``host_matching=True`` + with a ``static_folder`` configured. + :param host_matching: set ``url_map.host_matching`` attribute. + Defaults to False. + :param subdomain_matching: consider the subdomain relative to + :data:`SERVER_NAME` when matching routes. Defaults to False. + :param template_folder: the folder that contains the templates that should + be used by the application. Defaults to + ``'templates'`` folder in the root path of the + application. + :param instance_path: An alternative instance path for the application. + By default the folder ``'instance'`` next to the + package or module is assumed to be the instance + path. + :param instance_relative_config: if set to ``True`` relative filenames + for loading the config are assumed to + be relative to the instance path instead + of the application root. + :param root_path: The path to the root of the application files. + This should only be set manually when it can't be detected + automatically, such as for namespace packages. + """ + + #: The class that is used for request objects. See :class:`~flask.Request` + #: for more information. + request_class = Request + + #: The class that is used for response objects. See + #: :class:`~flask.Response` for more information. + response_class = Response + + #: The class that is used for the Jinja environment. + #: + #: .. versionadded:: 0.11 + jinja_environment = Environment + + #: The class that is used for the :data:`~flask.g` instance. + #: + #: Example use cases for a custom class: + #: + #: 1. Store arbitrary attributes on flask.g. + #: 2. Add a property for lazy per-request database connectors. + #: 3. Return None instead of AttributeError on unexpected attributes. + #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g. + #: + #: In Flask 0.9 this property was called `request_globals_class` but it + #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the + #: flask.g object is now application context scoped. + #: + #: .. versionadded:: 0.10 + app_ctx_globals_class = _AppCtxGlobals + + #: The class that is used for the ``config`` attribute of this app. + #: Defaults to :class:`~flask.Config`. + #: + #: Example use cases for a custom class: + #: + #: 1. Default values for certain config options. + #: 2. Access to config values through attributes in addition to keys. + #: + #: .. versionadded:: 0.11 + config_class = Config + + #: The testing flag. Set this to ``True`` to enable the test mode of + #: Flask extensions (and in the future probably also Flask itself). + #: For example this might activate test helpers that have an + #: additional runtime cost which should not be enabled by default. + #: + #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the + #: default it's implicitly enabled. + #: + #: This attribute can also be configured from the config with the + #: ``TESTING`` configuration key. Defaults to ``False``. + testing = ConfigAttribute("TESTING") + + #: If a secret key is set, cryptographic components can use this to + #: sign cookies and other things. Set this to a complex random value + #: when you want to use the secure cookie for instance. + #: + #: This attribute can also be configured from the config with the + #: :data:`SECRET_KEY` configuration key. Defaults to ``None``. + secret_key = ConfigAttribute("SECRET_KEY") + + #: The secure cookie uses this for the name of the session cookie. + #: + #: This attribute can also be configured from the config with the + #: ``SESSION_COOKIE_NAME`` configuration key. Defaults to ``'session'`` + session_cookie_name = ConfigAttribute("SESSION_COOKIE_NAME") + + #: A :class:`~datetime.timedelta` which is used to set the expiration + #: date of a permanent session. The default is 31 days which makes a + #: permanent session survive for roughly one month. + #: + #: This attribute can also be configured from the config with the + #: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to + #: ``timedelta(days=31)`` + permanent_session_lifetime = ConfigAttribute( + "PERMANENT_SESSION_LIFETIME", get_converter=_make_timedelta + ) + + #: A :class:`~datetime.timedelta` or number of seconds which is used + #: as the default ``max_age`` for :func:`send_file`. The default is + #: ``None``, which tells the browser to use conditional requests + #: instead of a timed cache. + #: + #: Configured with the :data:`SEND_FILE_MAX_AGE_DEFAULT` + #: configuration key. + #: + #: .. versionchanged:: 2.0 + #: Defaults to ``None`` instead of 12 hours. + send_file_max_age_default = ConfigAttribute( + "SEND_FILE_MAX_AGE_DEFAULT", get_converter=_make_timedelta + ) + + #: Enable this if you want to use the X-Sendfile feature. Keep in + #: mind that the server has to support this. This only affects files + #: sent with the :func:`send_file` method. + #: + #: .. versionadded:: 0.2 + #: + #: This attribute can also be configured from the config with the + #: ``USE_X_SENDFILE`` configuration key. Defaults to ``False``. + use_x_sendfile = ConfigAttribute("USE_X_SENDFILE") + + #: The JSON encoder class to use. Defaults to :class:`~flask.json.JSONEncoder`. + #: + #: .. versionadded:: 0.10 + json_encoder = json.JSONEncoder + + #: The JSON decoder class to use. Defaults to :class:`~flask.json.JSONDecoder`. + #: + #: .. versionadded:: 0.10 + json_decoder = json.JSONDecoder + + #: Options that are passed to the Jinja environment in + #: :meth:`create_jinja_environment`. Changing these options after + #: the environment is created (accessing :attr:`jinja_env`) will + #: have no effect. + #: + #: .. versionchanged:: 1.1.0 + #: This is a ``dict`` instead of an ``ImmutableDict`` to allow + #: easier configuration. + #: + jinja_options: dict = {} + + #: Default configuration parameters. + default_config = ImmutableDict( + { + "ENV": None, + "DEBUG": None, + "TESTING": False, + "PROPAGATE_EXCEPTIONS": None, + "PRESERVE_CONTEXT_ON_EXCEPTION": None, + "SECRET_KEY": None, + "PERMANENT_SESSION_LIFETIME": timedelta(days=31), + "USE_X_SENDFILE": False, + "SERVER_NAME": None, + "APPLICATION_ROOT": "/", + "SESSION_COOKIE_NAME": "session", + "SESSION_COOKIE_DOMAIN": None, + "SESSION_COOKIE_PATH": None, + "SESSION_COOKIE_HTTPONLY": True, + "SESSION_COOKIE_SECURE": False, + "SESSION_COOKIE_SAMESITE": None, + "SESSION_REFRESH_EACH_REQUEST": True, + "MAX_CONTENT_LENGTH": None, + "SEND_FILE_MAX_AGE_DEFAULT": None, + "TRAP_BAD_REQUEST_ERRORS": None, + "TRAP_HTTP_EXCEPTIONS": False, + "EXPLAIN_TEMPLATE_LOADING": False, + "PREFERRED_URL_SCHEME": "http", + "JSON_AS_ASCII": True, + "JSON_SORT_KEYS": True, + "JSONIFY_PRETTYPRINT_REGULAR": False, + "JSONIFY_MIMETYPE": "application/json", + "TEMPLATES_AUTO_RELOAD": None, + "MAX_COOKIE_SIZE": 4093, + } + ) + + #: The rule object to use for URL rules created. This is used by + #: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`. + #: + #: .. versionadded:: 0.7 + url_rule_class = Rule + + #: The map object to use for storing the URL rules and routing + #: configuration parameters. Defaults to :class:`werkzeug.routing.Map`. + #: + #: .. versionadded:: 1.1.0 + url_map_class = Map + + #: The :meth:`test_client` method creates an instance of this test + #: client class. Defaults to :class:`~flask.testing.FlaskClient`. + #: + #: .. versionadded:: 0.7 + test_client_class: t.Optional[t.Type["FlaskClient"]] = None + + #: The :class:`~click.testing.CliRunner` subclass, by default + #: :class:`~flask.testing.FlaskCliRunner` that is used by + #: :meth:`test_cli_runner`. Its ``__init__`` method should take a + #: Flask app object as the first argument. + #: + #: .. versionadded:: 1.0 + test_cli_runner_class: t.Optional[t.Type["FlaskCliRunner"]] = None + + #: the session interface to use. By default an instance of + #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here. + #: + #: .. versionadded:: 0.8 + session_interface = SecureCookieSessionInterface() + + def __init__( + self, + import_name: str, + static_url_path: t.Optional[str] = None, + static_folder: t.Optional[t.Union[str, os.PathLike]] = "static", + static_host: t.Optional[str] = None, + host_matching: bool = False, + subdomain_matching: bool = False, + template_folder: t.Optional[str] = "templates", + instance_path: t.Optional[str] = None, + instance_relative_config: bool = False, + root_path: t.Optional[str] = None, + ): + super().__init__( + import_name=import_name, + static_folder=static_folder, + static_url_path=static_url_path, + template_folder=template_folder, + root_path=root_path, + ) + + if instance_path is None: + instance_path = self.auto_find_instance_path() + elif not os.path.isabs(instance_path): + raise ValueError( + "If an instance path is provided it must be absolute." + " A relative path was given instead." + ) + + #: Holds the path to the instance folder. + #: + #: .. versionadded:: 0.8 + self.instance_path = instance_path + + #: The configuration dictionary as :class:`Config`. This behaves + #: exactly like a regular dictionary but supports additional methods + #: to load a config from files. + self.config = self.make_config(instance_relative_config) + + #: A list of functions that are called when :meth:`url_for` raises a + #: :exc:`~werkzeug.routing.BuildError`. Each function registered here + #: is called with `error`, `endpoint` and `values`. If a function + #: returns ``None`` or raises a :exc:`BuildError` the next function is + #: tried. + #: + #: .. versionadded:: 0.9 + self.url_build_error_handlers: t.List[ + t.Callable[[Exception, str, dict], str] + ] = [] + + #: A list of functions that will be called at the beginning of the + #: first request to this instance. To register a function, use the + #: :meth:`before_first_request` decorator. + #: + #: .. versionadded:: 0.8 + self.before_first_request_funcs: t.List[BeforeFirstRequestCallable] = [] + + #: A list of functions that are called when the application context + #: is destroyed. Since the application context is also torn down + #: if the request ends this is the place to store code that disconnects + #: from databases. + #: + #: .. versionadded:: 0.9 + self.teardown_appcontext_funcs: t.List[TeardownCallable] = [] + + #: A list of shell context processor functions that should be run + #: when a shell context is created. + #: + #: .. versionadded:: 0.11 + self.shell_context_processors: t.List[t.Callable[[], t.Dict[str, t.Any]]] = [] + + #: Maps registered blueprint names to blueprint objects. The + #: dict retains the order the blueprints were registered in. + #: Blueprints can be registered multiple times, this dict does + #: not track how often they were attached. + #: + #: .. versionadded:: 0.7 + self.blueprints: t.Dict[str, "Blueprint"] = {} + + #: a place where extensions can store application specific state. For + #: example this is where an extension could store database engines and + #: similar things. + #: + #: The key must match the name of the extension module. For example in + #: case of a "Flask-Foo" extension in `flask_foo`, the key would be + #: ``'foo'``. + #: + #: .. versionadded:: 0.7 + self.extensions: dict = {} + + #: The :class:`~werkzeug.routing.Map` for this instance. You can use + #: this to change the routing converters after the class was created + #: but before any routes are connected. Example:: + #: + #: from werkzeug.routing import BaseConverter + #: + #: class ListConverter(BaseConverter): + #: def to_python(self, value): + #: return value.split(',') + #: def to_url(self, values): + #: return ','.join(super(ListConverter, self).to_url(value) + #: for value in values) + #: + #: app = Flask(__name__) + #: app.url_map.converters['list'] = ListConverter + self.url_map = self.url_map_class() + + self.url_map.host_matching = host_matching + self.subdomain_matching = subdomain_matching + + # tracks internally if the application already handled at least one + # request. + self._got_first_request = False + self._before_request_lock = Lock() + + # Add a static route using the provided static_url_path, static_host, + # and static_folder if there is a configured static_folder. + # Note we do this without checking if static_folder exists. + # For one, it might be created while the server is running (e.g. during + # development). Also, Google App Engine stores static files somewhere + if self.has_static_folder: + assert ( + bool(static_host) == host_matching + ), "Invalid static_host/host_matching combination" + # Use a weakref to avoid creating a reference cycle between the app + # and the view function (see #3761). + self_ref = weakref.ref(self) + self.add_url_rule( + f"{self.static_url_path}/", + endpoint="static", + host=static_host, + view_func=lambda **kw: self_ref().send_static_file(**kw), # type: ignore # noqa: B950 + ) + + # Set the name of the Click group in case someone wants to add + # the app's commands to another CLI tool. + self.cli.name = self.name + + def _is_setup_finished(self) -> bool: + return self.debug and self._got_first_request + + @locked_cached_property + def name(self) -> str: # type: ignore + """The name of the application. This is usually the import name + with the difference that it's guessed from the run file if the + import name is main. This name is used as a display name when + Flask needs the name of the application. It can be set and overridden + to change the value. + + .. versionadded:: 0.8 + """ + if self.import_name == "__main__": + fn = getattr(sys.modules["__main__"], "__file__", None) + if fn is None: + return "__main__" + return os.path.splitext(os.path.basename(fn))[0] + return self.import_name + + @property + def propagate_exceptions(self) -> bool: + """Returns the value of the ``PROPAGATE_EXCEPTIONS`` configuration + value in case it's set, otherwise a sensible default is returned. + + .. versionadded:: 0.7 + """ + rv = self.config["PROPAGATE_EXCEPTIONS"] + if rv is not None: + return rv + return self.testing or self.debug + + @property + def preserve_context_on_exception(self) -> bool: + """Returns the value of the ``PRESERVE_CONTEXT_ON_EXCEPTION`` + configuration value in case it's set, otherwise a sensible default + is returned. + + .. versionadded:: 0.7 + """ + rv = self.config["PRESERVE_CONTEXT_ON_EXCEPTION"] + if rv is not None: + return rv + return self.debug + + @locked_cached_property + def logger(self) -> logging.Logger: + """A standard Python :class:`~logging.Logger` for the app, with + the same name as :attr:`name`. + + In debug mode, the logger's :attr:`~logging.Logger.level` will + be set to :data:`~logging.DEBUG`. + + If there are no handlers configured, a default handler will be + added. See :doc:`/logging` for more information. + + .. versionchanged:: 1.1.0 + The logger takes the same name as :attr:`name` rather than + hard-coding ``"flask.app"``. + + .. versionchanged:: 1.0.0 + Behavior was simplified. The logger is always named + ``"flask.app"``. The level is only set during configuration, + it doesn't check ``app.debug`` each time. Only one format is + used, not different ones depending on ``app.debug``. No + handlers are removed, and a handler is only added if no + handlers are already configured. + + .. versionadded:: 0.3 + """ + return create_logger(self) + + @locked_cached_property + def jinja_env(self) -> Environment: + """The Jinja environment used to load templates. + + The environment is created the first time this property is + accessed. Changing :attr:`jinja_options` after that will have no + effect. + """ + return self.create_jinja_environment() + + @property + def got_first_request(self) -> bool: + """This attribute is set to ``True`` if the application started + handling the first request. + + .. versionadded:: 0.8 + """ + return self._got_first_request + + def make_config(self, instance_relative: bool = False) -> Config: + """Used to create the config attribute by the Flask constructor. + The `instance_relative` parameter is passed in from the constructor + of Flask (there named `instance_relative_config`) and indicates if + the config should be relative to the instance path or the root path + of the application. + + .. versionadded:: 0.8 + """ + root_path = self.root_path + if instance_relative: + root_path = self.instance_path + defaults = dict(self.default_config) + defaults["ENV"] = get_env() + defaults["DEBUG"] = get_debug_flag() + return self.config_class(root_path, defaults) + + def auto_find_instance_path(self) -> str: + """Tries to locate the instance path if it was not provided to the + constructor of the application class. It will basically calculate + the path to a folder named ``instance`` next to your main file or + the package. + + .. versionadded:: 0.8 + """ + prefix, package_path = find_package(self.import_name) + if prefix is None: + return os.path.join(package_path, "instance") + return os.path.join(prefix, "var", f"{self.name}-instance") + + def open_instance_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: + """Opens a resource from the application's instance folder + (:attr:`instance_path`). Otherwise works like + :meth:`open_resource`. Instance resources can also be opened for + writing. + + :param resource: the name of the resource. To access resources within + subfolders use forward slashes as separator. + :param mode: resource file opening mode, default is 'rb'. + """ + return open(os.path.join(self.instance_path, resource), mode) + + @property + def templates_auto_reload(self) -> bool: + """Reload templates when they are changed. Used by + :meth:`create_jinja_environment`. + + This attribute can be configured with :data:`TEMPLATES_AUTO_RELOAD`. If + not set, it will be enabled in debug mode. + + .. versionadded:: 1.0 + This property was added but the underlying config and behavior + already existed. + """ + rv = self.config["TEMPLATES_AUTO_RELOAD"] + return rv if rv is not None else self.debug + + @templates_auto_reload.setter + def templates_auto_reload(self, value: bool) -> None: + self.config["TEMPLATES_AUTO_RELOAD"] = value + + def create_jinja_environment(self) -> Environment: + """Create the Jinja environment based on :attr:`jinja_options` + and the various Jinja-related methods of the app. Changing + :attr:`jinja_options` after this will have no effect. Also adds + Flask-related globals and filters to the environment. + + .. versionchanged:: 0.11 + ``Environment.auto_reload`` set in accordance with + ``TEMPLATES_AUTO_RELOAD`` configuration option. + + .. versionadded:: 0.5 + """ + options = dict(self.jinja_options) + + if "autoescape" not in options: + options["autoescape"] = self.select_jinja_autoescape + + if "auto_reload" not in options: + options["auto_reload"] = self.templates_auto_reload + + rv = self.jinja_environment(self, **options) + rv.globals.update( + url_for=url_for, + get_flashed_messages=get_flashed_messages, + config=self.config, + # request, session and g are normally added with the + # context processor for efficiency reasons but for imported + # templates we also want the proxies in there. + request=request, + session=session, + g=g, + ) + rv.policies["json.dumps_function"] = json.dumps + return rv + + def create_global_jinja_loader(self) -> DispatchingJinjaLoader: + """Creates the loader for the Jinja2 environment. Can be used to + override just the loader and keeping the rest unchanged. It's + discouraged to override this function. Instead one should override + the :meth:`jinja_loader` function instead. + + The global loader dispatches between the loaders of the application + and the individual blueprints. + + .. versionadded:: 0.7 + """ + return DispatchingJinjaLoader(self) + + def select_jinja_autoescape(self, filename: str) -> bool: + """Returns ``True`` if autoescaping should be active for the given + template name. If no template name is given, returns `True`. + + .. versionadded:: 0.5 + """ + if filename is None: + return True + return filename.endswith((".html", ".htm", ".xml", ".xhtml")) + + def update_template_context(self, context: dict) -> None: + """Update the template context with some commonly used variables. + This injects request, session, config and g into the template + context as well as everything template context processors want + to inject. Note that the as of Flask 0.6, the original values + in the context will not be overridden if a context processor + decides to return a value with the same key. + + :param context: the context as a dictionary that is updated in place + to add extra variables. + """ + names: t.Iterable[t.Optional[str]] = (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 + + #: What environment the app is running in. Flask and extensions may + #: enable behaviors based on the environment, such as enabling debug + #: mode. This maps to the :data:`ENV` config key. This is set by the + #: :envvar:`FLASK_ENV` environment variable and may not behave as + #: expected if set in code. + #: + #: **Do not enable development when deploying in production.** + #: + #: Default: ``'production'`` + env = ConfigAttribute("ENV") + + @property + def debug(self) -> bool: + """Whether debug mode is enabled. When using ``flask run`` to start + the development server, an interactive debugger will be shown for + unhandled exceptions, and the server will be reloaded when code + changes. This maps to the :data:`DEBUG` config key. This is + enabled when :attr:`env` is ``'development'`` and is overridden + by the ``FLASK_DEBUG`` environment variable. It may not behave as + expected if set in code. + + **Do not enable debug mode when deploying in production.** + + Default: ``True`` if :attr:`env` is ``'development'``, or + ``False`` otherwise. + """ + return self.config["DEBUG"] + + @debug.setter + def debug(self, value: bool) -> None: + self.config["DEBUG"] = value + self.jinja_env.auto_reload = self.templates_auto_reload + + def run( + self, + host: t.Optional[str] = None, + port: t.Optional[int] = None, + debug: t.Optional[bool] = None, + load_dotenv: bool = True, + **options: t.Any, + ) -> None: + """Runs the application on a local development server. + + Do not use ``run()`` in a production setting. It is not intended to + meet security and performance requirements for a production server. + Instead, see :doc:`/deploying/index` for WSGI server recommendations. + + If the :attr:`debug` flag is set the server will automatically reload + for code changes and show a debugger in case an exception happened. + + If you want to run the application in debug mode, but disable the + code execution on the interactive debugger, you can pass + ``use_evalex=False`` as parameter. This will keep the debugger's + traceback screen active, but disable code execution. + + It is not recommended to use this function for development with + automatic reloading as this is badly supported. Instead you should + be using the :command:`flask` command line script's ``run`` support. + + .. admonition:: Keep in Mind + + Flask will suppress any server error with a generic error page + unless it is in debug mode. As such to enable just the + interactive debugger without the code reloading, you have to + invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``. + Setting ``use_debugger`` to ``True`` without being in debug mode + won't catch any exceptions because there won't be any to + catch. + + :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to + have the server available externally as well. Defaults to + ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable + if present. + :param port: the port of the webserver. Defaults to ``5000`` or the + port defined in the ``SERVER_NAME`` config variable if present. + :param debug: if given, enable or disable debug mode. See + :attr:`debug`. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param options: the options to be forwarded to the underlying Werkzeug + server. See :func:`werkzeug.serving.run_simple` for more + information. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment + variables from :file:`.env` and :file:`.flaskenv` files. + + If set, the :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG` + environment variables will override :attr:`env` and + :attr:`debug`. + + Threaded mode is enabled by default. + + .. versionchanged:: 0.10 + The default port is now picked from the ``SERVER_NAME`` + variable. + """ + # Change this into a no-op if the server is invoked from the + # command line. Have a look at cli.py for more information. + if os.environ.get("FLASK_RUN_FROM_CLI") == "true": + from .debughelpers import explain_ignored_app_run + + explain_ignored_app_run() + return + + if get_load_dotenv(load_dotenv): + cli.load_dotenv() + + # if set, let env vars override previous values + if "FLASK_ENV" in os.environ: + self.env = get_env() + self.debug = get_debug_flag() + elif "FLASK_DEBUG" in os.environ: + self.debug = get_debug_flag() + + # debug passed to method overrides all other sources + if debug is not None: + self.debug = bool(debug) + + server_name = self.config.get("SERVER_NAME") + sn_host = sn_port = None + + if server_name: + sn_host, _, sn_port = server_name.partition(":") + + if not host: + if sn_host: + host = sn_host + else: + host = "127.0.0.1" + + if port or port == 0: + port = int(port) + elif sn_port: + port = int(sn_port) + else: + port = 5000 + + options.setdefault("use_reloader", self.debug) + options.setdefault("use_debugger", self.debug) + options.setdefault("threaded", True) + + cli.show_server_banner(self.env, self.debug, self.name, False) + + from werkzeug.serving import run_simple + + try: + run_simple(t.cast(str, host), port, self, **options) + finally: + # reset the first request information if the development server + # reset normally. This makes it possible to restart the server + # without reloader and that stuff from an interactive shell. + self._got_first_request = False + + def test_client(self, use_cookies: bool = True, **kwargs: t.Any) -> "FlaskClient": + """Creates a test client for this application. For information + about unit testing head over to :doc:`/testing`. + + Note that if you are testing for assertions or exceptions in your + application code, you must set ``app.testing = True`` in order for the + exceptions to propagate to the test client. Otherwise, the exception + will be handled by the application (not visible to the test client) and + the only indication of an AssertionError or other exception will be a + 500 status code response to the test client. See the :attr:`testing` + attribute. For example:: + + app.testing = True + client = app.test_client() + + The test client can be used in a ``with`` block to defer the closing down + of the context until the end of the ``with`` block. This is useful if + you want to access the context locals for testing:: + + with app.test_client() as c: + rv = c.get('/?vodka=42') + assert request.args['vodka'] == '42' + + Additionally, you may pass optional keyword arguments that will then + be passed to the application's :attr:`test_client_class` constructor. + For example:: + + from flask.testing import FlaskClient + + class CustomClient(FlaskClient): + def __init__(self, *args, **kwargs): + self._authentication = kwargs.pop("authentication") + super(CustomClient,self).__init__( *args, **kwargs) + + app.test_client_class = CustomClient + client = app.test_client(authentication='Basic ....') + + See :class:`~flask.testing.FlaskClient` for more information. + + .. versionchanged:: 0.4 + added support for ``with`` block usage for the client. + + .. versionadded:: 0.7 + The `use_cookies` parameter was added as well as the ability + to override the client to be used by setting the + :attr:`test_client_class` attribute. + + .. versionchanged:: 0.11 + Added `**kwargs` to support passing additional keyword arguments to + the constructor of :attr:`test_client_class`. + """ + cls = self.test_client_class + if cls is None: + from .testing import FlaskClient as cls # type: ignore + return cls( # type: ignore + self, self.response_class, use_cookies=use_cookies, **kwargs + ) + + def test_cli_runner(self, **kwargs: t.Any) -> "FlaskCliRunner": + """Create a CLI runner for testing CLI commands. + See :ref:`testing-cli`. + + Returns an instance of :attr:`test_cli_runner_class`, by default + :class:`~flask.testing.FlaskCliRunner`. The Flask app object is + passed as the first argument. + + .. versionadded:: 1.0 + """ + cls = self.test_cli_runner_class + + if cls is None: + from .testing import FlaskCliRunner as cls # type: ignore + + return cls(self, **kwargs) # type: ignore + + @setupmethod + def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None: + """Register a :class:`~flask.Blueprint` on the application. Keyword + arguments passed to this method will override the defaults set on the + blueprint. + + Calls the blueprint's :meth:`~flask.Blueprint.register` method after + recording the blueprint in the application's :attr:`blueprints`. + + :param blueprint: The blueprint to register. + :param url_prefix: Blueprint routes will be prefixed with this. + :param subdomain: Blueprint routes will match on this subdomain. + :param url_defaults: Blueprint routes will use these default values for + view arguments. + :param options: Additional keyword arguments are passed to + :class:`~flask.blueprints.BlueprintSetupState`. They can be + accessed in :meth:`~flask.Blueprint.record` callbacks. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionadded:: 0.7 + """ + blueprint.register(self, options) + + def iter_blueprints(self) -> t.ValuesView["Blueprint"]: + """Iterates over all blueprints by the order they were registered. + + .. versionadded:: 0.11 + """ + return self.blueprints.values() + + @setupmethod + def add_url_rule( + self, + rule: str, + endpoint: t.Optional[str] = None, + view_func: t.Optional[t.Callable] = None, + provide_automatic_options: t.Optional[bool] = None, + **options: t.Any, + ) -> None: + if endpoint is None: + endpoint = _endpoint_from_view_func(view_func) # type: ignore + options["endpoint"] = endpoint + methods = options.pop("methods", None) + + # if the methods are not given and the view_func object knows its + # methods we can use that instead. If neither exists, we go with + # a tuple of only ``GET`` as default. + if methods is None: + methods = getattr(view_func, "methods", None) or ("GET",) + if isinstance(methods, str): + raise TypeError( + "Allowed methods must be a list of strings, for" + ' example: @app.route(..., methods=["POST"])' + ) + methods = {item.upper() for item in methods} + + # Methods that should always be added + required_methods = set(getattr(view_func, "required_methods", ())) + + # starting with Flask 0.8 the view_func object can disable and + # force-enable the automatic options handling. + if provide_automatic_options is None: + provide_automatic_options = getattr( + view_func, "provide_automatic_options", None + ) + + if provide_automatic_options is None: + if "OPTIONS" not in methods: + provide_automatic_options = True + required_methods.add("OPTIONS") + else: + provide_automatic_options = False + + # Add the required methods now. + methods |= required_methods + + rule = self.url_rule_class(rule, methods=methods, **options) + rule.provide_automatic_options = provide_automatic_options # type: ignore + + self.url_map.add(rule) + if view_func is not None: + old_func = self.view_functions.get(endpoint) + if old_func is not None and old_func != view_func: + raise AssertionError( + "View function mapping is overwriting an existing" + f" endpoint function: {endpoint}" + ) + self.view_functions[endpoint] = view_func + + @setupmethod + def template_filter( + self, name: t.Optional[str] = None + ) -> t.Callable[[TemplateFilterCallable], TemplateFilterCallable]: + """A decorator that is used to register custom template filter. + You can specify a name for the filter, otherwise the function + name will be used. Example:: + + @app.template_filter() + def reverse(s): + return s[::-1] + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def decorator(f: TemplateFilterCallable) -> TemplateFilterCallable: + self.add_template_filter(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_filter( + self, f: TemplateFilterCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template filter. Works exactly like the + :meth:`template_filter` decorator. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + self.jinja_env.filters[name or f.__name__] = f + + @setupmethod + def template_test( + self, name: t.Optional[str] = None + ) -> t.Callable[[TemplateTestCallable], TemplateTestCallable]: + """A decorator that is used to register custom template test. + You can specify a name for the test, otherwise the function + name will be used. Example:: + + @app.template_test() + def is_prime(n): + if n == 2: + return True + for i in range(2, int(math.ceil(math.sqrt(n))) + 1): + if n % i == 0: + return False + return True + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def decorator(f: TemplateTestCallable) -> TemplateTestCallable: + self.add_template_test(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_test( + self, f: TemplateTestCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template test. Works exactly like the + :meth:`template_test` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + self.jinja_env.tests[name or f.__name__] = f + + @setupmethod + def template_global( + self, name: t.Optional[str] = None + ) -> t.Callable[[TemplateGlobalCallable], TemplateGlobalCallable]: + """A decorator that is used to register a custom template global function. + You can specify a name for the global function, otherwise the function + name will be used. Example:: + + @app.template_global() + def double(n): + return 2 * n + + .. versionadded:: 0.10 + + :param name: the optional name of the global function, otherwise the + function name will be used. + """ + + def decorator(f: TemplateGlobalCallable) -> TemplateGlobalCallable: + self.add_template_global(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_global( + self, f: TemplateGlobalCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template global function. Works exactly like the + :meth:`template_global` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the global function, otherwise the + function name will be used. + """ + self.jinja_env.globals[name or f.__name__] = f + + @setupmethod + def before_first_request( + self, f: BeforeFirstRequestCallable + ) -> BeforeFirstRequestCallable: + """Registers a function to be run before the first request to this + instance of the application. + + The function will be called without any arguments and its return + value is ignored. + + .. versionadded:: 0.8 + """ + self.before_first_request_funcs.append(f) + return f + + @setupmethod + def teardown_appcontext(self, f: TeardownCallable) -> TeardownCallable: + """Registers a function to be called when the application context + ends. These functions are typically also called when the request + context is popped. + + Example:: + + ctx = app.app_context() + ctx.push() + ... + ctx.pop() + + When ``ctx.pop()`` is executed in the above example, the teardown + functions are called just before the app context moves from the + stack of active contexts. This becomes relevant if you are using + such constructs in tests. + + Since a request context typically also manages an application + context it would also be called when you pop a request context. + + When a teardown function was called because of an unhandled exception + it will be passed an error object. If an :meth:`errorhandler` is + registered, it will handle the exception and the teardown will not + receive it. + + The return values of teardown functions are ignored. + + .. versionadded:: 0.9 + """ + self.teardown_appcontext_funcs.append(f) + return f + + @setupmethod + def shell_context_processor(self, f: t.Callable) -> t.Callable: + """Registers a shell context processor function. + + .. versionadded:: 0.11 + """ + self.shell_context_processors.append(f) + return f + + def _find_error_handler( + self, e: Exception + ) -> t.Optional["ErrorHandlerCallable[Exception]"]: + """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 + ) -> t.Union[HTTPException, ResponseReturnValue]: + """Handles an HTTP exception. By default this will invoke the + registered error handlers and fall back to returning the + exception as response. + + .. versionchanged:: 1.0.3 + ``RoutingException``, used internally for actions such as + slash redirects during routing, is not passed to error + handlers. + + .. versionchanged:: 1.0 + Exceptions are looked up by code *and* by MRO, so + ``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 + ) -> t.Union[HTTPException, ResponseReturnValue]: + """This method is called whenever an exception occurs that + should be handled. A special case is :class:`~werkzeug + .exceptions.HTTPException` which is forwarded to the + :meth:`handle_http_exception` method. This function will either + return a response value or reraise the exception with the same + traceback. + + .. versionchanged:: 1.0 + Key errors raised from request data like ``form`` show the + bad key in debug mode rather than a generic bad request + message. + + .. versionadded:: 0.7 + """ + if isinstance(e, BadRequestKeyError) and ( + self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"] + ): + e.show_exception = True + + if isinstance(e, HTTPException) and not self.trap_http_exception(e): + return self.handle_http_exception(e) + + handler = self._find_error_handler(e) + + if handler is None: + raise + + return self.ensure_sync(handler)(e) + + def handle_exception(self, e: Exception) -> Response: + """Handle an exception that did not have an error handler + associated with it, or that was raised from an error handler. + This always causes a 500 ``InternalServerError``. + + Always sends the :data:`got_request_exception` signal. + + If :attr:`propagate_exceptions` is ``True``, such as in debug + mode, the error will be re-raised so that the debugger can + display it. Otherwise, the original exception is logged, and + an :exc:`~werkzeug.exceptions.InternalServerError` is returned. + + If an error handler is registered for ``InternalServerError`` or + ``500``, it will be used. For consistency, the handler will + always receive the ``InternalServerError``. The original + unhandled exception is available as ``e.original_exception``. + + .. versionchanged:: 1.1.0 + Always passes the ``InternalServerError`` instance to the + handler, setting ``original_exception`` to the unhandled + error. + + .. versionchanged:: 1.1.0 + ``after_request`` functions and other finalization is done + even for the default 500 response when there is no handler. + + .. versionadded:: 0.3 + """ + exc_info = sys.exc_info() + got_request_exception.send(self, exception=e) + + if self.propagate_exceptions: + # Re-raise if called with an active exception, otherwise + # raise the passed in exception. + if exc_info[1] is e: + raise + + raise e + + self.log_exception(exc_info) + server_error: t.Union[InternalServerError, ResponseReturnValue] + server_error = InternalServerError(original_exception=e) + handler = self._find_error_handler(server_error) + + if handler is not None: + server_error = self.ensure_sync(handler)(server_error) + + return self.finalize_request(server_error, from_error_handler=True) + + def log_exception( + self, + exc_info: t.Union[ + t.Tuple[type, BaseException, TracebackType], t.Tuple[None, None, None] + ], + ) -> None: + """Logs an exception. This is called by :meth:`handle_exception` + if debugging is disabled and right before the handler is called. + The default implementation logs the exception as error on the + :attr:`logger`. + + .. versionadded:: 0.8 + """ + self.logger.error( + f"Exception on {request.path} [{request.method}]", exc_info=exc_info + ) + + def raise_routing_exception(self, request: Request) -> "te.NoReturn": + """Exceptions that are recording during routing are reraised with + this method. During debug we are not reraising redirect requests + for non ``GET``, ``HEAD``, or ``OPTIONS`` requests and we're raising + a different error instead to help debug situations. + + :internal: + """ + if ( + not self.debug + or not isinstance(request.routing_exception, RequestRedirect) + or request.method in ("GET", "HEAD", "OPTIONS") + ): + raise request.routing_exception # type: ignore + + from .debughelpers import FormDataRoutingRedirect + + raise FormDataRoutingRedirect(request) + + def dispatch_request(self) -> ResponseReturnValue: + """Does the request dispatching. Matches the URL and returns the + return value of the view or error handler. This does not have to + be a response object. In order to convert the return value to a + proper response object, call :func:`make_response`. + + .. versionchanged:: 0.7 + This no longer does the exception handling, this code was + moved to the new :meth:`full_dispatch_request`. + """ + req = _request_ctx_stack.top.request + if req.routing_exception is not None: + self.raise_routing_exception(req) + rule = req.url_rule + # if we provide automatic options for this URL and the + # request came with the OPTIONS method, reply automatically + if ( + getattr(rule, "provide_automatic_options", False) + and req.method == "OPTIONS" + ): + return self.make_default_options_response() + # otherwise dispatch to the handler for that endpoint + return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args) + + def full_dispatch_request(self) -> Response: + """Dispatches the request and on top of that performs request + pre and postprocessing as well as HTTP exception catching and + error handling. + + .. versionadded:: 0.7 + """ + self.try_trigger_before_first_request_functions() + try: + request_started.send(self) + rv = self.preprocess_request() + if rv is None: + rv = self.dispatch_request() + except Exception as e: + rv = self.handle_user_exception(e) + return self.finalize_request(rv) + + def finalize_request( + self, + rv: t.Union[ResponseReturnValue, HTTPException], + from_error_handler: bool = False, + ) -> Response: + """Given the return value from a view function this finalizes + the request by converting it into a response and invoking the + postprocessing functions. This is invoked for both normal + request dispatching as well as error handlers. + + Because this means that it might be called as a result of a + failure a special safe mode is available which can be enabled + with the `from_error_handler` flag. If enabled, failures in + response processing will be logged and otherwise ignored. + + :internal: + """ + response = self.make_response(rv) + try: + response = self.process_response(response) + request_finished.send(self, response=response) + except Exception: + if not from_error_handler: + raise + self.logger.exception( + "Request finalizing failed with an error while handling an error" + ) + return response + + def try_trigger_before_first_request_functions(self) -> None: + """Called before each request and will ensure that it triggers + the :attr:`before_first_request_funcs` and only exactly once per + application instance (which means process usually). + + :internal: + """ + if self._got_first_request: + return + with self._before_request_lock: + if self._got_first_request: + return + for func in self.before_first_request_funcs: + self.ensure_sync(func)() + self._got_first_request = True + + def make_default_options_response(self) -> Response: + """This method is called to create the default ``OPTIONS`` response. + This can be changed through subclassing to change the default + behavior of ``OPTIONS`` responses. + + .. versionadded:: 0.7 + """ + adapter = _request_ctx_stack.top.url_adapter + methods = adapter.allowed_methods() + rv = self.response_class() + rv.allow.update(methods) + return rv + + def should_ignore_error(self, error: t.Optional[BaseException]) -> bool: + """This is called to figure out if an error should be ignored + or not as far as the teardown system is concerned. If this + function returns ``True`` then the teardown handlers will not be + passed the error. + + .. versionadded:: 0.10 + """ + return False + + def ensure_sync(self, func: t.Callable) -> t.Callable: + """Ensure that the function is synchronous for WSGI workers. + Plain ``def`` functions are returned as-is. ``async def`` + functions are wrapped to run and wait for the response. + + Override this method to change how the app runs async views. + + .. versionadded:: 2.0 + """ + if iscoroutinefunction(func): + return self.async_to_sync(func) + + return func + + def async_to_sync( + self, func: t.Callable[..., t.Coroutine] + ) -> t.Callable[..., t.Any]: + """Return a sync function that will run the coroutine function. + + .. code-block:: python + + result = app.async_to_sync(func)(*args, **kwargs) + + Override this method to change how the app converts async code + to be synchronously callable. + + .. versionadded:: 2.0 + """ + try: + from asgiref.sync import async_to_sync as asgiref_async_to_sync + except ImportError: + raise RuntimeError( + "Install Flask with the 'async' extra in order to use async views." + ) from None + + # Check that Werkzeug isn't using its fallback ContextVar class. + if ContextVar.__module__ == "werkzeug.local": + raise RuntimeError( + "Async cannot be used with this combination of Python " + "and Greenlet versions." + ) + + return asgiref_async_to_sync(func) + + def make_response(self, rv: ResponseReturnValue) -> Response: + """Convert the return value from a view function to an instance of + :attr:`response_class`. + + :param rv: the return value from the view function. The view function + must return a response. Returning ``None``, or the view ending + without returning, is not allowed. The following types are allowed + for ``view_rv``: + + ``str`` + A response object is created with the string encoded to UTF-8 + as the body. + + ``bytes`` + A response object is created with the bytes as the body. + + ``dict`` + A dictionary that will be jsonify'd before being returned. + + ``tuple`` + Either ``(body, status, headers)``, ``(body, status)``, or + ``(body, headers)``, where ``body`` is any of the other types + allowed here, ``status`` is a string or an integer, and + ``headers`` is a dictionary or a list of ``(key, value)`` + tuples. If ``body`` is a :attr:`response_class` instance, + ``status`` overwrites the exiting value and ``headers`` are + extended. + + :attr:`response_class` + The object is returned unchanged. + + other :class:`~werkzeug.wrappers.Response` class + The object is coerced to :attr:`response_class`. + + :func:`callable` + The function is called as a WSGI application. The result is + used to create a response object. + + .. versionchanged:: 0.9 + Previously a tuple was interpreted as the arguments for the + response object. + """ + + status = headers = None + + # unpack tuple returns + if isinstance(rv, tuple): + len_rv = len(rv) + + # a 3-tuple is unpacked directly + if len_rv == 3: + rv, status, headers = rv + # decide if a 2-tuple has status or headers + elif len_rv == 2: + if isinstance(rv[1], (Headers, dict, tuple, list)): + rv, headers = rv + else: + rv, status = rv + # other sized tuples are not allowed + else: + raise TypeError( + "The view function did not return a valid response tuple." + " The tuple must have the form (body, status, headers)," + " (body, status), or (body, headers)." + ) + + # the body must not be None + if rv is None: + raise TypeError( + f"The view function for {request.endpoint!r} did not" + " return a valid response. The function either returned" + " None or ended without a return statement." + ) + + # make sure the body is an instance of the response class + if not isinstance(rv, self.response_class): + if isinstance(rv, (str, bytes, bytearray)): + # let the response class set the status and headers instead of + # waiting to do it manually, so that the class can handle any + # special logic + rv = self.response_class(rv, status=status, headers=headers) + status = headers = None + elif isinstance(rv, dict): + rv = jsonify(rv) + elif isinstance(rv, BaseResponse) or callable(rv): + # evaluate a WSGI callable, or coerce a different response + # class to the correct type + try: + rv = self.response_class.force_type(rv, request.environ) # type: ignore # noqa: B950 + except TypeError as e: + raise TypeError( + f"{e}\nThe view function did not return a valid" + " response. The return type must be a string," + " dict, tuple, Response instance, or WSGI" + f" callable, but it was a {type(rv).__name__}." + ).with_traceback(sys.exc_info()[2]) from None + else: + raise TypeError( + "The view function did not return a valid" + " response. The return type must be a string," + " dict, tuple, Response instance, or WSGI" + f" callable, but it was a {type(rv).__name__}." + ) + + rv = t.cast(Response, rv) + # prefer the status if it was provided + if status is not None: + if isinstance(status, (str, bytes, bytearray)): + rv.status = status # type: ignore + else: + rv.status_code = status + + # extend existing headers with provided headers + if headers: + rv.headers.update(headers) + + return rv + + def create_url_adapter( + self, request: t.Optional[Request] + ) -> t.Optional[MapAdapter]: + """Creates a URL adapter for the given request. The URL adapter + is created at a point where the request context is not yet set + up so the request is passed explicitly. + + .. versionadded:: 0.6 + + .. versionchanged:: 0.9 + This can now also be called without a request object when the + URL adapter is created for the application context. + + .. versionchanged:: 1.0 + :data:`SERVER_NAME` no longer implicitly enables subdomain + matching. Use :attr:`subdomain_matching` instead. + """ + if request is not None: + # If subdomain matching is disabled (the default), use the + # default subdomain in all cases. This should be the default + # in Werkzeug but it currently does not have that feature. + if not self.subdomain_matching: + subdomain = self.url_map.default_subdomain or None + else: + subdomain = None + + return self.url_map.bind_to_environ( + request.environ, + server_name=self.config["SERVER_NAME"], + subdomain=subdomain, + ) + # We need at the very least the server name to be set for this + # to work. + if self.config["SERVER_NAME"] is not None: + return self.url_map.bind( + self.config["SERVER_NAME"], + script_name=self.config["APPLICATION_ROOT"], + url_scheme=self.config["PREFERRED_URL_SCHEME"], + ) + + return None + + def inject_url_defaults(self, endpoint: str, values: dict) -> None: + """Injects the URL defaults for the given endpoint directly into + the values dictionary passed. This is used internally and + automatically called on URL building. + + .. versionadded:: 0.7 + """ + names: t.Iterable[t.Optional[str]] = (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: Exception, endpoint: str, values: dict + ) -> str: + """Handle :class:`~werkzeug.routing.BuildError` on + :meth:`url_for`. + """ + for handler in self.url_build_error_handlers: + try: + rv = handler(error, endpoint, values) + except BuildError as e: + # make error available outside except block + error = e + else: + if rv is not None: + return rv + + # Re-raise if called with an active exception, otherwise raise + # the passed in exception. + if error is sys.exc_info()[1]: + raise + + raise error + + def preprocess_request(self) -> t.Optional[ResponseReturnValue]: + """Called before the request is dispatched. Calls + :attr:`url_value_preprocessors` registered with the app and the + current blueprint (if any). Then calls :attr:`before_request_funcs` + registered with the app and the blueprint. + + If any :meth:`before_request` handler returns a non-None value, the + value is handled as if it was the return value from the view, and + further request handling is stopped. + """ + 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_stack.top + + 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: t.Optional[BaseException] = _sentinel # type: ignore + ) -> None: + """Called after the request is dispatched and the response is + returned, right before the request context is popped. + + This calls all functions decorated with + :meth:`teardown_request`, and :meth:`Blueprint.teardown_request` + if a blueprint handled the request. Finally, the + :data:`request_tearing_down` signal is sent. + + This is called by + :meth:`RequestContext.pop() `, + which may be delayed during testing to maintain access to + resources. + + :param exc: An unhandled exception raised while dispatching the + request. Detected from the current exception information if + not passed. Passed to each teardown function. + + .. versionchanged:: 0.9 + Added the ``exc`` argument. + """ + if exc is _sentinel: + exc = sys.exc_info()[1] + + 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, exc=exc) + + def do_teardown_appcontext( + self, exc: t.Optional[BaseException] = _sentinel # type: ignore + ) -> None: + """Called right before the application context is popped. + + When handling a request, the application context is popped + after the request context. See :meth:`do_teardown_request`. + + This calls all functions decorated with + :meth:`teardown_appcontext`. Then the + :data:`appcontext_tearing_down` signal is sent. + + This is called by + :meth:`AppContext.pop() `. + + .. versionadded:: 0.9 + """ + if exc is _sentinel: + exc = sys.exc_info()[1] + + for func in reversed(self.teardown_appcontext_funcs): + self.ensure_sync(func)(exc) + + appcontext_tearing_down.send(self, exc=exc) + + def app_context(self) -> AppContext: + """Create an :class:`~flask.ctx.AppContext`. Use as a ``with`` + block to push the context, which will make :data:`current_app` + point at this application. + + An application context is automatically pushed by + :meth:`RequestContext.push() ` + when handling a request, and when running a CLI command. Use + this to manually create a context outside of these situations. + + :: + + with app.app_context(): + init_db() + + See :doc:`/appcontext`. + + .. versionadded:: 0.9 + """ + return AppContext(self) + + def request_context(self, environ: dict) -> RequestContext: + """Create a :class:`~flask.ctx.RequestContext` representing a + WSGI environment. Use a ``with`` block to push the context, + which will make :data:`request` point at this request. + + See :doc:`/reqcontext`. + + Typically you should not call this from your own code. A request + context is automatically pushed by the :meth:`wsgi_app` when + handling a request. Use :meth:`test_request_context` to create + an environment and context instead of this method. + + :param environ: a WSGI environment + """ + return RequestContext(self, environ) + + def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> RequestContext: + """Create a :class:`~flask.ctx.RequestContext` for a WSGI + environment created from the given values. This is mostly useful + during testing, where you may want to run a function that uses + request data without dispatching a full request. + + See :doc:`/reqcontext`. + + Use a ``with`` block to push the context, which will make + :data:`request` point at the request for the created + environment. :: + + with test_request_context(...): + generate_report() + + When using the shell, it may be easier to push and pop the + context manually to avoid indentation. :: + + ctx = app.test_request_context(...) + ctx.push() + ... + ctx.pop() + + Takes the same arguments as Werkzeug's + :class:`~werkzeug.test.EnvironBuilder`, with some defaults from + the application. See the linked Werkzeug docs for most of the + available arguments. Flask-specific behavior is listed here. + + :param path: URL path being requested. + :param base_url: Base URL where the app is being served, which + ``path`` is relative to. If not given, built from + :data:`PREFERRED_URL_SCHEME`, ``subdomain``, + :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. + :param subdomain: Subdomain name to append to + :data:`SERVER_NAME`. + :param url_scheme: Scheme to use instead of + :data:`PREFERRED_URL_SCHEME`. + :param data: The request body, either as a string or a dict of + form keys and values. + :param json: If given, this is serialized as JSON and passed as + ``data``. Also defaults ``content_type`` to + ``application/json``. + :param args: other positional arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + :param kwargs: other keyword arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + """ + from .testing import EnvironBuilder + + builder = EnvironBuilder(self, *args, **kwargs) + + try: + return self.request_context(builder.get_environ()) + finally: + builder.close() + + def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any: + """The actual WSGI application. This is not implemented in + :meth:`__call__` so that middlewares can be applied without + losing a reference to the app object. Instead of doing this:: + + app = MyMiddleware(app) + + It's a better idea to do this instead:: + + app.wsgi_app = MyMiddleware(app.wsgi_app) + + Then you still have the original application object around and + can continue to call methods on it. + + .. versionchanged:: 0.7 + Teardown events for the request and app contexts are called + even if an unhandled error occurs. Other events may not be + called depending on when an error occurs during dispatch. + See :ref:`callbacks-and-errors`. + + :param environ: A WSGI environment. + :param start_response: A callable accepting a status code, + a list of headers, and an optional exception context to + start the response. + """ + ctx = self.request_context(environ) + error: t.Optional[BaseException] = None + try: + try: + ctx.push() + response = self.full_dispatch_request() + except Exception as e: + error = e + response = self.handle_exception(e) + except: # noqa: B001 + error = sys.exc_info()[1] + raise + return response(environ, start_response) + finally: + if self.should_ignore_error(error): + error = None + ctx.auto_pop(error) + + def __call__(self, environ: dict, start_response: t.Callable) -> t.Any: + """The WSGI server calls the Flask application object as the + WSGI application. This calls :meth:`wsgi_app`, which can be + wrapped to apply middleware. + """ + return self.wsgi_app(environ, start_response) diff --git a/myvenv/lib/python3.10/site-packages/flask/blueprints.py b/myvenv/lib/python3.10/site-packages/flask/blueprints.py new file mode 100644 index 0000000..5c23a73 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/flask/blueprints.py @@ -0,0 +1,609 @@ +import os +import typing as t +from collections import defaultdict +from functools import update_wrapper + +from .scaffold import _endpoint_from_view_func +from .scaffold import _sentinel +from .scaffold import Scaffold +from .typing import AfterRequestCallable +from .typing import BeforeFirstRequestCallable +from .typing import BeforeRequestCallable +from .typing import TeardownCallable +from .typing import TemplateContextProcessorCallable +from .typing import TemplateFilterCallable +from .typing import TemplateGlobalCallable +from .typing import TemplateTestCallable +from .typing import URLDefaultCallable +from .typing import URLValuePreprocessorCallable + +if t.TYPE_CHECKING: + from .app import Flask + from .typing import ErrorHandlerCallable + +DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable] + + +class BlueprintSetupState: + """Temporary holder object for registering a blueprint with the + application. An instance of this class is created by the + :meth:`~flask.Blueprint.make_setup_state` method and later passed + to all register callback functions. + """ + + def __init__( + self, + blueprint: "Blueprint", + app: "Flask", + options: t.Any, + first_registration: bool, + ) -> None: + #: a reference to the current application + self.app = app + + #: a reference to the blueprint that created this setup state. + self.blueprint = blueprint + + #: a dictionary with all options that were passed to the + #: :meth:`~flask.Flask.register_blueprint` method. + self.options = options + + #: as blueprints can be registered multiple times with the + #: application and not everything wants to be registered + #: multiple times on it, this attribute can be used to figure + #: out if the blueprint was registered in the past already. + self.first_registration = first_registration + + subdomain = self.options.get("subdomain") + if subdomain is None: + subdomain = self.blueprint.subdomain + + #: The subdomain that the blueprint should be active for, ``None`` + #: otherwise. + self.subdomain = subdomain + + url_prefix = self.options.get("url_prefix") + if url_prefix is None: + url_prefix = self.blueprint.url_prefix + #: The prefix that should be used for all URLs defined on the + #: blueprint. + self.url_prefix = url_prefix + + self.name = self.options.get("name", blueprint.name) + self.name_prefix = self.options.get("name_prefix", "") + + #: A dictionary with URL defaults that is added to each and every + #: URL that was defined with the blueprint. + self.url_defaults = dict(self.blueprint.url_values_defaults) + self.url_defaults.update(self.options.get("url_defaults", ())) + + def add_url_rule( + self, + rule: str, + endpoint: t.Optional[str] = None, + view_func: t.Optional[t.Callable] = None, + **options: t.Any, + ) -> None: + """A helper method to register a rule (and optionally a view function) + to the application. The endpoint is automatically prefixed with the + blueprint's name. + """ + if self.url_prefix is not None: + if rule: + rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/"))) + else: + rule = self.url_prefix + options.setdefault("subdomain", self.subdomain) + if endpoint is None: + endpoint = _endpoint_from_view_func(view_func) # type: ignore + defaults = self.url_defaults + if "defaults" in options: + defaults = dict(defaults, **options.pop("defaults")) + + self.app.add_url_rule( + rule, + f"{self.name_prefix}.{self.name}.{endpoint}".lstrip("."), + view_func, + defaults=defaults, + **options, + ) + + +class Blueprint(Scaffold): + """Represents a blueprint, a collection of routes and other + app-related functions that can be registered on a real application + later. + + A blueprint is an object that allows defining application functions + without requiring an application object ahead of time. It uses the + same decorators as :class:`~flask.Flask`, but defers the need for an + application by recording them for later registration. + + Decorating a function with a blueprint creates a deferred function + that is called with :class:`~flask.blueprints.BlueprintSetupState` + when the blueprint is registered on an application. + + See :doc:`/blueprints` for more information. + + :param name: The name of the blueprint. Will be prepended to each + endpoint name. + :param import_name: The name of the blueprint package, usually + ``__name__``. This helps locate the ``root_path`` for the + blueprint. + :param static_folder: A folder with static files that should be + served by the blueprint's static route. The path is relative to + the blueprint's root path. Blueprint static files are disabled + by default. + :param static_url_path: The url to serve static files from. + Defaults to ``static_folder``. If the blueprint does not have + a ``url_prefix``, the app's static route will take precedence, + and the blueprint's static files won't be accessible. + :param template_folder: A folder with templates that should be added + to the app's template search path. The path is relative to the + blueprint's root path. Blueprint templates are disabled by + default. Blueprint templates have a lower precedence than those + in the app's templates folder. + :param url_prefix: A path to prepend to all of the blueprint's URLs, + to make them distinct from the rest of the app's routes. + :param subdomain: A subdomain that blueprint routes will match on by + default. + :param url_defaults: A dict of default values that blueprint routes + will receive by default. + :param root_path: By default, the blueprint will automatically set + this based on ``import_name``. In certain situations this + automatic detection can fail, so the path can be specified + manually instead. + + .. versionchanged:: 1.1.0 + Blueprints have a ``cli`` group to register nested CLI commands. + The ``cli_group`` parameter controls the name of the group under + the ``flask`` command. + + .. versionadded:: 0.7 + """ + + warn_on_modifications = False + _got_registered_once = False + + #: Blueprint local JSON encoder class to use. Set to ``None`` to use + #: the app's :class:`~flask.Flask.json_encoder`. + json_encoder = None + #: Blueprint local JSON decoder class to use. Set to ``None`` to use + #: the app's :class:`~flask.Flask.json_decoder`. + json_decoder = None + + def __init__( + self, + name: str, + import_name: str, + static_folder: t.Optional[t.Union[str, os.PathLike]] = None, + static_url_path: t.Optional[str] = None, + template_folder: t.Optional[str] = None, + url_prefix: t.Optional[str] = None, + subdomain: t.Optional[str] = None, + url_defaults: t.Optional[dict] = None, + root_path: t.Optional[str] = None, + cli_group: t.Optional[str] = _sentinel, # type: ignore + ): + super().__init__( + import_name=import_name, + static_folder=static_folder, + static_url_path=static_url_path, + template_folder=template_folder, + root_path=root_path, + ) + + if "." in name: + raise ValueError("'name' may not contain a dot '.' character.") + + self.name = name + self.url_prefix = url_prefix + self.subdomain = subdomain + self.deferred_functions: t.List[DeferredSetupFunction] = [] + + if url_defaults is None: + url_defaults = {} + + self.url_values_defaults = url_defaults + self.cli_group = cli_group + self._blueprints: t.List[t.Tuple["Blueprint", dict]] = [] + + def _is_setup_finished(self) -> bool: + return self.warn_on_modifications and self._got_registered_once + + def record(self, func: t.Callable) -> None: + """Registers a function that is called when the blueprint is + registered on the application. This function is called with the + state as argument as returned by the :meth:`make_setup_state` + method. + """ + if self._got_registered_once and self.warn_on_modifications: + from warnings import warn + + warn( + Warning( + "The blueprint was already registered once but is" + " getting modified now. These changes will not show" + " up." + ) + ) + self.deferred_functions.append(func) + + def record_once(self, func: t.Callable) -> None: + """Works like :meth:`record` but wraps the function in another + function that will ensure the function is only called once. If the + blueprint is registered a second time on the application, the + function passed is not called. + """ + + def wrapper(state: BlueprintSetupState) -> None: + if state.first_registration: + func(state) + + return self.record(update_wrapper(wrapper, func)) + + def make_setup_state( + self, app: "Flask", options: dict, first_registration: bool = False + ) -> BlueprintSetupState: + """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState` + object that is later passed to the register callback functions. + Subclasses can override this to return a subclass of the setup state. + """ + return BlueprintSetupState(self, app, options, first_registration) + + def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None: + """Register a :class:`~flask.Blueprint` on this blueprint. Keyword + arguments passed to this method will override the defaults set + on the blueprint. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionadded:: 2.0 + """ + if blueprint is self: + raise ValueError("Cannot register a blueprint on itself") + self._blueprints.append((blueprint, options)) + + def register(self, app: "Flask", options: dict) -> None: + """Called by :meth:`Flask.register_blueprint` to register all + views and callbacks registered on the blueprint with the + application. Creates a :class:`.BlueprintSetupState` and calls + each :meth:`record` callback with it. + + :param app: The application this blueprint is being registered + with. + :param options: Keyword arguments forwarded from + :meth:`~Flask.register_blueprint`. + + .. versionchanged:: 2.0.1 + Nested blueprints are registered with their dotted name. + This allows different blueprints with the same name to be + nested at different locations. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionchanged:: 2.0.1 + Registering the same blueprint with the same name multiple + times is deprecated and will become an error in Flask 2.1. + """ + name_prefix = options.get("name_prefix", "") + self_name = options.get("name", self.name) + name = f"{name_prefix}.{self_name}".lstrip(".") + + if name in app.blueprints: + existing_at = f" '{name}'" if self_name != name else "" + + if app.blueprints[name] is not self: + raise ValueError( + f"The name '{self_name}' is already registered for" + f" a different blueprint{existing_at}. Use 'name='" + " to provide a unique name." + ) + else: + import warnings + + warnings.warn( + f"The name '{self_name}' is already registered for" + f" this blueprint{existing_at}. Use 'name=' to" + " provide a unique name. This will become an error" + " in Flask 2.1.", + stacklevel=4, + ) + + 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") + + if bp_url_prefix is None: + bp_url_prefix = blueprint.url_prefix + + if state.url_prefix is not None and bp_url_prefix is not None: + bp_options["url_prefix"] = ( + state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/") + ) + elif bp_url_prefix is not None: + bp_options["url_prefix"] = bp_url_prefix + elif state.url_prefix is not None: + bp_options["url_prefix"] = state.url_prefix + + bp_options["name_prefix"] = name + blueprint.register(app, bp_options) + + def add_url_rule( + self, + rule: str, + endpoint: t.Optional[str] = None, + view_func: t.Optional[t.Callable] = None, + provide_automatic_options: t.Optional[bool] = None, + **options: t.Any, + ) -> None: + """Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for + the :func:`url_for` function is prefixed with the name of the blueprint. + """ + if endpoint and "." in endpoint: + raise ValueError("'endpoint' may not contain a dot '.' character.") + + if view_func and hasattr(view_func, "__name__") and "." in view_func.__name__: + raise ValueError("'view_func' name may not contain a dot '.' character.") + + self.record( + lambda s: s.add_url_rule( + rule, + endpoint, + view_func, + provide_automatic_options=provide_automatic_options, + **options, + ) + ) + + def app_template_filter( + self, name: t.Optional[str] = None + ) -> t.Callable[[TemplateFilterCallable], TemplateFilterCallable]: + """Register a custom template filter, available application wide. Like + :meth:`Flask.template_filter` but for a blueprint. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def decorator(f: TemplateFilterCallable) -> TemplateFilterCallable: + self.add_app_template_filter(f, name=name) + return f + + return decorator + + def add_app_template_filter( + self, f: TemplateFilterCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template filter, available application wide. Like + :meth:`Flask.add_template_filter` but for a blueprint. Works exactly + like the :meth:`app_template_filter` decorator. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.filters[name or f.__name__] = f + + self.record_once(register_template) + + def app_template_test( + self, name: t.Optional[str] = None + ) -> t.Callable[[TemplateTestCallable], TemplateTestCallable]: + """Register a custom template test, available application wide. Like + :meth:`Flask.template_test` but for a blueprint. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def decorator(f: TemplateTestCallable) -> TemplateTestCallable: + self.add_app_template_test(f, name=name) + return f + + return decorator + + def add_app_template_test( + self, f: TemplateTestCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template test, available application wide. Like + :meth:`Flask.add_template_test` but for a blueprint. Works exactly + like the :meth:`app_template_test` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.tests[name or f.__name__] = f + + self.record_once(register_template) + + def app_template_global( + self, name: t.Optional[str] = None + ) -> t.Callable[[TemplateGlobalCallable], TemplateGlobalCallable]: + """Register a custom template global, available application wide. Like + :meth:`Flask.template_global` but for a blueprint. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + + def decorator(f: TemplateGlobalCallable) -> TemplateGlobalCallable: + self.add_app_template_global(f, name=name) + return f + + return decorator + + def add_app_template_global( + self, f: TemplateGlobalCallable, name: t.Optional[str] = None + ) -> None: + """Register a custom template global, available application wide. Like + :meth:`Flask.add_template_global` but for a blueprint. Works exactly + like the :meth:`app_template_global` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.globals[name or f.__name__] = f + + self.record_once(register_template) + + def before_app_request(self, f: BeforeRequestCallable) -> BeforeRequestCallable: + """Like :meth:`Flask.before_request`. Such a function is executed + before each request, even if outside of a blueprint. + """ + self.record_once( + lambda s: s.app.before_request_funcs.setdefault(None, []).append(f) + ) + return f + + def before_app_first_request( + self, f: BeforeFirstRequestCallable + ) -> BeforeFirstRequestCallable: + """Like :meth:`Flask.before_first_request`. Such a function is + executed before the first request to the application. + """ + self.record_once(lambda s: s.app.before_first_request_funcs.append(f)) + return f + + def after_app_request(self, f: AfterRequestCallable) -> AfterRequestCallable: + """Like :meth:`Flask.after_request` but for a blueprint. Such a function + is executed after each request, even if outside of the blueprint. + """ + self.record_once( + lambda s: s.app.after_request_funcs.setdefault(None, []).append(f) + ) + return f + + def teardown_app_request(self, f: TeardownCallable) -> TeardownCallable: + """Like :meth:`Flask.teardown_request` but for a blueprint. Such a + function is executed when tearing down each request, even if outside of + the blueprint. + """ + self.record_once( + lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f) + ) + return f + + def app_context_processor( + self, f: TemplateContextProcessorCallable + ) -> TemplateContextProcessorCallable: + """Like :meth:`Flask.context_processor` but for a blueprint. Such a + function is executed each request, even if outside of the blueprint. + """ + self.record_once( + lambda s: s.app.template_context_processors.setdefault(None, []).append(f) + ) + return f + + def app_errorhandler(self, code: t.Union[t.Type[Exception], int]) -> t.Callable: + """Like :meth:`Flask.errorhandler` but for a blueprint. This + handler is used for all requests, even if outside of the blueprint. + """ + + def decorator( + f: "ErrorHandlerCallable[Exception]", + ) -> "ErrorHandlerCallable[Exception]": + self.record_once(lambda s: s.app.errorhandler(code)(f)) + return f + + return decorator + + def app_url_value_preprocessor( + self, f: URLValuePreprocessorCallable + ) -> URLValuePreprocessorCallable: + """Same as :meth:`url_value_preprocessor` but application wide.""" + self.record_once( + lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f) + ) + return f + + def app_url_defaults(self, f: URLDefaultCallable) -> URLDefaultCallable: + """Same as :meth:`url_defaults` but application wide.""" + self.record_once( + lambda s: s.app.url_default_functions.setdefault(None, []).append(f) + ) + return f diff --git a/myvenv/lib/python3.10/site-packages/flask/cli.py b/myvenv/lib/python3.10/site-packages/flask/cli.py new file mode 100644 index 0000000..8e21532 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/flask/cli.py @@ -0,0 +1,999 @@ +import ast +import inspect +import os +import platform +import re +import sys +import traceback +import warnings +from functools import update_wrapper +from operator import attrgetter +from threading import Lock +from threading import Thread + +import click +from werkzeug.utils import import_string + +from .globals import current_app +from .helpers import get_debug_flag +from .helpers import get_env +from .helpers import get_load_dotenv + +try: + import dotenv +except ImportError: + dotenv = None + +try: + import ssl +except ImportError: + ssl = None # type: ignore + + +class NoAppException(click.UsageError): + """Raised if an application cannot be found or loaded.""" + + +def find_best_app(script_info, module): + """Given a module instance this tries to find the best possible + application in the module or raises an exception. + """ + from . import Flask + + # Search for the most common names first. + for attr_name in ("app", "application"): + app = getattr(module, attr_name, None) + + if isinstance(app, Flask): + return app + + # Otherwise find the only object that is a Flask instance. + matches = [v for v in module.__dict__.values() if isinstance(v, Flask)] + + if len(matches) == 1: + return matches[0] + elif len(matches) > 1: + raise NoAppException( + "Detected multiple Flask applications in module" + f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'" + f" to specify the correct one." + ) + + # Search for app factory functions. + for attr_name in ("create_app", "make_app"): + app_factory = getattr(module, attr_name, None) + + if inspect.isfunction(app_factory): + try: + app = call_factory(script_info, app_factory) + + if isinstance(app, Flask): + return app + except TypeError as e: + if not _called_with_wrong_args(app_factory): + raise + + raise NoAppException( + f"Detected factory {attr_name!r} in module {module.__name__!r}," + " but could not call it without arguments. Use" + f" \"FLASK_APP='{module.__name__}:{attr_name}(args)'\"" + " to specify arguments." + ) from e + + raise NoAppException( + "Failed to find Flask application or factory in module" + f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'" + " to specify one." + ) + + +def call_factory(script_info, app_factory, args=None, kwargs=None): + """Takes an app factory, a ``script_info` object and optionally a tuple + of arguments. Checks for the existence of a script_info argument and calls + the app_factory depending on that and the arguments provided. + """ + sig = inspect.signature(app_factory) + args = [] if args is None else args + kwargs = {} if kwargs is None else kwargs + + if "script_info" in sig.parameters: + warnings.warn( + "The 'script_info' argument is deprecated and will not be" + " passed to the app factory function in Flask 2.1.", + DeprecationWarning, + ) + kwargs["script_info"] = script_info + + if not args and len(sig.parameters) == 1: + first_parameter = next(iter(sig.parameters.values())) + + if ( + first_parameter.default is inspect.Parameter.empty + # **kwargs is reported as an empty default, ignore it + and first_parameter.kind is not inspect.Parameter.VAR_KEYWORD + ): + warnings.warn( + "Script info is deprecated and will not be passed as the" + " single argument to the app factory function in Flask" + " 2.1.", + DeprecationWarning, + ) + args.append(script_info) + + return app_factory(*args, **kwargs) + + +def _called_with_wrong_args(f): + """Check whether calling a function raised a ``TypeError`` because + the call failed or because something in the factory raised the + error. + + :param f: The function that was called. + :return: ``True`` if the call failed. + """ + tb = sys.exc_info()[2] + + try: + while tb is not None: + if tb.tb_frame.f_code is f.__code__: + # In the function, it was called successfully. + return False + + tb = tb.tb_next + + # Didn't reach the function. + return True + finally: + # Delete tb to break a circular reference. + # https://docs.python.org/2/library/sys.html#sys.exc_info + del tb + + +def find_app_by_string(script_info, module, app_name): + """Check if the given string is a variable name or a function. Call + a function to get the app instance, or return the variable directly. + """ + from . import Flask + + # Parse app_name as a single expression to determine if it's a valid + # attribute name or function call. + try: + expr = ast.parse(app_name.strip(), mode="eval").body + except SyntaxError: + raise NoAppException( + f"Failed to parse {app_name!r} as an attribute name or function call." + ) from None + + if isinstance(expr, ast.Name): + name = expr.id + args = kwargs = None + elif isinstance(expr, ast.Call): + # Ensure the function name is an attribute name only. + if not isinstance(expr.func, ast.Name): + raise NoAppException( + f"Function reference must be a simple name: {app_name!r}." + ) + + name = expr.func.id + + # Parse the positional and keyword arguments as literals. + try: + args = [ast.literal_eval(arg) for arg in expr.args] + kwargs = {kw.arg: ast.literal_eval(kw.value) for kw in expr.keywords} + except ValueError: + # literal_eval gives cryptic error messages, show a generic + # message with the full expression instead. + raise NoAppException( + f"Failed to parse arguments as literal values: {app_name!r}." + ) 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 = call_factory(script_info, 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(script_info, module_name, app_name, raise_if_not_found=True): + __traceback_hide__ = True # noqa: F841 + + try: + __import__(module_name) + except ImportError: + # Reraise the ImportError if it occurred within the imported module. + # Determine this by checking whether the trace has a depth > 1. + if sys.exc_info()[2].tb_next: + raise NoAppException( + f"While importing {module_name!r}, an ImportError was" + f" raised:\n\n{traceback.format_exc()}" + ) 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(script_info, module) + else: + return find_app_by_string(script_info, module, app_name) + + +def get_version(ctx, param, value): + if not value or ctx.resilient_parsing: + return + + import werkzeug + from . import __version__ + + click.echo( + f"Python {platform.python_version()}\n" + f"Flask {__version__}\n" + f"Werkzeug {werkzeug.__version__}", + color=ctx.color, + ) + ctx.exit() + + +version_option = click.Option( + ["--version"], + help="Show the flask version", + expose_value=False, + callback=get_version, + is_flag=True, + is_eager=True, +) + + +class DispatchingApp: + """Special application that dispatches to a Flask application which + is imported by name in a background thread. If an error happens + it is recorded and shown as part of the WSGI handling which in case + of the Werkzeug debugger means that it shows up in the browser. + """ + + def __init__(self, loader, use_eager_loading=None): + self.loader = loader + self._app = None + self._lock = Lock() + self._bg_loading_exc = None + + if use_eager_loading is None: + use_eager_loading = os.environ.get("WERKZEUG_RUN_MAIN") != "true" + + if use_eager_loading: + self._load_unlocked() + else: + self._load_in_background() + + def _load_in_background(self): + def _load_app(): + __traceback_hide__ = True # noqa: F841 + with self._lock: + try: + self._load_unlocked() + except Exception as e: + self._bg_loading_exc = e + + t = Thread(target=_load_app, args=()) + t.start() + + def _flush_bg_loading_exception(self): + __traceback_hide__ = True # noqa: F841 + exc = self._bg_loading_exc + + if exc is not None: + self._bg_loading_exc = None + raise exc + + def _load_unlocked(self): + __traceback_hide__ = True # noqa: F841 + self._app = rv = self.loader() + self._bg_loading_exc = None + return rv + + def __call__(self, environ, start_response): + __traceback_hide__ = True # noqa: F841 + if self._app is not None: + return self._app(environ, start_response) + self._flush_bg_loading_exception() + with self._lock: + if self._app is not None: + rv = self._app + else: + rv = self._load_unlocked() + return rv(environ, start_response) + + +class ScriptInfo: + """Helper object to deal with Flask applications. This is usually not + necessary to interface with as it's used internally in the dispatching + to click. In future versions of Flask this object will most likely play + a bigger role. Typically it's created automatically by the + :class:`FlaskGroup` but you can also manually create it and pass it + onwards as click object. + """ + + def __init__(self, app_import_path=None, create_app=None, set_debug_flag=True): + #: Optionally the import path for the Flask application. + self.app_import_path = app_import_path or os.environ.get("FLASK_APP") + #: Optionally a function that is passed the script info to create + #: the instance of the application. + self.create_app = create_app + #: A dictionary with arbitrary data that can be associated with + #: this script info. + self.data = {} + self.set_debug_flag = set_debug_flag + self._loaded_app = None + + def load_app(self): + """Loads the Flask app (if not yet loaded) and returns it. Calling + this multiple times will just result in the already loaded app to + be returned. + """ + __traceback_hide__ = True # noqa: F841 + + if self._loaded_app is not None: + return self._loaded_app + + if self.create_app is not None: + app = call_factory(self, self.create_app) + else: + if self.app_import_path: + path, name = ( + re.split(r":(?![\\/])", self.app_import_path, 1) + [None] + )[:2] + import_name = prepare_import(path) + app = locate_app(self, import_name, name) + else: + for path in ("wsgi.py", "app.py"): + import_name = prepare_import(path) + app = locate_app(self, import_name, None, raise_if_not_found=False) + + if app: + break + + if not app: + raise NoAppException( + "Could not locate a Flask application. You did not provide " + 'the "FLASK_APP" environment variable, and a "wsgi.py" or ' + '"app.py" module was not found in the current directory.' + ) + + if self.set_debug_flag: + # Update the app's debug flag through the descriptor so that + # other values repopulate as well. + app.debug = get_debug_flag() + + self._loaded_app = app + return app + + +pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True) + + +def with_appcontext(f): + """Wraps a callback so that it's guaranteed to be executed with the + script's application context. If callbacks are registered directly + to the ``app.cli`` object then they are wrapped with this function + by default unless it's disabled. + """ + + @click.pass_context + def decorator(__ctx, *args, **kwargs): + with __ctx.ensure_object(ScriptInfo).load_app().app_context(): + return __ctx.invoke(f, *args, **kwargs) + + return update_wrapper(decorator, f) + + +class AppGroup(click.Group): + """This works similar to a regular click :class:`~click.Group` but it + changes the behavior of the :meth:`command` decorator so that it + automatically wraps the functions in :func:`with_appcontext`. + + Not to be confused with :class:`FlaskGroup`. + """ + + def command(self, *args, **kwargs): + """This works exactly like the method of the same name on a regular + :class:`click.Group` but it wraps callbacks in :func:`with_appcontext` + unless it's disabled by passing ``with_appcontext=False``. + """ + wrap_for_ctx = kwargs.pop("with_appcontext", True) + + def decorator(f): + if wrap_for_ctx: + f = with_appcontext(f) + return click.Group.command(self, *args, **kwargs)(f) + + return decorator + + def group(self, *args, **kwargs): + """This works exactly like the method of the same name on a regular + :class:`click.Group` but it defaults the group class to + :class:`AppGroup`. + """ + kwargs.setdefault("cls", AppGroup) + return click.Group.group(self, *args, **kwargs) + + +class FlaskGroup(AppGroup): + """Special subclass of the :class:`AppGroup` group that supports + loading more commands from the configured Flask app. Normally a + developer does not have to interface with this class but there are + some very advanced use cases for which it makes sense to create an + instance of this. see :ref:`custom-scripts`. + + :param add_default_commands: if this is True then the default run and + shell commands will be added. + :param add_version_option: adds the ``--version`` option. + :param create_app: an optional callback that is passed the script info and + returns the loaded app. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param set_debug_flag: Set the app's debug flag based on the active + environment + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment variables + from :file:`.env` and :file:`.flaskenv` files. + """ + + def __init__( + self, + add_default_commands=True, + create_app=None, + add_version_option=True, + load_dotenv=True, + set_debug_flag=True, + **extra, + ): + params = list(extra.pop("params", None) or ()) + + if add_version_option: + params.append(version_option) + + AppGroup.__init__(self, params=params, **extra) + self.create_app = create_app + self.load_dotenv = load_dotenv + self.set_debug_flag = set_debug_flag + + if add_default_commands: + self.add_command(run_command) + self.add_command(shell_command) + self.add_command(routes_command) + + self._loaded_plugin_commands = False + + def _load_plugin_commands(self): + if self._loaded_plugin_commands: + return + try: + import pkg_resources + except ImportError: + self._loaded_plugin_commands = True + return + + for ep in pkg_resources.iter_entry_points("flask.commands"): + self.add_command(ep.load(), ep.name) + self._loaded_plugin_commands = True + + def get_command(self, ctx, name): + self._load_plugin_commands() + # Look up built-in and plugin commands, which should be + # available even if the app fails to load. + rv = super().get_command(ctx, name) + + if rv is not None: + return rv + + info = ctx.ensure_object(ScriptInfo) + + # Look up commands provided by the app, showing an error and + # continuing if the app couldn't be loaded. + try: + return info.load_app().cli.get_command(ctx, name) + except NoAppException as e: + click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") + + def list_commands(self, ctx): + self._load_plugin_commands() + # Start with the built-in and plugin commands. + rv = set(super().list_commands(ctx)) + info = ctx.ensure_object(ScriptInfo) + + # Add commands provided by the app, showing an error and + # continuing if the app couldn't be loaded. + try: + rv.update(info.load_app().cli.list_commands(ctx)) + except NoAppException as e: + # When an app couldn't be loaded, show the error message + # without the traceback. + click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") + except Exception: + # When any other errors occurred during loading, show the + # full traceback. + click.secho(f"{traceback.format_exc()}\n", err=True, fg="red") + + return sorted(rv) + + def main(self, *args, **kwargs): + # Set a global flag that indicates that we were invoked from the + # command line interface. This is detected by Flask.run to make the + # call into a no-op. This is necessary to avoid ugly errors when the + # script that is loaded here also attempts to start a server. + os.environ["FLASK_RUN_FROM_CLI"] = "true" + + if get_load_dotenv(self.load_dotenv): + load_dotenv() + + obj = kwargs.get("obj") + + if obj is None: + obj = ScriptInfo( + create_app=self.create_app, set_debug_flag=self.set_debug_flag + ) + + kwargs["obj"] = obj + kwargs.setdefault("auto_envvar_prefix", "FLASK") + return super().main(*args, **kwargs) + + +def _path_is_ancestor(path, other): + """Take ``other`` and remove the length of ``path`` from it. Then join it + to ``path``. If it is the original value, ``path`` is an ancestor of + ``other``.""" + return os.path.join(path, other[len(path) :].lstrip(os.sep)) == other + + +def load_dotenv(path=None): + """Load "dotenv" files in order of precedence to set environment variables. + + If an env var is already set it is not overwritten, so earlier files in the + list are preferred over later files. + + This is a no-op if `python-dotenv`_ is not installed. + + .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme + + :param path: Load the file at this location instead of searching. + :return: ``True`` if a file was loaded. + + .. versionchanged:: 1.1.0 + Returns ``False`` when python-dotenv is not installed, or when + the given path isn't a file. + + .. versionchanged:: 2.0 + When loading the env files, set the default encoding to UTF-8. + + .. versionadded:: 1.0 + """ + if dotenv is None: + if path or os.path.isfile(".env") or os.path.isfile(".flaskenv"): + click.secho( + " * Tip: There are .env or .flaskenv files present." + ' Do "pip install python-dotenv" to use them.', + fg="yellow", + err=True, + ) + + return False + + # if the given path specifies the actual file then return True, + # else False + if path is not None: + if os.path.isfile(path): + return dotenv.load_dotenv(path, encoding="utf-8") + + return False + + new_dir = None + + for name in (".env", ".flaskenv"): + path = dotenv.find_dotenv(name, usecwd=True) + + if not path: + continue + + if new_dir is None: + new_dir = os.path.dirname(path) + + dotenv.load_dotenv(path, encoding="utf-8") + + return new_dir is not None # at least one file was located and loaded + + +def show_server_banner(env, debug, app_import_path, eager_loading): + """Show extra startup messages the first time the server is run, + ignoring the reloader. + """ + if os.environ.get("WERKZEUG_RUN_MAIN") == "true": + return + + if app_import_path is not None: + message = f" * Serving Flask app {app_import_path!r}" + + if not eager_loading: + message += " (lazy loading)" + + click.echo(message) + + click.echo(f" * Environment: {env}") + + if env == "production": + click.secho( + " WARNING: This is a development server. Do not use it in" + " a production deployment.", + fg="red", + ) + click.secho(" Use a production WSGI server instead.", dim=True) + + if debug is not None: + click.echo(f" * Debug mode: {'on' if debug else 'off'}") + + +class CertParamType(click.ParamType): + """Click option type for the ``--cert`` option. Allows either an + existing file, the string ``'adhoc'``, or an import for a + :class:`~ssl.SSLContext` object. + """ + + name = "path" + + def __init__(self): + self.path_type = click.Path(exists=True, dir_okay=False, resolve_path=True) + + def convert(self, value, param, ctx): + if ssl is None: + raise click.BadParameter( + 'Using "--cert" requires Python to be compiled with SSL support.', + ctx, + param, + ) + + try: + return self.path_type(value, param, ctx) + except click.BadParameter: + value = click.STRING(value, param, ctx).lower() + + if value == "adhoc": + try: + import cryptography # noqa: F401 + except ImportError: + raise click.BadParameter( + "Using ad-hoc certificates requires the cryptography library.", + ctx, + param, + ) 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" + is_context = ssl and isinstance(cert, ssl.SSLContext) + + if value is not None: + if is_adhoc: + raise click.BadParameter( + 'When "--cert" is "adhoc", "--key" is not used.', ctx, param + ) + + if is_context: + raise click.BadParameter( + 'When "--cert" is an SSLContext object, "--key is not used.', ctx, param + ) + + if not cert: + raise click.BadParameter('"--cert" must also be specified.', ctx, param) + + ctx.params["cert"] = cert, value + + else: + if cert and not (is_adhoc or is_context): + raise click.BadParameter('Required when using "--cert".', ctx, param) + + return value + + +class SeparatedPathType(click.Path): + """Click option type that accepts a list of values separated by the + OS's path separator (``:``, ``;`` on Windows). Each value is + validated as a :class:`click.Path` type. + """ + + def convert(self, value, param, ctx): + items = self.split_envvar_value(value) + super_convert = super().convert + return [super_convert(item, param, ctx) for item in items] + + +@click.command("run", short_help="Run a development server.") +@click.option("--host", "-h", default="127.0.0.1", help="The interface to bind to.") +@click.option("--port", "-p", default=5000, help="The port to bind to.") +@click.option( + "--cert", type=CertParamType(), help="Specify a certificate file to use HTTPS." +) +@click.option( + "--key", + type=click.Path(exists=True, dir_okay=False, resolve_path=True), + callback=_validate_key, + expose_value=False, + help="The key file to use when specifying a certificate.", +) +@click.option( + "--reload/--no-reload", + default=None, + help="Enable or disable the reloader. By default the reloader " + "is active if debug is enabled.", +) +@click.option( + "--debugger/--no-debugger", + default=None, + help="Enable or disable the debugger. By default the debugger " + "is active if debug is enabled.", +) +@click.option( + "--eager-loading/--lazy-loading", + default=None, + help="Enable or disable eager loading. By default eager " + "loading is enabled if the reloader is disabled.", +) +@click.option( + "--with-threads/--without-threads", + default=True, + help="Enable or disable multithreading.", +) +@click.option( + "--extra-files", + default=None, + type=SeparatedPathType(), + help=( + "Extra files that trigger a reload on change. Multiple paths" + f" are separated by {os.path.pathsep!r}." + ), +) +@pass_script_info +def run_command( + info, host, port, reload, debugger, eager_loading, with_threads, cert, extra_files +): + """Run a local development server. + + This server is for development purposes only. It does not provide + the stability, security, or performance of production WSGI servers. + + The reloader and debugger are enabled by default if + FLASK_ENV=development or FLASK_DEBUG=1. + """ + debug = get_debug_flag() + + if reload is None: + reload = debug + + if debugger is None: + debugger = debug + + show_server_banner(get_env(), debug, info.app_import_path, eager_loading) + app = DispatchingApp(info.load_app, use_eager_loading=eager_loading) + + from werkzeug.serving import run_simple + + run_simple( + host, + port, + app, + use_reloader=reload, + use_debugger=debugger, + threaded=with_threads, + ssl_context=cert, + extra_files=extra_files, + ) + + +@click.command("shell", short_help="Run a shell in the app context.") +@with_appcontext +def shell_command() -> None: + """Run an interactive Python shell in the context of a given + Flask application. The application will populate the default + namespace of this shell according to its configuration. + + This is useful for executing small snippets of management code + without having to manually configure the application. + """ + import code + from .globals import _app_ctx_stack + + app = _app_ctx_stack.top.app + banner = ( + f"Python {sys.version} on {sys.platform}\n" + f"App: {app.import_name} [{app.env}]\n" + f"Instance: {app.instance_path}" + ) + ctx: dict = {} + + # Support the regular Python interpreter startup script if someone + # is using it. + startup = os.environ.get("PYTHONSTARTUP") + if startup and os.path.isfile(startup): + with open(startup) as f: + eval(compile(f.read(), startup, "exec"), ctx) + + ctx.update(app.make_shell_context()) + + # Site, customize, or startup script can set a hook to call when + # entering interactive mode. The default one sets up readline with + # tab and history completion. + interactive_hook = getattr(sys, "__interactivehook__", None) + + if interactive_hook is not None: + try: + import readline + from rlcompleter import Completer + except ImportError: + pass + else: + # rlcompleter uses __main__.__dict__ by default, which is + # flask.__main__. Use the shell context instead. + readline.set_completer(Completer(ctx).complete) + + interactive_hook() + + code.interact(banner=banner, local=ctx) + + +@click.command("routes", short_help="Show the routes for the app.") +@click.option( + "--sort", + "-s", + type=click.Choice(("endpoint", "methods", "rule", "match")), + default="endpoint", + help=( + 'Method to sort routes by. "match" is the order that Flask will match ' + "routes when dispatching a request." + ), +) +@click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.") +@with_appcontext +def routes_command(sort: str, all_methods: bool) -> None: + """Show all registered routes with endpoints and methods.""" + + rules = list(current_app.url_map.iter_rules()) + if not rules: + click.echo("No routes were registered.") + return + + ignored_methods = set(() if all_methods else ("HEAD", "OPTIONS")) + + if sort in ("endpoint", "rule"): + rules = sorted(rules, key=attrgetter(sort)) + elif sort == "methods": + rules = sorted(rules, key=lambda rule: sorted(rule.methods)) # type: ignore + + rule_methods = [ + ", ".join(sorted(rule.methods - ignored_methods)) # type: ignore + for rule in rules + ] + + headers = ("Endpoint", "Methods", "Rule") + widths = ( + max(len(rule.endpoint) for rule in rules), + max(len(methods) for methods in rule_methods), + max(len(rule.rule) for rule in rules), + ) + widths = [max(len(h), w) for h, w in zip(headers, widths)] + row = "{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}".format(*widths) + + click.echo(row.format(*headers).strip()) + click.echo(row.format(*("-" * width for width in widths))) + + for rule, methods in zip(rules, rule_methods): + click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip()) + + +cli = FlaskGroup( + help="""\ +A general utility script for Flask applications. + +Provides commands from Flask, extensions, and the application. Loads the +application defined in the FLASK_APP environment variable, or from a wsgi.py +file. Setting the FLASK_ENV environment variable to 'development' will enable +debug mode. + +\b + {prefix}{cmd} FLASK_APP=hello.py + {prefix}{cmd} FLASK_ENV=development + {prefix}flask run +""".format( + cmd="export" if os.name == "posix" else "set", + prefix="$ " if os.name == "posix" else "> ", + ) +) + + +def main() -> None: + if int(click.__version__[0]) < 8: + warnings.warn( + "Using the `flask` cli with Click 7 is deprecated and" + " will not be supported starting with Flask 2.1." + " Please upgrade to Click 8 as soon as possible.", + DeprecationWarning, + ) + # TODO omit sys.argv once https://github.com/pallets/click/issues/536 is fixed + cli.main(args=sys.argv[1:]) + + +if __name__ == "__main__": + main() diff --git a/myvenv/lib/python3.10/site-packages/flask/config.py b/myvenv/lib/python3.10/site-packages/flask/config.py new file mode 100644 index 0000000..ca76902 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/flask/config.py @@ -0,0 +1,295 @@ +import errno +import os +import types +import typing as t + +from werkzeug.utils import import_string + + +class ConfigAttribute: + """Makes an attribute forward to the config""" + + def __init__(self, name: str, get_converter: t.Optional[t.Callable] = None) -> None: + self.__name__ = name + self.get_converter = get_converter + + def __get__(self, obj: t.Any, owner: t.Any = None) -> t.Any: + if obj is None: + return self + rv = obj.config[self.__name__] + if self.get_converter is not None: + rv = self.get_converter(rv) + return rv + + def __set__(self, obj: t.Any, value: t.Any) -> None: + obj.config[self.__name__] = value + + +class Config(dict): + """Works exactly like a dict but provides ways to fill it from files + or special dictionaries. There are two common patterns to populate the + config. + + Either you can fill the config from a config file:: + + app.config.from_pyfile('yourconfig.cfg') + + Or alternatively you can define the configuration options in the + module that calls :meth:`from_object` or provide an import path to + a module that should be loaded. It is also possible to tell it to + use the same module and with that provide the configuration values + just before the call:: + + DEBUG = True + SECRET_KEY = 'development key' + app.config.from_object(__name__) + + In both cases (loading from any Python file or loading from modules), + only uppercase keys are added to the config. This makes it possible to use + lowercase values in the config file for temporary values that are not added + to the config or to define the config keys in the same file that implements + the application. + + Probably the most interesting way to load configurations is from an + environment variable pointing to a file:: + + app.config.from_envvar('YOURAPPLICATION_SETTINGS') + + In this case before launching the application you have to set this + environment variable to the file you want to use. On Linux and OS X + use the export statement:: + + export YOURAPPLICATION_SETTINGS='/path/to/config/file' + + On windows use `set` instead. + + :param root_path: path to which files are read relative from. When the + config object is created by the application, this is + the application's :attr:`~flask.Flask.root_path`. + :param defaults: an optional dictionary of default values + """ + + def __init__(self, root_path: str, defaults: t.Optional[dict] = None) -> None: + dict.__init__(self, defaults or {}) + self.root_path = root_path + + def from_envvar(self, variable_name: str, silent: bool = False) -> bool: + """Loads a configuration from an environment variable pointing to + a configuration file. This is basically just a shortcut with nicer + error messages for this line of code:: + + app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) + + :param variable_name: name of the environment variable + :param silent: set to ``True`` if you want silent failure for missing + files. + :return: ``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_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: t.Union[object, str]) -> None: + """Updates the values from the given object. An object can be of one + of the following two types: + + - a string: in this case the object with that name will be imported + - an actual object reference: that object is used directly + + Objects are usually either modules or classes. :meth:`from_object` + loads only the uppercase attributes of the module/class. A ``dict`` + object will not work with :meth:`from_object` because the keys of a + ``dict`` are not attributes of the ``dict`` class. + + Example of module-based configuration:: + + app.config.from_object('yourapplication.default_config') + from yourapplication import default_config + app.config.from_object(default_config) + + Nothing is done to the object before loading. If the object is a + class and has ``@property`` attributes, it needs to be + instantiated before being passed to this method. + + You should not use this function to load the actual configuration but + rather configuration defaults. The actual config should be loaded + with :meth:`from_pyfile` and ideally from a location not within the + package because the package might be installed system wide. + + See :ref:`config-dev-prod` for an example of class-based configuration + using :meth:`from_object`. + + :param obj: an import name or object + """ + if isinstance(obj, str): + obj = import_string(obj) + for key in dir(obj): + if key.isupper(): + self[key] = getattr(obj, key) + + def from_file( + self, + filename: str, + load: t.Callable[[t.IO[t.Any]], t.Mapping], + silent: bool = False, + ) -> bool: + """Update the values in the config from a file that is loaded + using the ``load`` parameter. The loaded data is passed to the + :meth:`from_mapping` method. + + .. code-block:: python + + import toml + app.config.from_file("config.toml", load=toml.load) + + :param filename: The path to the data file. This can be an + absolute path or relative to the config root path. + :param load: A callable that takes a file handle and returns a + mapping of loaded data from the file. + :type load: ``Callable[[Reader], Mapping]`` where ``Reader`` + implements a ``read`` method. + :param silent: Ignore the file if it doesn't exist. + :return: ``True`` if the file was loaded successfully. + + .. versionadded:: 2.0 + """ + filename = os.path.join(self.root_path, filename) + + try: + with open(filename) as f: + obj = load(f) + except OSError as e: + if silent and e.errno in (errno.ENOENT, errno.EISDIR): + return False + + e.strerror = f"Unable to load configuration file ({e.strerror})" + raise + + return self.from_mapping(obj) + + def from_json(self, filename: str, silent: bool = False) -> bool: + """Update the values in the config from a JSON file. The loaded + data is passed to the :meth:`from_mapping` method. + + :param filename: The path to the JSON file. This can be an + absolute path or relative to the config root path. + :param silent: Ignore the file if it doesn't exist. + :return: ``True`` if the file was loaded successfully. + + .. deprecated:: 2.0.0 + Will be removed in Flask 2.1. Use :meth:`from_file` instead. + This was removed early in 2.0.0, was added back in 2.0.1. + + .. versionadded:: 0.11 + """ + import warnings + from . import json + + warnings.warn( + "'from_json' is deprecated and will be removed in Flask" + " 2.1. Use 'from_file(path, json.load)' instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.from_file(filename, json.load, silent=silent) + + def from_mapping( + self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any + ) -> bool: + """Updates the config like :meth:`update` ignoring items with non-upper + keys. + :return: Always returns ``True``. + + .. versionadded:: 0.11 + """ + mappings: t.Dict[str, t.Any] = {} + if mapping is not None: + mappings.update(mapping) + mappings.update(kwargs) + for key, value in mappings.items(): + if key.isupper(): + self[key] = value + return True + + def get_namespace( + self, namespace: str, lowercase: bool = True, trim_namespace: bool = True + ) -> t.Dict[str, t.Any]: + """Returns a dictionary containing a subset of configuration options + that match the specified namespace/prefix. Example usage:: + + app.config['IMAGE_STORE_TYPE'] = 'fs' + app.config['IMAGE_STORE_PATH'] = '/var/app/images' + app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com' + image_store_config = app.config.get_namespace('IMAGE_STORE_') + + The resulting dictionary `image_store_config` would look like:: + + { + 'type': 'fs', + 'path': '/var/app/images', + 'base_url': 'http://img.website.com' + } + + This is often useful when configuration options map directly to + keyword arguments in functions or class constructors. + + :param namespace: a configuration namespace + :param lowercase: a flag indicating if the keys of the resulting + dictionary should be lowercase + :param trim_namespace: a flag indicating if the keys of the resulting + dictionary should not include the namespace + + .. versionadded:: 0.11 + """ + rv = {} + for k, v in self.items(): + if not k.startswith(namespace): + continue + if trim_namespace: + key = k[len(namespace) :] + else: + key = k + if lowercase: + key = key.lower() + rv[key] = v + return rv + + def __repr__(self) -> str: + return f"<{type(self).__name__} {dict.__repr__(self)}>" diff --git a/myvenv/lib/python3.10/site-packages/flask/ctx.py b/myvenv/lib/python3.10/site-packages/flask/ctx.py new file mode 100644 index 0000000..47465fd --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/flask/ctx.py @@ -0,0 +1,489 @@ +import sys +import typing as t +from functools import update_wrapper +from types import TracebackType + +from werkzeug.exceptions import HTTPException + +from .globals import _app_ctx_stack +from .globals import _request_ctx_stack +from .signals import appcontext_popped +from .signals import appcontext_pushed +from .typing import AfterRequestCallable + +if t.TYPE_CHECKING: + from .app import Flask + from .sessions import SessionMixin + from .wrappers import Request + + +# a singleton sentinel value for parameter defaults +_sentinel = object() + + +class _AppCtxGlobals: + """A plain object. Used as a namespace for storing data during an + application context. + + Creating an app context automatically creates this object, which is + made available as the :data:`g` proxy. + + .. describe:: 'key' in g + + Check whether an attribute is present. + + .. versionadded:: 0.10 + + .. describe:: iter(g) + + Return an iterator over the attribute names. + + .. versionadded:: 0.10 + """ + + # Define attr methods to let mypy know this is a namespace object + # that has arbitrary attributes. + + def __getattr__(self, name: str) -> t.Any: + try: + return self.__dict__[name] + except KeyError: + raise AttributeError(name) from None + + def __setattr__(self, name: str, value: t.Any) -> None: + self.__dict__[name] = value + + def __delattr__(self, name: str) -> None: + try: + del self.__dict__[name] + except KeyError: + raise AttributeError(name) from None + + def get(self, name: str, default: t.Optional[t.Any] = None) -> t.Any: + """Get an attribute by name, or a default value. Like + :meth:`dict.get`. + + :param name: Name of attribute to get. + :param default: Value to return if the attribute is not present. + + .. versionadded:: 0.10 + """ + return self.__dict__.get(name, default) + + def pop(self, name: str, default: t.Any = _sentinel) -> t.Any: + """Get and remove an attribute by name. Like :meth:`dict.pop`. + + :param name: Name of attribute to pop. + :param default: Value to return if the attribute is not present, + instead of raising a ``KeyError``. + + .. versionadded:: 0.11 + """ + if default is _sentinel: + return self.__dict__.pop(name) + else: + return self.__dict__.pop(name, default) + + def setdefault(self, name: str, default: t.Any = None) -> t.Any: + """Get the value of an attribute if it is present, otherwise + set and return a default value. Like :meth:`dict.setdefault`. + + :param name: Name of attribute to get. + :param default: Value to set and return if the attribute is not + present. + + .. versionadded:: 0.11 + """ + return self.__dict__.setdefault(name, default) + + def __contains__(self, item: str) -> bool: + return item in self.__dict__ + + def __iter__(self) -> t.Iterator[str]: + return iter(self.__dict__) + + def __repr__(self) -> str: + top = _app_ctx_stack.top + if top is not None: + return f"" + return object.__repr__(self) + + +def after_this_request(f: AfterRequestCallable) -> AfterRequestCallable: + """Executes a function after this request. This is useful to modify + response objects. The function is passed the response object and has + to return the same or a new one. + + Example:: + + @app.route('/') + def index(): + @after_this_request + def add_header(response): + response.headers['X-Foo'] = 'Parachute' + return response + return 'Hello World!' + + This is more useful if a function other than the view function wants to + modify a response. For instance think of a decorator that wants to add + some headers without converting the return value into a response object. + + .. versionadded:: 0.9 + """ + top = _request_ctx_stack.top + + if top is None: + raise RuntimeError( + "This decorator can only be used when a request context is" + " active, such as within a view function." + ) + + top._after_request_functions.append(f) + return f + + +def copy_current_request_context(f: t.Callable) -> t.Callable: + """A helper function that decorates a function to retain the current + request context. This is useful when working with greenlets. The moment + the function is decorated a copy of the request context is created and + then pushed when the function is called. The current session is also + included in the copied request context. + + Example:: + + import gevent + from flask import copy_current_request_context + + @app.route('/') + def index(): + @copy_current_request_context + def do_some_work(): + # do some work here, it can access flask.request or + # flask.session like you would otherwise in the view function. + ... + gevent.spawn(do_some_work) + return 'Regular response' + + .. versionadded:: 0.10 + """ + top = _request_ctx_stack.top + + if top is None: + raise RuntimeError( + "This decorator can only be used when a request context is" + " active, such as within a view function." + ) + + reqctx = top.copy() + + def wrapper(*args, **kwargs): + with reqctx: + return f(*args, **kwargs) + + return update_wrapper(wrapper, f) + + +def has_request_context() -> bool: + """If you have code that wants to test if a request context is there or + not this function can be used. For instance, you may want to take advantage + of request information if the request object is available, but fail + silently if it is unavailable. + + :: + + class User(db.Model): + + def __init__(self, username, remote_addr=None): + self.username = username + if remote_addr is None and has_request_context(): + remote_addr = request.remote_addr + self.remote_addr = remote_addr + + Alternatively you can also just test any of the context bound objects + (such as :class:`request` or :class:`g`) for truthness:: + + class User(db.Model): + + def __init__(self, username, remote_addr=None): + self.username = username + if remote_addr is None and request: + remote_addr = request.remote_addr + self.remote_addr = remote_addr + + .. versionadded:: 0.7 + """ + return _request_ctx_stack.top is not None + + +def has_app_context() -> bool: + """Works like :func:`has_request_context` but for the application + context. You can also just do a boolean check on the + :data:`current_app` object instead. + + .. versionadded:: 0.9 + """ + return _app_ctx_stack.top is not None + + +class AppContext: + """The application context binds an application object implicitly + to the current thread or greenlet, similar to how the + :class:`RequestContext` binds request information. The application + context is also implicitly created if a request context is created + but the application is not on top of the individual application + context. + """ + + def __init__(self, app: "Flask") -> None: + self.app = app + self.url_adapter = app.create_url_adapter(None) + self.g = app.app_ctx_globals_class() + + # Like request context, app contexts can be pushed multiple times + # but there a basic "refcount" is enough to track them. + self._refcnt = 0 + + def push(self) -> None: + """Binds the app context to the current context.""" + self._refcnt += 1 + _app_ctx_stack.push(self) + appcontext_pushed.send(self.app) + + def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore + """Pops the app context.""" + try: + self._refcnt -= 1 + if self._refcnt <= 0: + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_appcontext(exc) + finally: + rv = _app_ctx_stack.pop() + assert rv is self, f"Popped wrong app context. ({rv!r} instead of {self!r})" + appcontext_popped.send(self.app) + + def __enter__(self) -> "AppContext": + self.push() + return self + + def __exit__( + self, exc_type: type, exc_value: BaseException, tb: TracebackType + ) -> None: + self.pop(exc_value) + + +class RequestContext: + """The request context contains all request relevant information. It is + created at the beginning of the request and pushed to the + `_request_ctx_stack` and removed at the end of it. It will create the + URL adapter and request object for the WSGI environment provided. + + Do not attempt to use this class directly, instead use + :meth:`~flask.Flask.test_request_context` and + :meth:`~flask.Flask.request_context` to create this object. + + When the request context is popped, it will evaluate all the + functions registered on the application for teardown execution + (:meth:`~flask.Flask.teardown_request`). + + The request context is automatically popped at the end of the request + for you. In debug mode the request context is kept around if + exceptions happen so that interactive debuggers have a chance to + introspect the data. With 0.4 this can also be forced for requests + that did not fail and outside of ``DEBUG`` mode. By setting + ``'flask._preserve_context'`` to ``True`` on the WSGI environment the + context will not pop itself at the end of the request. This is used by + the :meth:`~flask.Flask.test_client` for example to implement the + deferred cleanup functionality. + + You might find this helpful for unittests where you need the + information from the context local around for a little longer. Make + sure to properly :meth:`~werkzeug.LocalStack.pop` the stack yourself in + that situation, otherwise your unittests will leak memory. + """ + + def __init__( + self, + app: "Flask", + environ: dict, + request: t.Optional["Request"] = None, + session: t.Optional["SessionMixin"] = None, + ) -> None: + self.app = app + if request is None: + request = app.request_class(environ) + self.request = request + self.url_adapter = None + try: + self.url_adapter = app.create_url_adapter(self.request) + except HTTPException as e: + self.request.routing_exception = e + self.flashes = None + self.session = session + + # Request contexts can be pushed multiple times and interleaved with + # other request contexts. Now only if the last level is popped we + # get rid of them. Additionally if an application context is missing + # one is created implicitly so for each level we add this information + self._implicit_app_ctx_stack: t.List[t.Optional["AppContext"]] = [] + + # indicator if the context was preserved. Next time another context + # is pushed the preserved context is popped. + self.preserved = False + + # remembers the exception for pop if there is one in case the context + # preservation kicks in. + self._preserved_exc = None + + # Functions that should be executed after the request on the response + # object. These will be called before the regular "after_request" + # functions. + self._after_request_functions: t.List[AfterRequestCallable] = [] + + @property + def g(self) -> AppContext: + return _app_ctx_stack.top.g + + @g.setter + def g(self, value: AppContext) -> None: + _app_ctx_stack.top.g = value + + def copy(self) -> "RequestContext": + """Creates a copy of this request context with the same request object. + This can be used to move a request context to a different greenlet. + Because the actual request object is the same this cannot be used to + move a request context to a different thread unless access to the + request object is locked. + + .. versionadded:: 0.10 + + .. versionchanged:: 1.1 + The current session object is used instead of reloading the original + data. This prevents `flask.session` pointing to an out-of-date object. + """ + return self.__class__( + self.app, + environ=self.request.environ, + request=self.request, + session=self.session, + ) + + def match_request(self) -> None: + """Can be overridden by a subclass to hook into the matching + of the request. + """ + try: + result = self.url_adapter.match(return_rule=True) # type: ignore + self.request.url_rule, self.request.view_args = result # type: ignore + except HTTPException as e: + self.request.routing_exception = e + + def push(self) -> None: + """Binds the request context to the current context.""" + # If an exception occurs in debug mode or if context preservation is + # activated under exception situations exactly one context stays + # on the stack. The rationale is that you want to access that + # information under debug situations. However if someone forgets to + # pop that context again we want to make sure that on the next push + # it's invalidated, otherwise we run at risk that something leaks + # memory. This is usually only a problem in test suite since this + # functionality is not active in production environments. + top = _request_ctx_stack.top + if top is not None and top.preserved: + top.pop(top._preserved_exc) + + # Before we push the request context we have to ensure that there + # is an application context. + app_ctx = _app_ctx_stack.top + if app_ctx is None or app_ctx.app != self.app: + app_ctx = self.app.app_context() + app_ctx.push() + self._implicit_app_ctx_stack.append(app_ctx) + else: + self._implicit_app_ctx_stack.append(None) + + _request_ctx_stack.push(self) + + # Open the session at the moment that the request context is available. + # This allows a custom open_session method to use the request context. + # Only open a new session if this is the first time the request was + # pushed, otherwise stream_with_context loses the session. + if self.session is None: + session_interface = self.app.session_interface + self.session = session_interface.open_session(self.app, self.request) + + if self.session is None: + self.session = session_interface.make_null_session(self.app) + + # Match the request URL after loading the session, so that the + # session is available in custom URL converters. + if self.url_adapter is not None: + self.match_request() + + def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore + """Pops the request context and unbinds it by doing that. This will + also trigger the execution of functions registered by the + :meth:`~flask.Flask.teardown_request` decorator. + + .. versionchanged:: 0.9 + Added the `exc` argument. + """ + app_ctx = self._implicit_app_ctx_stack.pop() + clear_request = False + + try: + if not self._implicit_app_ctx_stack: + self.preserved = False + self._preserved_exc = None + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_request(exc) + + request_close = getattr(self.request, "close", None) + if request_close is not None: + request_close() + clear_request = True + finally: + rv = _request_ctx_stack.pop() + + # get rid of circular dependencies at the end of the request + # so that we don't require the GC to be active. + if clear_request: + rv.request.environ["werkzeug.request"] = None + + # Get rid of the app as well if necessary. + if app_ctx is not None: + app_ctx.pop(exc) + + assert ( + rv is self + ), f"Popped wrong request context. ({rv!r} instead of {self!r})" + + def auto_pop(self, exc: t.Optional[BaseException]) -> None: + if self.request.environ.get("flask._preserve_context") or ( + exc is not None and self.app.preserve_context_on_exception + ): + self.preserved = True + self._preserved_exc = exc # type: ignore + else: + self.pop(exc) + + def __enter__(self) -> "RequestContext": + self.push() + return self + + def __exit__( + self, exc_type: type, exc_value: BaseException, tb: TracebackType + ) -> None: + # do not pop the request stack if we are in debug mode and an + # exception happened. This will allow the debugger to still + # access the request object in the interactive shell. Furthermore + # the context can be force kept alive for the test client. + # See flask.testing for how this works. + self.auto_pop(exc_value) + + def __repr__(self) -> str: + return ( + f"<{type(self).__name__} {self.request.url!r}" + f" [{self.request.method}] of {self.app.name}>" + ) diff --git a/myvenv/lib/python3.10/site-packages/flask/debughelpers.py b/myvenv/lib/python3.10/site-packages/flask/debughelpers.py new file mode 100644 index 0000000..212f7d7 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/flask/debughelpers.py @@ -0,0 +1,172 @@ +import os +import typing as t +from warnings import warn + +from .app import Flask +from .blueprints import Blueprint +from .globals import _request_ctx_stack + + +class UnexpectedUnicodeError(AssertionError, UnicodeError): + """Raised in places where we want some better error reporting for + unexpected unicode or binary data. + """ + + +class DebugFilesKeyError(KeyError, AssertionError): + """Raised from request.files during debugging. The idea is that it can + provide a better error message than just a generic KeyError/BadRequest. + """ + + def __init__(self, request, key): + form_matches = request.form.getlist(key) + buf = [ + f"You tried to access the file {key!r} in the request.files" + " dictionary but it does not exist. The mimetype for the" + f" request is {request.mimetype!r} instead of" + " 'multipart/form-data' which means that no file contents" + " were transmitted. To fix this error you should provide" + ' enctype="multipart/form-data" in your form.' + ] + if form_matches: + names = ", ".join(repr(x) for x in form_matches) + buf.append( + "\n\nThe browser instead transmitted some file names. " + f"This was submitted: {names}" + ) + self.msg = "".join(buf) + + def __str__(self): + return self.msg + + +class FormDataRoutingRedirect(AssertionError): + """This exception is raised by Flask in debug mode if it detects a + redirect caused by the routing system when the request method is not + GET, HEAD or OPTIONS. Reasoning: form data will be dropped. + """ + + def __init__(self, request): + exc = request.routing_exception + buf = [ + f"A request was sent to this URL ({request.url}) but a" + " redirect was issued automatically by the routing system" + f" to {exc.new_url!r}." + ] + + # In case just a slash was appended we can be extra helpful + if f"{request.base_url}/" == exc.new_url.split("?")[0]: + buf.append( + " The URL was defined with a trailing slash so Flask" + " will automatically redirect to the URL with the" + " trailing slash if it was accessed without one." + ) + + buf.append( + " Make sure to directly send your" + f" {request.method}-request to this URL since we can't make" + " browsers or HTTP clients redirect with form data reliably" + " or without user interaction." + ) + buf.append("\n\nNote: this exception is only raised in debug mode") + AssertionError.__init__(self, "".join(buf).encode("utf-8")) + + +def attach_enctype_error_multidict(request): + """Since Flask 0.8 we're monkeypatching the files object in case a + request is detected that does not use multipart form data but the files + object is accessed. + """ + oldcls = request.files.__class__ + + class newcls(oldcls): + def __getitem__(self, key): + try: + return oldcls.__getitem__(self, key) + except KeyError as e: + if key not in request.form: + raise + + raise DebugFilesKeyError(request, key) from e + + newcls.__name__ = oldcls.__name__ + newcls.__module__ = oldcls.__module__ + request.files.__class__ = newcls + + +def _dump_loader_info(loader) -> t.Generator: + yield f"class: {type(loader).__module__}.{type(loader).__name__}" + for key, value in sorted(loader.__dict__.items()): + if key.startswith("_"): + continue + if isinstance(value, (tuple, list)): + if not all(isinstance(x, str) for x in value): + continue + yield f"{key}:" + for item in value: + yield f" - {item}" + continue + elif not isinstance(value, (str, int, float, bool)): + continue + yield f"{key}: {value!r}" + + +def explain_template_loading_attempts(app: Flask, template, attempts) -> None: + """This should help developers understand what failed""" + info = [f"Locating template {template!r}:"] + total_found = 0 + blueprint = None + reqctx = _request_ctx_stack.top + if reqctx is not None and reqctx.request.blueprint is not None: + blueprint = reqctx.request.blueprint + + for idx, (loader, srcobj, triple) in enumerate(attempts): + if isinstance(srcobj, Flask): + src_info = f"application {srcobj.import_name!r}" + elif isinstance(srcobj, Blueprint): + src_info = f"blueprint {srcobj.name!r} ({srcobj.import_name})" + else: + src_info = repr(srcobj) + + info.append(f"{idx + 1:5}: trying loader of {src_info}") + + for line in _dump_loader_info(loader): + info.append(f" {line}") + + if triple is None: + detail = "no match" + else: + detail = f"found ({triple[1] or ''!r})" + total_found += 1 + info.append(f" -> {detail}") + + seems_fishy = False + if total_found == 0: + info.append("Error: the template could not be found.") + seems_fishy = True + elif total_found > 1: + info.append("Warning: multiple loaders returned a match for the template.") + seems_fishy = True + + if blueprint is not None and seems_fishy: + info.append( + " The template was looked up from an endpoint that belongs" + f" to the blueprint {blueprint!r}." + ) + info.append(" Maybe you did not place a template in the right folder?") + info.append(" See https://flask.palletsprojects.com/blueprints/#templates") + + app.logger.info("\n".join(info)) + + +def explain_ignored_app_run() -> None: + if os.environ.get("WERKZEUG_RUN_MAIN") != "true": + warn( + Warning( + "Silently ignoring app.run() because the application is" + " run from the flask command line executable. Consider" + ' putting app.run() behind an if __name__ == "__main__"' + " guard to silence this warning." + ), + stacklevel=3, + ) diff --git a/myvenv/lib/python3.10/site-packages/flask/globals.py b/myvenv/lib/python3.10/site-packages/flask/globals.py new file mode 100644 index 0000000..6d91c75 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/flask/globals.py @@ -0,0 +1,59 @@ +import typing as t +from functools import partial + +from werkzeug.local import LocalProxy +from werkzeug.local import LocalStack + +if t.TYPE_CHECKING: + from .app import Flask + from .ctx import _AppCtxGlobals + from .sessions import SessionMixin + from .wrappers import Request + +_request_ctx_err_msg = """\ +Working outside of request context. + +This typically means that you attempted to use functionality that needed +an active HTTP request. Consult the documentation on testing for +information about how to avoid this problem.\ +""" +_app_ctx_err_msg = """\ +Working outside of application context. + +This typically means that you attempted to use functionality that needed +to interface with the current application object in some way. To solve +this, set up an application context with app.app_context(). See the +documentation for more information.\ +""" + + +def _lookup_req_object(name): + top = _request_ctx_stack.top + if top is None: + raise RuntimeError(_request_ctx_err_msg) + return getattr(top, name) + + +def _lookup_app_object(name): + top = _app_ctx_stack.top + if top is None: + raise RuntimeError(_app_ctx_err_msg) + return getattr(top, name) + + +def _find_app(): + top = _app_ctx_stack.top + if top is None: + raise RuntimeError(_app_ctx_err_msg) + return top.app + + +# context locals +_request_ctx_stack = LocalStack() +_app_ctx_stack = LocalStack() +current_app: "Flask" = LocalProxy(_find_app) # type: ignore +request: "Request" = LocalProxy(partial(_lookup_req_object, "request")) # type: ignore +session: "SessionMixin" = LocalProxy( # type: ignore + partial(_lookup_req_object, "session") +) +g: "_AppCtxGlobals" = LocalProxy(partial(_lookup_app_object, "g")) # type: ignore diff --git a/myvenv/lib/python3.10/site-packages/flask/helpers.py b/myvenv/lib/python3.10/site-packages/flask/helpers.py new file mode 100644 index 0000000..4359780 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/flask/helpers.py @@ -0,0 +1,836 @@ +import os +import pkgutil +import socket +import sys +import typing as t +import warnings +from datetime import datetime +from datetime import timedelta +from functools import lru_cache +from functools import update_wrapper +from threading import RLock + +import werkzeug.utils +from werkzeug.exceptions import NotFound +from werkzeug.routing import BuildError +from werkzeug.urls import url_quote + +from .globals import _app_ctx_stack +from .globals import _request_ctx_stack +from .globals import current_app +from .globals import request +from .globals import session +from .signals import message_flashed + +if t.TYPE_CHECKING: + from .wrappers import Response + + +def get_env() -> str: + """Get the environment the app is running in, indicated by the + :envvar:`FLASK_ENV` environment variable. The default is + ``'production'``. + """ + return os.environ.get("FLASK_ENV") or "production" + + +def get_debug_flag() -> bool: + """Get whether debug mode should be enabled for the app, indicated + by the :envvar:`FLASK_DEBUG` environment variable. The default is + ``True`` if :func:`.get_env` returns ``'development'``, or ``False`` + otherwise. + """ + val = os.environ.get("FLASK_DEBUG") + + if not val: + return get_env() == "development" + + return val.lower() not in ("0", "false", "no") + + +def get_load_dotenv(default: bool = True) -> bool: + """Get whether the user has disabled loading dotenv files by setting + :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the + files. + + :param default: What to return if the env var isn't set. + """ + val = os.environ.get("FLASK_SKIP_DOTENV") + + if not val: + return default + + return val.lower() in ("0", "false", "no") + + +def stream_with_context( + generator_or_function: t.Union[ + t.Iterator[t.AnyStr], t.Callable[..., t.Iterator[t.AnyStr]] + ] +) -> t.Iterator[t.AnyStr]: + """Request contexts disappear when the response is started on the server. + This is done for efficiency reasons and to make it less likely to encounter + memory leaks with badly written WSGI middlewares. The downside is that if + you are using streamed responses, the generator cannot access request bound + information any more. + + This function however can help you keep the context around for longer:: + + from flask import stream_with_context, request, Response + + @app.route('/stream') + def streamed_response(): + @stream_with_context + def generate(): + yield 'Hello ' + yield request.args['name'] + yield '!' + return Response(generate()) + + Alternatively it can also be used around a specific generator:: + + from flask import stream_with_context, request, Response + + @app.route('/stream') + def streamed_response(): + def generate(): + yield 'Hello ' + yield request.args['name'] + yield '!' + return Response(stream_with_context(generate())) + + .. versionadded:: 0.9 + """ + try: + gen = iter(generator_or_function) # type: ignore + except TypeError: + + def decorator(*args: t.Any, **kwargs: t.Any) -> t.Any: + gen = generator_or_function(*args, **kwargs) # type: ignore + return stream_with_context(gen) + + return update_wrapper(decorator, generator_or_function) # type: ignore + + def generator() -> t.Generator: + ctx = _request_ctx_stack.top + if ctx is None: + raise RuntimeError( + "Attempted to stream with context but " + "there was no context in the first place to keep around." + ) + with ctx: + # Dummy sentinel. Has to be inside the context block or we're + # not actually keeping the context around. + yield None + + # The try/finally is here so that if someone passes a WSGI level + # iterator in we're still running the cleanup logic. Generators + # don't need that because they are closed on their destruction + # automatically. + try: + yield from gen + finally: + if hasattr(gen, "close"): + gen.close() # type: ignore + + # The trick is to start the generator. Then the code execution runs until + # the first dummy None is yielded at which point the context was already + # pushed. This item is discarded. Then when the iteration continues the + # real generator is executed. + wrapped_g = generator() + next(wrapped_g) + return wrapped_g + + +def make_response(*args: t.Any) -> "Response": + """Sometimes it is necessary to set additional headers in a view. Because + views do not have to return response objects but can return a value that + is converted into a response object by Flask itself, it becomes tricky to + add headers to it. This function can be called instead of using a return + and you will get a response object which you can use to attach headers. + + If view looked like this and you want to add a new header:: + + def index(): + return render_template('index.html', foo=42) + + You can now do something like this:: + + def index(): + response = make_response(render_template('index.html', foo=42)) + response.headers['X-Parachutes'] = 'parachutes are cool' + return response + + This function accepts the very same arguments you can return from a + view function. This for example creates a response with a 404 error + code:: + + response = make_response(render_template('not_found.html'), 404) + + The other use case of this function is to force the return value of a + view function into a response which is helpful with view + decorators:: + + response = make_response(view_function()) + response.headers['X-Parachutes'] = 'parachutes are cool' + + Internally this function does the following things: + + - if no arguments are passed, it creates a new response argument + - if one argument is passed, :meth:`flask.Flask.make_response` + is invoked with it. + - if more than one argument is passed, the arguments are passed + to the :meth:`flask.Flask.make_response` function as tuple. + + .. versionadded:: 0.6 + """ + if not args: + return current_app.response_class() + if len(args) == 1: + args = args[0] + return current_app.make_response(args) + + +def url_for(endpoint: str, **values: t.Any) -> str: + """Generates a URL to the given endpoint with the method provided. + + Variable arguments that are unknown to the target endpoint are appended + to the generated URL as query arguments. If the value of a query argument + is ``None``, the whole pair is skipped. In case blueprints are active + you can shortcut references to the same blueprint by prefixing the + local endpoint with a dot (``.``). + + This will reference the index function local to the current blueprint:: + + url_for('.index') + + See :ref:`url-building`. + + Configuration values ``APPLICATION_ROOT`` and ``SERVER_NAME`` are only used when + generating URLs outside of a request context. + + To integrate applications, :class:`Flask` has a hook to intercept URL build + errors through :attr:`Flask.url_build_error_handlers`. The `url_for` + function results in a :exc:`~werkzeug.routing.BuildError` when the current + app does not have a URL for the given endpoint and values. When it does, the + :data:`~flask.current_app` calls its :attr:`~Flask.url_build_error_handlers` if + it is not ``None``, which can return a string to use as the result of + `url_for` (instead of `url_for`'s default to raise the + :exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception. + An example:: + + def external_url_handler(error, endpoint, values): + "Looks up an external URL when `url_for` cannot build a URL." + # This is an example of hooking the build_error_handler. + # Here, lookup_url is some utility function you've built + # which looks up the endpoint in some external URL registry. + url = lookup_url(endpoint, **values) + if url is None: + # External lookup did not have a URL. + # Re-raise the BuildError, in context of original traceback. + exc_type, exc_value, tb = sys.exc_info() + if exc_value is error: + raise exc_type(exc_value).with_traceback(tb) + else: + raise error + # url_for will use this result, instead of raising BuildError. + return url + + app.url_build_error_handlers.append(external_url_handler) + + Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and + `endpoint` and `values` are the arguments passed into `url_for`. Note + that this is for building URLs outside the current application, and not for + handling 404 NotFound errors. + + .. versionadded:: 0.10 + The `_scheme` parameter was added. + + .. versionadded:: 0.9 + The `_anchor` and `_method` parameters were added. + + .. versionadded:: 0.9 + Calls :meth:`Flask.handle_build_error` on + :exc:`~werkzeug.routing.BuildError`. + + :param endpoint: the endpoint of the URL (name of the function) + :param values: the variable arguments of the URL rule + :param _external: if set to ``True``, an absolute URL is generated. Server + address can be changed via ``SERVER_NAME`` configuration variable which + falls back to the `Host` header, then to the IP and port of the request. + :param _scheme: a string specifying the desired URL scheme. The `_external` + parameter must be set to ``True`` or a :exc:`ValueError` is raised. The default + behavior uses the same scheme as the current request, or + :data:`PREFERRED_URL_SCHEME` if no request context is available. + This also can be set to an empty string to build protocol-relative + URLs. + :param _anchor: if provided this is added as anchor to the URL. + :param _method: if provided this explicitly specifies an HTTP method. + """ + appctx = _app_ctx_stack.top + reqctx = _request_ctx_stack.top + + if appctx is None: + raise RuntimeError( + "Attempted to generate a URL without the application context being" + " pushed. This has to be executed when application context is" + " available." + ) + + # If request specific information is available we have some extra + # features that support "relative" URLs. + if reqctx is not None: + url_adapter = reqctx.url_adapter + blueprint_name = request.blueprint + + if endpoint[:1] == ".": + if blueprint_name is not None: + endpoint = f"{blueprint_name}{endpoint}" + else: + endpoint = endpoint[1:] + + external = values.pop("_external", False) + + # Otherwise go with the url adapter from the appctx and make + # the URLs external by default. + else: + url_adapter = appctx.url_adapter + + if url_adapter is None: + raise RuntimeError( + "Application was not able to create a URL adapter for request" + " independent URL generation. You might be able to fix this by" + " setting the SERVER_NAME config variable." + ) + + external = values.pop("_external", True) + + anchor = values.pop("_anchor", None) + method = values.pop("_method", None) + scheme = values.pop("_scheme", None) + appctx.app.inject_url_defaults(endpoint, values) + + # This is not the best way to deal with this but currently the + # underlying Werkzeug router does not support overriding the scheme on + # a per build call basis. + old_scheme = None + if scheme is not None: + if not external: + raise ValueError("When specifying _scheme, _external must be True") + old_scheme = url_adapter.url_scheme + url_adapter.url_scheme = scheme + + try: + try: + rv = url_adapter.build( + endpoint, values, method=method, force_external=external + ) + finally: + if old_scheme is not None: + url_adapter.url_scheme = old_scheme + except BuildError as error: + # We need to inject the values again so that the app callback can + # deal with that sort of stuff. + values["_external"] = external + values["_anchor"] = anchor + values["_method"] = method + values["_scheme"] = scheme + return appctx.app.handle_url_build_error(error, endpoint, values) + + if anchor is not None: + rv += f"#{url_quote(anchor)}" + return rv + + +def get_template_attribute(template_name: str, attribute: str) -> t.Any: + """Loads a macro (or variable) a template exports. This can be used to + invoke a macro from within Python code. If you for example have a + template named :file:`_cider.html` with the following contents: + + .. sourcecode:: html+jinja + + {% macro hello(name) %}Hello {{ name }}!{% endmacro %} + + You can access this from Python code like this:: + + hello = get_template_attribute('_cider.html', 'hello') + return hello('World') + + .. versionadded:: 0.2 + + :param template_name: the name of the template + :param attribute: the name of the variable of macro to access + """ + return getattr(current_app.jinja_env.get_template(template_name).module, attribute) + + +def flash(message: str, category: str = "message") -> None: + """Flashes a message to the next request. In order to remove the + flashed message from the session and to display it to the user, + the template has to call :func:`get_flashed_messages`. + + .. versionchanged:: 0.3 + `category` parameter added. + + :param message: the message to be flashed. + :param category: the category for the message. The following values + are recommended: ``'message'`` for any kind of message, + ``'error'`` for errors, ``'info'`` for information + messages and ``'warning'`` for warnings. However any + kind of string can be used as category. + """ + # Original implementation: + # + # session.setdefault('_flashes', []).append((category, message)) + # + # This assumed that changes made to mutable structures in the session are + # always in sync with the session object, which is not true for session + # implementations that use external storage for keeping their keys/values. + flashes = session.get("_flashes", []) + flashes.append((category, message)) + session["_flashes"] = flashes + message_flashed.send( + current_app._get_current_object(), # type: ignore + message=message, + category=category, + ) + + +def get_flashed_messages( + with_categories: bool = False, category_filter: t.Iterable[str] = () +) -> t.Union[t.List[str], t.List[t.Tuple[str, str]]]: + """Pulls all flashed messages from the session and returns them. + Further calls in the same request to the function will return + the same messages. By default just the messages are returned, + but when `with_categories` is set to ``True``, the return value will + be a list of tuples in the form ``(category, message)`` instead. + + Filter the flashed messages to one or more categories by providing those + categories in `category_filter`. This allows rendering categories in + separate html blocks. The `with_categories` and `category_filter` + arguments are distinct: + + * `with_categories` controls whether categories are returned with message + text (``True`` gives a tuple, where ``False`` gives just the message text). + * `category_filter` filters the messages down to only those matching the + provided categories. + + See :doc:`/patterns/flashing` for examples. + + .. versionchanged:: 0.3 + `with_categories` parameter added. + + .. versionchanged:: 0.9 + `category_filter` parameter added. + + :param with_categories: set to ``True`` to also receive categories. + :param category_filter: filter of categories to limit return values. Only + categories in the list will be returned. + """ + flashes = _request_ctx_stack.top.flashes + if flashes is None: + _request_ctx_stack.top.flashes = flashes = ( + session.pop("_flashes") if "_flashes" in session else [] + ) + if category_filter: + flashes = list(filter(lambda f: f[0] in category_filter, flashes)) + if not with_categories: + return [x[1] for x in flashes] + return flashes + + +def _prepare_send_file_kwargs( + download_name: t.Optional[str] = None, + attachment_filename: t.Optional[str] = None, + etag: t.Optional[t.Union[bool, str]] = None, + add_etags: t.Optional[t.Union[bool]] = None, + max_age: t.Optional[ + t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]] + ] = None, + cache_timeout: t.Optional[int] = None, + **kwargs: t.Any, +) -> t.Dict[str, t.Any]: + if attachment_filename is not None: + warnings.warn( + "The 'attachment_filename' parameter has been renamed to" + " 'download_name'. The old name will be removed in Flask" + " 2.1.", + DeprecationWarning, + stacklevel=3, + ) + download_name = attachment_filename + + if cache_timeout is not None: + warnings.warn( + "The 'cache_timeout' parameter has been renamed to" + " 'max_age'. The old name will be removed in Flask 2.1.", + DeprecationWarning, + stacklevel=3, + ) + max_age = cache_timeout + + if add_etags is not None: + warnings.warn( + "The 'add_etags' parameter has been renamed to 'etag'. The" + " old name will be removed in Flask 2.1.", + DeprecationWarning, + stacklevel=3, + ) + etag = add_etags + + if max_age is None: + max_age = current_app.get_send_file_max_age + + kwargs.update( + environ=request.environ, + download_name=download_name, + etag=etag, + max_age=max_age, + use_x_sendfile=current_app.use_x_sendfile, + response_class=current_app.response_class, + _root_path=current_app.root_path, # type: ignore + ) + return kwargs + + +def send_file( + path_or_file: t.Union[os.PathLike, str, t.BinaryIO], + mimetype: t.Optional[str] = None, + as_attachment: bool = False, + download_name: t.Optional[str] = None, + attachment_filename: t.Optional[str] = None, + conditional: bool = True, + etag: t.Union[bool, str] = True, + add_etags: t.Optional[bool] = None, + last_modified: t.Optional[t.Union[datetime, int, float]] = None, + max_age: t.Optional[ + t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]] + ] = None, + cache_timeout: t.Optional[int] = None, +): + """Send the contents of a file to the client. + + The first argument can be a file path or a file-like object. Paths + are preferred in most cases because Werkzeug can manage the file and + get extra information from the path. Passing a file-like object + requires that the file is opened in binary mode, and is mostly + useful when building a file in memory with :class:`io.BytesIO`. + + Never pass file paths provided by a user. The path is assumed to be + trusted, so a user could craft a path to access a file you didn't + intend. Use :func:`send_from_directory` to safely serve + user-requested paths from within a directory. + + If the WSGI server sets a ``file_wrapper`` in ``environ``, it is + used, otherwise Werkzeug's built-in wrapper is used. Alternatively, + if the HTTP server supports ``X-Sendfile``, configuring Flask with + ``USE_X_SENDFILE = True`` will tell the server to send the given + path, which is much more efficient than reading it in Python. + + :param path_or_file: The path to the file to send, relative to the + current working directory if a relative path is given. + Alternatively, a file-like object opened in binary mode. Make + sure the file pointer is seeked to the start of the data. + :param mimetype: The MIME type to send for the file. If not + provided, it will try to detect it from the file name. + :param as_attachment: Indicate to a browser that it should offer to + save the file instead of displaying it. + :param download_name: The default name browsers will use when saving + the file. Defaults to the passed file name. + :param conditional: Enable conditional and range responses based on + request headers. Requires passing a file path and ``environ``. + :param etag: Calculate an ETag for the file, which requires passing + a file path. Can also be a string to use instead. + :param last_modified: The last modified time to send for the file, + in seconds. If not provided, it will try to detect it from the + file path. + :param max_age: How long the client should cache the file, in + seconds. If set, ``Cache-Control`` will be ``public``, otherwise + it will be ``no-cache`` to prefer conditional caching. + + .. versionchanged:: 2.0 + ``download_name`` replaces the ``attachment_filename`` + parameter. If ``as_attachment=False``, it is passed with + ``Content-Disposition: inline`` instead. + + .. versionchanged:: 2.0 + ``max_age`` replaces the ``cache_timeout`` parameter. + ``conditional`` is enabled and ``max_age`` is not set by + default. + + .. versionchanged:: 2.0 + ``etag`` replaces the ``add_etags`` parameter. It can be a + string to use instead of generating one. + + .. versionchanged:: 2.0 + Passing a file-like object that inherits from + :class:`~io.TextIOBase` will raise a :exc:`ValueError` rather + than sending an empty file. + + .. versionadded:: 2.0 + Moved the implementation to Werkzeug. This is now a wrapper to + pass some Flask-specific arguments. + + .. versionchanged:: 1.1 + ``filename`` may be a :class:`~os.PathLike` object. + + .. versionchanged:: 1.1 + Passing a :class:`~io.BytesIO` object supports range requests. + + .. versionchanged:: 1.0.3 + Filenames are encoded with ASCII instead of Latin-1 for broader + compatibility with WSGI servers. + + .. versionchanged:: 1.0 + UTF-8 filenames as specified in :rfc:`2231` are supported. + + .. versionchanged:: 0.12 + The filename is no longer automatically inferred from file + objects. If you want to use automatic MIME and etag support, + pass a filename via ``filename_or_fp`` or + ``attachment_filename``. + + .. versionchanged:: 0.12 + ``attachment_filename`` is preferred over ``filename`` for MIME + detection. + + .. versionchanged:: 0.9 + ``cache_timeout`` defaults to + :meth:`Flask.get_send_file_max_age`. + + .. versionchanged:: 0.7 + MIME guessing and etag support for file-like objects was + deprecated because it was unreliable. Pass a filename if you are + able to, otherwise attach an etag yourself. + + .. versionchanged:: 0.5 + The ``add_etags``, ``cache_timeout`` and ``conditional`` + parameters were added. The default behavior is to add etags. + + .. versionadded:: 0.2 + """ + return werkzeug.utils.send_file( + **_prepare_send_file_kwargs( + path_or_file=path_or_file, + environ=request.environ, + mimetype=mimetype, + as_attachment=as_attachment, + download_name=download_name, + attachment_filename=attachment_filename, + conditional=conditional, + etag=etag, + add_etags=add_etags, + last_modified=last_modified, + max_age=max_age, + cache_timeout=cache_timeout, + ) + ) + + +def safe_join(directory: str, *pathnames: str) -> str: + """Safely join zero or more untrusted path components to a base + directory to avoid escaping the base directory. + + :param directory: The trusted base directory. + :param pathnames: The untrusted path components relative to the + base directory. + :return: A safe path, otherwise ``None``. + """ + warnings.warn( + "'flask.helpers.safe_join' is deprecated and will be removed in" + " Flask 2.1. Use 'werkzeug.utils.safe_join' instead.", + DeprecationWarning, + stacklevel=2, + ) + path = werkzeug.utils.safe_join(directory, *pathnames) + + if path is None: + raise NotFound() + + return path + + +def send_from_directory( + directory: t.Union[os.PathLike, str], + path: t.Union[os.PathLike, str], + filename: t.Optional[str] = None, + **kwargs: t.Any, +) -> "Response": + """Send a file from within a directory using :func:`send_file`. + + .. code-block:: python + + @app.route("/uploads/") + def download_file(name): + return send_from_directory( + app.config['UPLOAD_FOLDER'], name, as_attachment=True + ) + + This is a secure way to serve files from a folder, such as static + files or uploads. Uses :func:`~werkzeug.security.safe_join` to + ensure the path coming from the client is not maliciously crafted to + point outside the specified directory. + + If the final path does not point to an existing regular file, + raises a 404 :exc:`~werkzeug.exceptions.NotFound` error. + + :param directory: The directory that ``path`` must be located under. + :param path: The path to the file to send, relative to + ``directory``. + :param kwargs: Arguments to pass to :func:`send_file`. + + .. versionchanged:: 2.0 + ``path`` replaces the ``filename`` parameter. + + .. versionadded:: 2.0 + Moved the implementation to Werkzeug. This is now a wrapper to + pass some Flask-specific arguments. + + .. versionadded:: 0.5 + """ + if filename is not None: + warnings.warn( + "The 'filename' parameter has been renamed to 'path'. The" + " old name will be removed in Flask 2.1.", + DeprecationWarning, + stacklevel=2, + ) + path = filename + + return werkzeug.utils.send_from_directory( # type: ignore + directory, path, **_prepare_send_file_kwargs(**kwargs) + ) + + +def get_root_path(import_name: str) -> str: + """Find the root path of a package, or the path that contains a + module. If it cannot be found, returns the current working + directory. + + Not to be confused with the value returned by :func:`find_package`. + + :meta private: + """ + # Module already imported and has a file attribute. Use that first. + mod = sys.modules.get(import_name) + + if mod is not None and hasattr(mod, "__file__") 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) # type: ignore + else: + # Fall back to imports. + __import__(import_name) + mod = sys.modules[import_name] + filepath = getattr(mod, "__file__", None) + + # If we don't have a file path it might be because it is a + # namespace package. In this case pick the root path from the + # first module that is contained in the package. + if filepath is None: + raise RuntimeError( + "No root path can be found for the provided module" + f" {import_name!r}. This can happen because the module" + " came from an import hook that does not provide file" + " name information or because it's a namespace package." + " In this case the root path needs to be explicitly" + " provided." + ) + + # filepath is import_name.py for a module, or __init__.py for a package. + return os.path.dirname(os.path.abspath(filepath)) + + +class locked_cached_property(werkzeug.utils.cached_property): + """A :func:`property` that is only evaluated once. Like + :class:`werkzeug.utils.cached_property` except access uses a lock + for thread safety. + + .. versionchanged:: 2.0 + Inherits from Werkzeug's ``cached_property`` (and ``property``). + """ + + def __init__( + self, + fget: t.Callable[[t.Any], t.Any], + name: t.Optional[str] = None, + doc: t.Optional[str] = None, + ) -> None: + super().__init__(fget, name=name, doc=doc) + self.lock = RLock() + + def __get__(self, obj: object, type: type = None) -> t.Any: # type: ignore + if obj is None: + return self + + with self.lock: + return super().__get__(obj, type=type) + + def __set__(self, obj: object, value: t.Any) -> None: + with self.lock: + super().__set__(obj, value) + + def __delete__(self, obj: object) -> None: + with self.lock: + super().__delete__(obj) + + +def total_seconds(td: timedelta) -> int: + """Returns the total seconds from a timedelta object. + + :param timedelta td: the timedelta to be converted in seconds + + :returns: number of seconds + :rtype: int + + .. deprecated:: 2.0 + Will be removed in Flask 2.1. Use + :meth:`timedelta.total_seconds` instead. + """ + warnings.warn( + "'total_seconds' is deprecated and will be removed in Flask" + " 2.1. Use 'timedelta.total_seconds' instead.", + DeprecationWarning, + stacklevel=2, + ) + return td.days * 60 * 60 * 24 + td.seconds + + +def is_ip(value: str) -> bool: + """Determine if the given string is an IP address. + + :param value: value to check + :type value: str + + :return: True if string is an IP address + :rtype: bool + """ + for family in (socket.AF_INET, socket.AF_INET6): + try: + socket.inet_pton(family, value) + except OSError: + pass + else: + return True + + return False + + +@lru_cache(maxsize=None) +def _split_blueprint_path(name: str) -> t.List[str]: + out: t.List[str] = [name] + + if "." in name: + out.extend(_split_blueprint_path(name.rpartition(".")[0])) + + return out diff --git a/myvenv/lib/python3.10/site-packages/flask/json/__init__.py b/myvenv/lib/python3.10/site-packages/flask/json/__init__.py new file mode 100644 index 0000000..ccb9efb --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/flask/json/__init__.py @@ -0,0 +1,363 @@ +import decimal +import io +import json as _json +import typing as t +import uuid +import warnings +from datetime import date + +from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps +from werkzeug.http import http_date + +from ..globals import current_app +from ..globals import request + +if t.TYPE_CHECKING: + from ..app import Flask + from ..wrappers import Response + +try: + import dataclasses +except ImportError: + # Python < 3.7 + dataclasses = None # type: ignore + + +class JSONEncoder(_json.JSONEncoder): + """The default JSON encoder. Handles extra types compared to the + built-in :class:`json.JSONEncoder`. + + - :class:`datetime.datetime` and :class:`datetime.date` are + serialized to :rfc:`822` strings. This is the same as the HTTP + date format. + - :class:`uuid.UUID` is serialized to a string. + - :class:`dataclasses.dataclass` is passed to + :func:`dataclasses.asdict`. + - :class:`~markupsafe.Markup` (or any object with a ``__html__`` + method) will call the ``__html__`` method to get a string. + + Assign a subclass of this to :attr:`flask.Flask.json_encoder` or + :attr:`flask.Blueprint.json_encoder` to override the default. + """ + + def default(self, o: t.Any) -> t.Any: + """Convert ``o`` to a JSON serializable type. See + :meth:`json.JSONEncoder.default`. Python does not support + overriding how basic types like ``str`` or ``list`` are + serialized, they are handled before this method. + """ + if isinstance(o, date): + return http_date(o) + if isinstance(o, (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__()) + return super().default(o) + + +class JSONDecoder(_json.JSONDecoder): + """The default JSON decoder. + + This does not change any behavior from the built-in + :class:`json.JSONDecoder`. + + Assign a subclass of this to :attr:`flask.Flask.json_decoder` or + :attr:`flask.Blueprint.json_decoder` to override the default. + """ + + +def _dump_arg_defaults( + kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None +) -> None: + """Inject default arguments for dump functions.""" + if app is None: + app = current_app + + if app: + cls = app.json_encoder + bp = app.blueprints.get(request.blueprint) if request else None # type: ignore + if bp is not None and bp.json_encoder is not None: + cls = bp.json_encoder + + # Only set a custom encoder if it has custom behavior. This is + # faster on PyPy. + if cls is not _json.JSONEncoder: + kwargs.setdefault("cls", cls) + + kwargs.setdefault("cls", cls) + kwargs.setdefault("ensure_ascii", app.config["JSON_AS_ASCII"]) + kwargs.setdefault("sort_keys", app.config["JSON_SORT_KEYS"]) + else: + kwargs.setdefault("sort_keys", True) + kwargs.setdefault("cls", JSONEncoder) + + +def _load_arg_defaults( + kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None +) -> None: + """Inject default arguments for load functions.""" + if app is None: + app = current_app + + if app: + cls = app.json_decoder + bp = app.blueprints.get(request.blueprint) if request else None # type: ignore + if bp is not None and bp.json_decoder is not None: + cls = bp.json_decoder + + # Only set a custom decoder if it has custom behavior. This is + # faster on PyPy. + if cls not in {JSONDecoder, _json.JSONDecoder}: + kwargs.setdefault("cls", cls) + + +def dumps(obj: t.Any, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> str: + """Serialize an object to a string of JSON. + + Takes the same arguments as the built-in :func:`json.dumps`, with + some defaults from application configuration. + + :param obj: Object to serialize to JSON. + :param app: Use this app's config instead of the active app context + or defaults. + :param kwargs: Extra arguments passed to :func:`json.dumps`. + + .. versionchanged:: 2.0.2 + :class:`decimal.Decimal` is supported by converting to a string. + + .. versionchanged:: 2.0 + ``encoding`` is deprecated and will be removed in Flask 2.1. + + .. versionchanged:: 1.0.3 + ``app`` can be passed directly, rather than requiring an app + context for configuration. + """ + _dump_arg_defaults(kwargs, app=app) + encoding = kwargs.pop("encoding", None) + rv = _json.dumps(obj, **kwargs) + + if encoding is not None: + warnings.warn( + "'encoding' is deprecated and will be removed in Flask 2.1.", + DeprecationWarning, + stacklevel=2, + ) + + if isinstance(rv, str): + return rv.encode(encoding) # type: ignore + + return rv + + +def dump( + obj: t.Any, fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any +) -> None: + """Serialize an object to JSON written to a file object. + + Takes the same arguments as the built-in :func:`json.dump`, with + some defaults from application configuration. + + :param obj: Object to serialize to JSON. + :param fp: File object to write JSON to. + :param app: Use this app's config instead of the active app context + or defaults. + :param kwargs: Extra arguments passed to :func:`json.dump`. + + .. versionchanged:: 2.0 + Writing to a binary file, and the ``encoding`` argument, is + deprecated and will be removed in Flask 2.1. + """ + _dump_arg_defaults(kwargs, app=app) + encoding = kwargs.pop("encoding", None) + show_warning = encoding is not None + + try: + fp.write("") + except TypeError: + show_warning = True + fp = io.TextIOWrapper(fp, encoding or "utf-8") # type: ignore + + if show_warning: + warnings.warn( + "Writing to a binary file, and the 'encoding' argument, is" + " deprecated and will be removed in Flask 2.1.", + DeprecationWarning, + stacklevel=2, + ) + + _json.dump(obj, fp, **kwargs) + + +def loads(s: str, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any: + """Deserialize an object from a string of JSON. + + Takes the same arguments as the built-in :func:`json.loads`, with + some defaults from application configuration. + + :param s: JSON string to deserialize. + :param app: Use this app's config instead of the active app context + or defaults. + :param kwargs: Extra arguments passed to :func:`json.loads`. + + .. versionchanged:: 2.0 + ``encoding`` is deprecated and will be removed in Flask 2.1. The + data must be a string or UTF-8 bytes. + + .. versionchanged:: 1.0.3 + ``app`` can be passed directly, rather than requiring an app + context for configuration. + """ + _load_arg_defaults(kwargs, app=app) + encoding = kwargs.pop("encoding", None) + + if encoding is not None: + warnings.warn( + "'encoding' is deprecated and will be removed in Flask 2.1." + " The data must be a string or UTF-8 bytes.", + DeprecationWarning, + stacklevel=2, + ) + + if isinstance(s, bytes): + s = s.decode(encoding) + + return _json.loads(s, **kwargs) + + +def load(fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any: + """Deserialize an object from JSON read from a file object. + + Takes the same arguments as the built-in :func:`json.load`, with + some defaults from application configuration. + + :param fp: File object to read JSON from. + :param app: Use this app's config instead of the active app context + or defaults. + :param kwargs: Extra arguments passed to :func:`json.load`. + + .. versionchanged:: 2.0 + ``encoding`` is deprecated and will be removed in Flask 2.1. The + file must be text mode, or binary mode with UTF-8 bytes. + """ + _load_arg_defaults(kwargs, app=app) + encoding = kwargs.pop("encoding", None) + + if encoding is not None: + warnings.warn( + "'encoding' is deprecated and will be removed in Flask 2.1." + " The file must be text mode, or binary mode with UTF-8" + " bytes.", + DeprecationWarning, + stacklevel=2, + ) + + if isinstance(fp.read(0), bytes): + fp = io.TextIOWrapper(fp, encoding) # type: ignore + + return _json.load(fp, **kwargs) + + +def htmlsafe_dumps(obj: t.Any, **kwargs: t.Any) -> str: + """Serialize an object to a string of JSON with :func:`dumps`, then + replace HTML-unsafe characters with Unicode escapes and mark the + result safe with :class:`~markupsafe.Markup`. + + This is available in templates as the ``|tojson`` filter. + + The returned string is safe to render in HTML documents and + ``') + # => <script> do_nasty_stuff() </script> + # sanitize_html('Click here for $100') + # => Click here for $100 + def sanitize_token(self, token): + + # accommodate filters which use token_type differently + token_type = token["type"] + if token_type in ("StartTag", "EndTag", "EmptyTag"): + name = token["name"] + namespace = token["namespace"] + if ((namespace, name) in self.allowed_elements or + (namespace is None and + (namespaces["html"], name) in self.allowed_elements)): + return self.allowed_token(token) + else: + return self.disallowed_token(token) + elif token_type == "Comment": + pass + else: + return token + + def allowed_token(self, token): + if "data" in token: + attrs = token["data"] + attr_names = set(attrs.keys()) + + # Remove forbidden attributes + for to_remove in (attr_names - self.allowed_attributes): + del token["data"][to_remove] + attr_names.remove(to_remove) + + # Remove attributes with disallowed URL values + for attr in (attr_names & self.attr_val_is_uri): + assert attr in attrs + # I don't have a clue where this regexp comes from or why it matches those + # characters, nor why we call unescape. I just know it's always been here. + # Should you be worried by this comment in a sanitizer? Yes. On the other hand, all + # this will do is remove *more* than it otherwise would. + val_unescaped = re.sub("[`\x00-\x20\x7f-\xa0\\s]+", '', + unescape(attrs[attr])).lower() + # remove replacement characters from unescaped characters + val_unescaped = val_unescaped.replace("\ufffd", "") + try: + uri = urlparse.urlparse(val_unescaped) + except ValueError: + uri = None + del attrs[attr] + if uri and uri.scheme: + if uri.scheme not in self.allowed_protocols: + del attrs[attr] + if uri.scheme == 'data': + m = data_content_type.match(uri.path) + if not m: + del attrs[attr] + elif m.group('content_type') not in self.allowed_content_types: + del attrs[attr] + + for attr in self.svg_attr_val_allows_ref: + if attr in attrs: + attrs[attr] = re.sub(r'url\s*\(\s*[^#\s][^)]+?\)', + ' ', + unescape(attrs[attr])) + if (token["name"] in self.svg_allow_local_href and + (namespaces['xlink'], 'href') in attrs and re.search(r'^\s*[^#\s].*', + attrs[(namespaces['xlink'], 'href')])): + del attrs[(namespaces['xlink'], 'href')] + if (None, 'style') in attrs: + attrs[(None, 'style')] = self.sanitize_css(attrs[(None, 'style')]) + token["data"] = attrs + return token + + def disallowed_token(self, token): + token_type = token["type"] + if token_type == "EndTag": + token["data"] = "" % token["name"] + elif token["data"]: + assert token_type in ("StartTag", "EmptyTag") + attrs = [] + for (ns, name), v in token["data"].items(): + attrs.append(' %s="%s"' % (name if ns is None else "%s:%s" % (prefixes[ns], name), escape(v))) + token["data"] = "<%s%s>" % (token["name"], ''.join(attrs)) + else: + token["data"] = "<%s>" % token["name"] + if token.get("selfClosing"): + token["data"] = token["data"][:-1] + "/>" + + token["type"] = "Characters" + + del token["name"] + return token + + def sanitize_css(self, style): + # disallow urls + style = re.compile(r'url\s*\(\s*[^\s)]+?\s*\)\s*').sub(' ', style) + + # gauntlet + if not re.match(r"""^([:,;#%.\sa-zA-Z0-9!]|\w-\w|'[\s\w]+'|"[\s\w]+"|\([\d,\s]+\))*$""", style): + return '' + if not re.match(r"^\s*([-\w]+\s*:[^:;]*(;\s*|$))*$", style): + return '' + + clean = [] + for prop, value in re.findall(r"([-\w]+)\s*:\s*([^:;]*)", style): + if not value: + continue + if prop.lower() in self.allowed_css_properties: + clean.append(prop + ': ' + value + ';') + elif prop.split('-')[0].lower() in ['background', 'border', 'margin', + 'padding']: + for keyword in value.split(): + if keyword not in self.allowed_css_keywords and \ + not re.match(r"^(#[0-9a-fA-F]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$", keyword): # noqa + break + else: + clean.append(prop + ': ' + value + ';') + elif prop.lower() in self.allowed_svg_properties: + clean.append(prop + ': ' + value + ';') + + return ' '.join(clean) diff --git a/myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/whitespace.py b/myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/whitespace.py new file mode 100644 index 0000000..0d12584 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/whitespace.py @@ -0,0 +1,38 @@ +from __future__ import absolute_import, division, unicode_literals + +import re + +from . import base +from ..constants import rcdataElements, spaceCharacters +spaceCharacters = "".join(spaceCharacters) + +SPACES_REGEX = re.compile("[%s]+" % spaceCharacters) + + +class Filter(base.Filter): + """Collapses whitespace except in pre, textarea, and script elements""" + spacePreserveElements = frozenset(["pre", "textarea"] + list(rcdataElements)) + + def __iter__(self): + preserve = 0 + for token in base.Filter.__iter__(self): + type = token["type"] + if type == "StartTag" \ + and (preserve or token["name"] in self.spacePreserveElements): + preserve += 1 + + elif type == "EndTag" and preserve: + preserve -= 1 + + elif not preserve and type == "SpaceCharacters" and token["data"]: + # Test on token["data"] above to not introduce spaces where there were not + token["data"] = " " + + elif not preserve and type == "Characters": + token["data"] = collapse_spaces(token["data"]) + + yield token + + +def collapse_spaces(text): + return SPACES_REGEX.sub(' ', text) diff --git a/myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/html5parser.py b/myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/html5parser.py new file mode 100644 index 0000000..d06784f --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/pip/_vendor/html5lib/html5parser.py @@ -0,0 +1,2795 @@ +from __future__ import absolute_import, division, unicode_literals +from pip._vendor.six import with_metaclass, viewkeys + +import types + +from . import _inputstream +from . import _tokenizer + +from . import treebuilders +from .treebuilders.base import Marker + +from . import _utils +from .constants import ( + spaceCharacters, asciiUpper2Lower, + specialElements, headingElements, cdataElements, rcdataElements, + tokenTypes, tagTokenTypes, + namespaces, + htmlIntegrationPointElements, mathmlTextIntegrationPointElements, + adjustForeignAttributes as adjustForeignAttributesMap, + adjustMathMLAttributes, adjustSVGAttributes, + E, + _ReparseException +) + + +def parse(doc, treebuilder="etree", namespaceHTMLElements=True, **kwargs): + """Parse an HTML document as a string or file-like object into a tree + + :arg doc: the document to parse as a string or file-like object + + :arg treebuilder: the treebuilder to use when parsing + + :arg namespaceHTMLElements: whether or not to namespace HTML elements + + :returns: parsed tree + + Example: + + >>> from html5lib.html5parser import parse + >>> parse('

This is a doc

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

This is a doc

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

Interactive Console

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

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

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

{title if title else ''}

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

    {description}

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

    {escape(self.name)}

    \n" + f"{self.get_description(environ)}\n" + ) + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + """Get a list of headers.""" + return [("Content-Type", "text/html; charset=utf-8")] + + def get_response( + self, + environ: t.Optional[t.Union["WSGIEnvironment", "WSGIRequest"]] = None, + scope: t.Optional[dict] = None, + ) -> "Response": + """Get a response object. If one was passed to the exception + it's returned directly. + + :param environ: the optional environ for the request. This + can be used to modify the response depending + on how the request looked like. + :return: a :class:`Response` object or a subclass thereof. + """ + from .wrappers.response import Response as WSGIResponse # noqa: F811 + + if self.response is not None: + return self.response + if environ is not None: + environ = _get_environ(environ) + headers = self.get_headers(environ, scope) + return WSGIResponse(self.get_body(environ, scope), self.code, headers) + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + """Call the exception as WSGI application. + + :param environ: the WSGI environment. + :param start_response: the response callable provided by the WSGI + server. + """ + response = t.cast("WSGIResponse", self.get_response(environ)) + return response(environ, start_response) + + def __str__(self) -> str: + code = self.code if self.code is not None else "???" + return f"{code} {self.name}: {self.description}" + + def __repr__(self) -> str: + code = self.code if self.code is not None else "???" + return f"<{type(self).__name__} '{code}: {self.name}'>" + + +class BadRequest(HTTPException): + """*400* `Bad Request` + + Raise if the browser sends something to the application the application + or server cannot handle. + """ + + code = 400 + description = ( + "The browser (or proxy) sent a request that this server could " + "not understand." + ) + + +class BadRequestKeyError(BadRequest, KeyError): + """An exception that is used to signal both a :exc:`KeyError` and a + :exc:`BadRequest`. Used by many of the datastructures. + """ + + _description = BadRequest.description + #: Show the KeyError along with the HTTP error message in the + #: response. This should be disabled in production, but can be + #: useful in a debug mode. + show_exception = False + + def __init__(self, arg: t.Optional[str] = None, *args: t.Any, **kwargs: t.Any): + super().__init__(*args, **kwargs) + + if arg is None: + KeyError.__init__(self) + else: + KeyError.__init__(self, arg) + + @property # type: ignore + def description(self) -> str: # type: ignore + if self.show_exception: + return ( + f"{self._description}\n" + f"{KeyError.__name__}: {KeyError.__str__(self)}" + ) + + return self._description + + @description.setter + def description(self, value: str) -> None: + self._description = value + + +class ClientDisconnected(BadRequest): + """Internal exception that is raised if Werkzeug detects a disconnected + client. Since the client is already gone at that point attempting to + send the error message to the client might not work and might ultimately + result in another exception in the server. Mainly this is here so that + it is silenced by default as far as Werkzeug is concerned. + + Since disconnections cannot be reliably detected and are unspecified + by WSGI to a large extent this might or might not be raised if a client + is gone. + + .. versionadded:: 0.8 + """ + + +class SecurityError(BadRequest): + """Raised if something triggers a security error. This is otherwise + exactly like a bad request error. + + .. versionadded:: 0.9 + """ + + +class BadHost(BadRequest): + """Raised if the submitted host is badly formatted. + + .. versionadded:: 0.11.2 + """ + + +class Unauthorized(HTTPException): + """*401* ``Unauthorized`` + + Raise if the user is not authorized to access a resource. + + The ``www_authenticate`` argument should be used to set the + ``WWW-Authenticate`` header. This is used for HTTP basic auth and + other schemes. Use :class:`~werkzeug.datastructures.WWWAuthenticate` + to create correctly formatted values. Strictly speaking a 401 + response is invalid if it doesn't provide at least one value for + this header, although real clients typically don't care. + + :param description: Override the default message used for the body + of the response. + :param www-authenticate: A single value, or list of values, for the + WWW-Authenticate header(s). + + .. versionchanged:: 2.0 + Serialize multiple ``www_authenticate`` items into multiple + ``WWW-Authenticate`` headers, rather than joining them + into a single value, for better interoperability. + + .. versionchanged:: 0.15.3 + If the ``www_authenticate`` argument is not set, the + ``WWW-Authenticate`` header is not set. + + .. versionchanged:: 0.15.3 + The ``response`` argument was restored. + + .. versionchanged:: 0.15.1 + ``description`` was moved back as the first argument, restoring + its previous position. + + .. versionchanged:: 0.15.0 + ``www_authenticate`` was added as the first argument, ahead of + ``description``. + """ + + code = 401 + description = ( + "The server could not verify that you are authorized to access" + " the URL requested. You either supplied the wrong credentials" + " (e.g. a bad password), or your browser doesn't understand" + " how to supply the credentials required." + ) + + def __init__( + self, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + www_authenticate: t.Optional[ + t.Union["WWWAuthenticate", t.Iterable["WWWAuthenticate"]] + ] = None, + ) -> None: + super().__init__(description, response) + + from .datastructures import WWWAuthenticate + + if isinstance(www_authenticate, WWWAuthenticate): + www_authenticate = (www_authenticate,) + + self.www_authenticate = www_authenticate + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + headers = super().get_headers(environ, scope) + if self.www_authenticate: + headers.extend(("WWW-Authenticate", str(x)) for x in self.www_authenticate) + return headers + + +class Forbidden(HTTPException): + """*403* `Forbidden` + + Raise if the user doesn't have the permission for the requested resource + but was authenticated. + """ + + code = 403 + description = ( + "You don't have the permission to access the requested" + " resource. It is either read-protected or not readable by the" + " server." + ) + + +class NotFound(HTTPException): + """*404* `Not Found` + + Raise if a resource does not exist and never existed. + """ + + code = 404 + description = ( + "The requested URL was not found on the server. If you entered" + " the URL manually please check your spelling and try again." + ) + + +class MethodNotAllowed(HTTPException): + """*405* `Method Not Allowed` + + Raise if the server used a method the resource does not handle. For + example `POST` if the resource is view only. Especially useful for REST. + + The first argument for this exception should be a list of allowed methods. + Strictly speaking the response would be invalid if you don't provide valid + methods in the header which you can do with that list. + """ + + code = 405 + description = "The method is not allowed for the requested URL." + + def __init__( + self, + valid_methods: t.Optional[t.Iterable[str]] = None, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + ) -> None: + """Takes an optional list of valid http methods + starting with werkzeug 0.3 the list will be mandatory.""" + super().__init__(description=description, response=response) + self.valid_methods = valid_methods + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + headers = super().get_headers(environ, scope) + if self.valid_methods: + headers.append(("Allow", ", ".join(self.valid_methods))) + return headers + + +class NotAcceptable(HTTPException): + """*406* `Not Acceptable` + + Raise if the server can't return any content conforming to the + `Accept` headers of the client. + """ + + code = 406 + description = ( + "The resource identified by the request is only capable of" + " generating response entities which have content" + " characteristics not acceptable according to the accept" + " headers sent in the request." + ) + + +class RequestTimeout(HTTPException): + """*408* `Request Timeout` + + Raise to signalize a timeout. + """ + + code = 408 + description = ( + "The server closed the network connection because the browser" + " didn't finish the request within the specified time." + ) + + +class Conflict(HTTPException): + """*409* `Conflict` + + Raise to signal that a request cannot be completed because it conflicts + with the current state on the server. + + .. versionadded:: 0.7 + """ + + code = 409 + description = ( + "A conflict happened while processing the request. The" + " resource might have been modified while the request was being" + " processed." + ) + + +class Gone(HTTPException): + """*410* `Gone` + + Raise if a resource existed previously and went away without new location. + """ + + code = 410 + description = ( + "The requested URL is no longer available on this server and" + " there is no forwarding address. If you followed a link from a" + " foreign page, please contact the author of this page." + ) + + +class LengthRequired(HTTPException): + """*411* `Length Required` + + Raise if the browser submitted data but no ``Content-Length`` header which + is required for the kind of processing the server does. + """ + + code = 411 + description = ( + "A request with this method requires a valid Content-" + "Length header." + ) + + +class PreconditionFailed(HTTPException): + """*412* `Precondition Failed` + + Status code used in combination with ``If-Match``, ``If-None-Match``, or + ``If-Unmodified-Since``. + """ + + code = 412 + description = ( + "The precondition on the request for the URL failed positive evaluation." + ) + + +class RequestEntityTooLarge(HTTPException): + """*413* `Request Entity Too Large` + + The status code one should return if the data submitted exceeded a given + limit. + """ + + code = 413 + description = "The data value transmitted exceeds the capacity limit." + + +class RequestURITooLarge(HTTPException): + """*414* `Request URI Too Large` + + Like *413* but for too long URLs. + """ + + code = 414 + description = ( + "The length of the requested URL exceeds the capacity limit for" + " this server. The request cannot be processed." + ) + + +class UnsupportedMediaType(HTTPException): + """*415* `Unsupported Media Type` + + The status code returned if the server is unable to handle the media type + the client transmitted. + """ + + code = 415 + description = ( + "The server does not support the media type transmitted in the request." + ) + + +class RequestedRangeNotSatisfiable(HTTPException): + """*416* `Requested Range Not Satisfiable` + + The client asked for an invalid part of the file. + + .. versionadded:: 0.7 + """ + + code = 416 + description = "The server cannot provide the requested range." + + def __init__( + self, + length: t.Optional[int] = None, + units: str = "bytes", + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + ) -> None: + """Takes an optional `Content-Range` header value based on ``length`` + parameter. + """ + super().__init__(description=description, response=response) + self.length = length + self.units = units + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + headers = super().get_headers(environ, scope) + if self.length is not None: + headers.append(("Content-Range", f"{self.units} */{self.length}")) + return headers + + +class ExpectationFailed(HTTPException): + """*417* `Expectation Failed` + + The server cannot meet the requirements of the Expect request-header. + + .. versionadded:: 0.7 + """ + + code = 417 + description = "The server could not meet the requirements of the Expect header" + + +class ImATeapot(HTTPException): + """*418* `I'm a teapot` + + The server should return this if it is a teapot and someone attempted + to brew coffee with it. + + .. versionadded:: 0.7 + """ + + code = 418 + description = "This server is a teapot, not a coffee machine" + + +class UnprocessableEntity(HTTPException): + """*422* `Unprocessable Entity` + + Used if the request is well formed, but the instructions are otherwise + incorrect. + """ + + code = 422 + description = ( + "The request was well-formed but was unable to be followed due" + " to semantic errors." + ) + + +class Locked(HTTPException): + """*423* `Locked` + + Used if the resource that is being accessed is locked. + """ + + code = 423 + description = "The resource that is being accessed is locked." + + +class FailedDependency(HTTPException): + """*424* `Failed Dependency` + + Used if the method could not be performed on the resource + because the requested action depended on another action and that action failed. + """ + + code = 424 + description = ( + "The method could not be performed on the resource because the" + " requested action depended on another action and that action" + " failed." + ) + + +class PreconditionRequired(HTTPException): + """*428* `Precondition Required` + + The server requires this request to be conditional, typically to prevent + the lost update problem, which is a race condition between two or more + clients attempting to update a resource through PUT or DELETE. By requiring + each client to include a conditional header ("If-Match" or "If-Unmodified- + Since") with the proper value retained from a recent GET request, the + server ensures that each client has at least seen the previous revision of + the resource. + """ + + code = 428 + description = ( + "This request is required to be conditional; try using" + ' "If-Match" or "If-Unmodified-Since".' + ) + + +class _RetryAfter(HTTPException): + """Adds an optional ``retry_after`` parameter which will set the + ``Retry-After`` header. May be an :class:`int` number of seconds or + a :class:`~datetime.datetime`. + """ + + def __init__( + self, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + retry_after: t.Optional[t.Union[datetime, int]] = None, + ) -> None: + super().__init__(description, response) + self.retry_after = retry_after + + def get_headers( + self, + environ: t.Optional["WSGIEnvironment"] = None, + scope: t.Optional[dict] = None, + ) -> t.List[t.Tuple[str, str]]: + headers = super().get_headers(environ, scope) + + if self.retry_after: + if isinstance(self.retry_after, datetime): + from .http import http_date + + value = http_date(self.retry_after) + else: + value = str(self.retry_after) + + headers.append(("Retry-After", value)) + + return headers + + +class TooManyRequests(_RetryAfter): + """*429* `Too Many Requests` + + The server is limiting the rate at which this user receives + responses, and this request exceeds that rate. (The server may use + any convenient method to identify users and their request rates). + The server may include a "Retry-After" header to indicate how long + the user should wait before retrying. + + :param retry_after: If given, set the ``Retry-After`` header to this + value. May be an :class:`int` number of seconds or a + :class:`~datetime.datetime`. + + .. versionchanged:: 1.0 + Added ``retry_after`` parameter. + """ + + code = 429 + description = "This user has exceeded an allotted request count. Try again later." + + +class RequestHeaderFieldsTooLarge(HTTPException): + """*431* `Request Header Fields Too Large` + + The server refuses to process the request because the header fields are too + large. One or more individual fields may be too large, or the set of all + headers is too large. + """ + + code = 431 + description = "One or more header fields exceeds the maximum size." + + +class UnavailableForLegalReasons(HTTPException): + """*451* `Unavailable For Legal Reasons` + + This status code indicates that the server is denying access to the + resource as a consequence of a legal demand. + """ + + code = 451 + description = "Unavailable for legal reasons." + + +class InternalServerError(HTTPException): + """*500* `Internal Server Error` + + Raise if an internal server error occurred. This is a good fallback if an + unknown error occurred in the dispatcher. + + .. versionchanged:: 1.0.0 + Added the :attr:`original_exception` attribute. + """ + + code = 500 + description = ( + "The server encountered an internal error and was unable to" + " complete your request. Either the server is overloaded or" + " there is an error in the application." + ) + + def __init__( + self, + description: t.Optional[str] = None, + response: t.Optional["Response"] = None, + original_exception: t.Optional[BaseException] = None, + ) -> None: + #: The original exception that caused this 500 error. Can be + #: used by frameworks to provide context when handling + #: unexpected errors. + self.original_exception = original_exception + super().__init__(description=description, response=response) + + +class NotImplemented(HTTPException): + """*501* `Not Implemented` + + Raise if the application does not support the action requested by the + browser. + """ + + code = 501 + description = "The server does not support the action requested by the browser." + + +class BadGateway(HTTPException): + """*502* `Bad Gateway` + + If you do proxying in your application you should return this status code + if you received an invalid response from the upstream server it accessed + in attempting to fulfill the request. + """ + + code = 502 + description = ( + "The proxy server received an invalid response from an upstream server." + ) + + +class ServiceUnavailable(_RetryAfter): + """*503* `Service Unavailable` + + Status code you should return if a service is temporarily + unavailable. + + :param retry_after: If given, set the ``Retry-After`` header to this + value. May be an :class:`int` number of seconds or a + :class:`~datetime.datetime`. + + .. versionchanged:: 1.0 + Added ``retry_after`` parameter. + """ + + code = 503 + description = ( + "The server is temporarily unable to service your request due" + " to maintenance downtime or capacity problems. Please try" + " again later." + ) + + +class GatewayTimeout(HTTPException): + """*504* `Gateway Timeout` + + Status code you should return if a connection to an upstream server + times out. + """ + + code = 504 + description = "The connection to an upstream server timed out." + + +class HTTPVersionNotSupported(HTTPException): + """*505* `HTTP Version Not Supported` + + The server does not support the HTTP protocol version used in the request. + """ + + code = 505 + description = ( + "The server does not support the HTTP protocol version used in the request." + ) + + +default_exceptions: t.Dict[int, t.Type[HTTPException]] = {} + + +def _find_exceptions() -> None: + for obj in globals().values(): + try: + is_http_exception = issubclass(obj, HTTPException) + except TypeError: + is_http_exception = False + if not is_http_exception or obj.code is None: + continue + old_obj = default_exceptions.get(obj.code, None) + if old_obj is not None and issubclass(obj, old_obj): + continue + default_exceptions[obj.code] = obj + + +_find_exceptions() +del _find_exceptions + + +class Aborter: + """When passed a dict of code -> exception items it can be used as + callable that raises exceptions. If the first argument to the + callable is an integer it will be looked up in the mapping, if it's + a WSGI application it will be raised in a proxy exception. + + The rest of the arguments are forwarded to the exception constructor. + """ + + def __init__( + self, + mapping: t.Optional[t.Dict[int, t.Type[HTTPException]]] = None, + extra: t.Optional[t.Dict[int, t.Type[HTTPException]]] = None, + ) -> None: + if mapping is None: + mapping = default_exceptions + self.mapping = dict(mapping) + if extra is not None: + self.mapping.update(extra) + + def __call__( + self, code: t.Union[int, "Response"], *args: t.Any, **kwargs: t.Any + ) -> "te.NoReturn": + from .sansio.response import Response + + if isinstance(code, Response): + raise HTTPException(response=code) + + if code not in self.mapping: + raise LookupError(f"no exception for {code!r}") + + raise self.mapping[code](*args, **kwargs) + + +def abort( + status: t.Union[int, "Response"], *args: t.Any, **kwargs: t.Any +) -> "te.NoReturn": + """Raises an :py:exc:`HTTPException` for the given status code or WSGI + application. + + If a status code is given, it will be looked up in the list of + exceptions and will raise that exception. If passed a WSGI application, + it will wrap it in a proxy WSGI exception and raise that:: + + abort(404) # 404 Not Found + abort(Response('Hello World')) + + """ + _aborter(status, *args, **kwargs) + + +_aborter: Aborter = Aborter() diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/filesystem.py b/myvenv/lib/python3.10/site-packages/werkzeug/filesystem.py new file mode 100644 index 0000000..36a3d12 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/filesystem.py @@ -0,0 +1,55 @@ +import codecs +import sys +import typing as t +import warnings + +# We do not trust traditional unixes. +has_likely_buggy_unicode_filesystem = ( + sys.platform.startswith("linux") or "bsd" in sys.platform +) + + +def _is_ascii_encoding(encoding: t.Optional[str]) -> bool: + """Given an encoding this figures out if the encoding is actually ASCII (which + is something we don't actually want in most cases). This is necessary + because ASCII comes under many names such as ANSI_X3.4-1968. + """ + if encoding is None: + return False + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +class BrokenFilesystemWarning(RuntimeWarning, UnicodeWarning): + """The warning used by Werkzeug to signal a broken filesystem. Will only be + used once per runtime.""" + + +_warned_about_filesystem_encoding = False + + +def get_filesystem_encoding() -> str: + """Returns the filesystem encoding that should be used. Note that this is + different from the Python understanding of the filesystem encoding which + might be deeply flawed. Do not use this value against Python's string APIs + because it might be different. See :ref:`filesystem-encoding` for the exact + behavior. + + The concept of a filesystem encoding in generally is not something you + should rely on. As such if you ever need to use this function except for + writing wrapper code reconsider. + """ + global _warned_about_filesystem_encoding + rv = sys.getfilesystemencoding() + if has_likely_buggy_unicode_filesystem and not rv or _is_ascii_encoding(rv): + if not _warned_about_filesystem_encoding: + warnings.warn( + "Detected a misconfigured UNIX filesystem: Will use" + f" UTF-8 as filesystem encoding instead of {rv!r}", + BrokenFilesystemWarning, + ) + _warned_about_filesystem_encoding = True + return "utf-8" + return rv diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/formparser.py b/myvenv/lib/python3.10/site-packages/werkzeug/formparser.py new file mode 100644 index 0000000..6cb758f --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/formparser.py @@ -0,0 +1,495 @@ +import typing as t +import warnings +from functools import update_wrapper +from io import BytesIO +from itertools import chain +from typing import Union + +from . import exceptions +from ._internal import _to_str +from .datastructures import FileStorage +from .datastructures import Headers +from .datastructures import MultiDict +from .http import parse_options_header +from .sansio.multipart import Data +from .sansio.multipart import Epilogue +from .sansio.multipart import Field +from .sansio.multipart import File +from .sansio.multipart import MultipartDecoder +from .sansio.multipart import NeedData +from .urls import url_decode_stream +from .wsgi import _make_chunk_iter +from .wsgi import get_content_length +from .wsgi import get_input_stream + +# there are some platforms where SpooledTemporaryFile is not available. +# In that case we need to provide a fallback. +try: + from tempfile import SpooledTemporaryFile +except ImportError: + from tempfile import TemporaryFile + + SpooledTemporaryFile = None # type: ignore + +if t.TYPE_CHECKING: + import typing as te + from _typeshed.wsgi import WSGIEnvironment + + t_parse_result = t.Tuple[t.IO[bytes], MultiDict, MultiDict] + + class TStreamFactory(te.Protocol): + def __call__( + self, + total_content_length: t.Optional[int], + content_type: t.Optional[str], + filename: t.Optional[str], + content_length: t.Optional[int] = None, + ) -> t.IO[bytes]: + ... + + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def _exhaust(stream: t.IO[bytes]) -> None: + bts = stream.read(64 * 1024) + while bts: + bts = stream.read(64 * 1024) + + +def default_stream_factory( + total_content_length: t.Optional[int], + content_type: t.Optional[str], + filename: t.Optional[str], + content_length: t.Optional[int] = None, +) -> t.IO[bytes]: + max_size = 1024 * 500 + + if SpooledTemporaryFile is not None: + return t.cast(t.IO[bytes], SpooledTemporaryFile(max_size=max_size, mode="rb+")) + elif total_content_length is None or total_content_length > max_size: + return t.cast(t.IO[bytes], TemporaryFile("rb+")) + + return BytesIO() + + +def parse_form_data( + environ: "WSGIEnvironment", + stream_factory: t.Optional["TStreamFactory"] = None, + charset: str = "utf-8", + errors: str = "replace", + max_form_memory_size: t.Optional[int] = None, + max_content_length: t.Optional[int] = None, + cls: t.Optional[t.Type[MultiDict]] = None, + silent: bool = True, +) -> "t_parse_result": + """Parse the form data in the environ and return it as tuple in the form + ``(stream, form, files)``. You should only call this method if the + transport method is `POST`, `PUT`, or `PATCH`. + + If the mimetype of the data transmitted is `multipart/form-data` the + files multidict will be filled with `FileStorage` objects. If the + mimetype is unknown the input stream is wrapped and returned as first + argument, else the stream is empty. + + This is a shortcut for the common usage of :class:`FormDataParser`. + + Have a look at :doc:`/request_data` for more details. + + .. versionadded:: 0.5 + The `max_form_memory_size`, `max_content_length` and + `cls` parameters were added. + + .. versionadded:: 0.5.1 + The optional `silent` flag was added. + + :param environ: the WSGI environment to be used for parsing. + :param stream_factory: An optional callable that returns a new read and + writeable file descriptor. This callable works + the same as :meth:`Response._get_file_stream`. + :param charset: The character set for URL and url encoded form data. + :param errors: The encoding error behavior. + :param max_form_memory_size: the maximum number of bytes to be accepted for + in-memory stored form data. If the data + exceeds the value specified an + :exc:`~exceptions.RequestEntityTooLarge` + exception is raised. + :param max_content_length: If this is provided and the transmitted data + is longer than this value an + :exc:`~exceptions.RequestEntityTooLarge` + exception is raised. + :param cls: an optional dict class to use. If this is not specified + or `None` the default :class:`MultiDict` is used. + :param silent: If set to False parsing errors will not be caught. + :return: A tuple in the form ``(stream, form, files)``. + """ + return FormDataParser( + stream_factory, + charset, + errors, + max_form_memory_size, + max_content_length, + cls, + silent, + ).parse_from_environ(environ) + + +def exhaust_stream(f: F) -> F: + """Helper decorator for methods that exhausts the stream on return.""" + + def wrapper(self, stream, *args, **kwargs): # type: ignore + try: + return f(self, stream, *args, **kwargs) + finally: + exhaust = getattr(stream, "exhaust", None) + + if exhaust is not None: + exhaust() + else: + while True: + chunk = stream.read(1024 * 64) + + if not chunk: + break + + return update_wrapper(t.cast(F, wrapper), f) + + +class FormDataParser: + """This class implements parsing of form data for Werkzeug. By itself + it can parse multipart and url encoded form data. It can be subclassed + and extended but for most mimetypes it is a better idea to use the + untouched stream and expose it as separate attributes on a request + object. + + .. versionadded:: 0.8 + + :param stream_factory: An optional callable that returns a new read and + writeable file descriptor. This callable works + the same as :meth:`Response._get_file_stream`. + :param charset: The character set for URL and url encoded form data. + :param errors: The encoding error behavior. + :param max_form_memory_size: the maximum number of bytes to be accepted for + in-memory stored form data. If the data + exceeds the value specified an + :exc:`~exceptions.RequestEntityTooLarge` + exception is raised. + :param max_content_length: If this is provided and the transmitted data + is longer than this value an + :exc:`~exceptions.RequestEntityTooLarge` + exception is raised. + :param cls: an optional dict class to use. If this is not specified + or `None` the default :class:`MultiDict` is used. + :param silent: If set to False parsing errors will not be caught. + """ + + def __init__( + self, + stream_factory: t.Optional["TStreamFactory"] = None, + charset: str = "utf-8", + errors: str = "replace", + max_form_memory_size: t.Optional[int] = None, + max_content_length: t.Optional[int] = None, + cls: t.Optional[t.Type[MultiDict]] = None, + silent: bool = True, + ) -> None: + if stream_factory is None: + stream_factory = default_stream_factory + + self.stream_factory = stream_factory + self.charset = charset + self.errors = errors + self.max_form_memory_size = max_form_memory_size + self.max_content_length = max_content_length + + if cls is None: + cls = MultiDict + + self.cls = cls + self.silent = silent + + def get_parse_func( + self, mimetype: str, options: t.Dict[str, str] + ) -> t.Optional[ + t.Callable[ + ["FormDataParser", t.IO[bytes], str, t.Optional[int], t.Dict[str, str]], + "t_parse_result", + ] + ]: + return self.parse_functions.get(mimetype) + + def parse_from_environ(self, environ: "WSGIEnvironment") -> "t_parse_result": + """Parses the information from the environment as form data. + + :param environ: the WSGI environment to be used for parsing. + :return: A tuple in the form ``(stream, form, files)``. + """ + content_type = environ.get("CONTENT_TYPE", "") + content_length = get_content_length(environ) + mimetype, options = parse_options_header(content_type) + return self.parse(get_input_stream(environ), mimetype, content_length, options) + + def parse( + self, + stream: t.IO[bytes], + mimetype: str, + content_length: t.Optional[int], + options: t.Optional[t.Dict[str, str]] = None, + ) -> "t_parse_result": + """Parses the information from the given stream, mimetype, + content length and mimetype parameters. + + :param stream: an input stream + :param mimetype: the mimetype of the data + :param content_length: the content length of the incoming data + :param options: optional mimetype parameters (used for + the multipart boundary for instance) + :return: A tuple in the form ``(stream, form, files)``. + """ + if ( + self.max_content_length is not None + and content_length is not None + and content_length > self.max_content_length + ): + # if the input stream is not exhausted, firefox reports Connection Reset + _exhaust(stream) + raise exceptions.RequestEntityTooLarge() + + if options is None: + options = {} + + parse_func = self.get_parse_func(mimetype, options) + + if parse_func is not None: + try: + return parse_func(self, stream, mimetype, content_length, options) + except ValueError: + if not self.silent: + raise + + return stream, self.cls(), self.cls() + + @exhaust_stream + def _parse_multipart( + self, + stream: t.IO[bytes], + mimetype: str, + content_length: t.Optional[int], + options: t.Dict[str, str], + ) -> "t_parse_result": + parser = MultiPartParser( + self.stream_factory, + self.charset, + self.errors, + max_form_memory_size=self.max_form_memory_size, + cls=self.cls, + ) + boundary = options.get("boundary", "").encode("ascii") + + if not boundary: + raise ValueError("Missing boundary") + + form, files = parser.parse(stream, boundary, content_length) + return stream, form, files + + @exhaust_stream + def _parse_urlencoded( + self, + stream: t.IO[bytes], + mimetype: str, + content_length: t.Optional[int], + options: t.Dict[str, str], + ) -> "t_parse_result": + if ( + self.max_form_memory_size is not None + and content_length is not None + and content_length > self.max_form_memory_size + ): + # if the input stream is not exhausted, firefox reports Connection Reset + _exhaust(stream) + raise exceptions.RequestEntityTooLarge() + + form = url_decode_stream(stream, self.charset, errors=self.errors, cls=self.cls) + return stream, form, self.cls() + + #: mapping of mimetypes to parsing functions + parse_functions: t.Dict[ + str, + t.Callable[ + ["FormDataParser", t.IO[bytes], str, t.Optional[int], t.Dict[str, str]], + "t_parse_result", + ], + ] = { + "multipart/form-data": _parse_multipart, + "application/x-www-form-urlencoded": _parse_urlencoded, + "application/x-url-encoded": _parse_urlencoded, + } + + +def _line_parse(line: str) -> t.Tuple[str, bool]: + """Removes line ending characters and returns a tuple (`stripped_line`, + `is_terminated`). + """ + if line[-2:] == "\r\n": + return line[:-2], True + + elif line[-1:] in {"\r", "\n"}: + return line[:-1], True + + return line, False + + +def parse_multipart_headers(iterable: t.Iterable[bytes]) -> Headers: + """Parses multipart headers from an iterable that yields lines (including + the trailing newline symbol). The iterable has to be newline terminated. + The iterable will stop at the line where the headers ended so it can be + further consumed. + :param iterable: iterable of strings that are newline terminated + """ + warnings.warn( + "'parse_multipart_headers' is deprecated and will be removed in" + " Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + result: t.List[t.Tuple[str, str]] = [] + + for b_line in iterable: + line = _to_str(b_line) + line, line_terminated = _line_parse(line) + + if not line_terminated: + raise ValueError("unexpected end of line in multipart header") + + if not line: + break + elif line[0] in " \t" and result: + key, value = result[-1] + result[-1] = (key, f"{value}\n {line[1:]}") + else: + parts = line.split(":", 1) + + if len(parts) == 2: + result.append((parts[0].strip(), parts[1].strip())) + + # we link the list to the headers, no need to create a copy, the + # list was not shared anyways. + return Headers(result) + + +class MultiPartParser: + def __init__( + self, + stream_factory: t.Optional["TStreamFactory"] = None, + charset: str = "utf-8", + errors: str = "replace", + max_form_memory_size: t.Optional[int] = None, + cls: t.Optional[t.Type[MultiDict]] = None, + buffer_size: int = 64 * 1024, + ) -> None: + self.charset = charset + self.errors = errors + self.max_form_memory_size = max_form_memory_size + + if stream_factory is None: + stream_factory = default_stream_factory + + self.stream_factory = stream_factory + + if cls is None: + cls = MultiDict + + self.cls = cls + + self.buffer_size = buffer_size + + def fail(self, message: str) -> "te.NoReturn": + raise ValueError(message) + + def get_part_charset(self, headers: Headers) -> str: + # Figure out input charset for current part + content_type = headers.get("content-type") + + if content_type: + mimetype, ct_params = parse_options_header(content_type) + return ct_params.get("charset", self.charset) + + return self.charset + + def start_file_streaming( + self, event: File, total_content_length: t.Optional[int] + ) -> t.IO[bytes]: + content_type = event.headers.get("content-type") + + try: + content_length = int(event.headers["content-length"]) + except (KeyError, ValueError): + content_length = 0 + + container = self.stream_factory( + total_content_length=total_content_length, + filename=event.filename, + content_type=content_type, + content_length=content_length, + ) + return container + + def parse( + self, stream: t.IO[bytes], boundary: bytes, content_length: t.Optional[int] + ) -> t.Tuple[MultiDict, MultiDict]: + container: t.Union[t.IO[bytes], t.List[bytes]] + _write: t.Callable[[bytes], t.Any] + + iterator = chain( + _make_chunk_iter( + stream, + limit=content_length, + buffer_size=self.buffer_size, + ), + [None], + ) + + parser = MultipartDecoder(boundary, self.max_form_memory_size) + + fields = [] + files = [] + + current_part: Union[Field, File] + for data in iterator: + parser.receive_data(data) + event = parser.next_event() + while not isinstance(event, (Epilogue, NeedData)): + if isinstance(event, Field): + current_part = event + container = [] + _write = container.append + elif isinstance(event, File): + current_part = event + container = self.start_file_streaming(event, content_length) + _write = container.write + elif isinstance(event, Data): + _write(event.data) + if not event.more_data: + if isinstance(current_part, Field): + value = b"".join(container).decode( + self.get_part_charset(current_part.headers), self.errors + ) + fields.append((current_part.name, value)) + else: + container = t.cast(t.IO[bytes], container) + container.seek(0) + files.append( + ( + current_part.name, + FileStorage( + container, + current_part.filename, + current_part.name, + headers=current_part.headers, + ), + ) + ) + + event = parser.next_event() + + return self.cls(fields), self.cls(files) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/http.py b/myvenv/lib/python3.10/site-packages/werkzeug/http.py new file mode 100644 index 0000000..45799bf --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/http.py @@ -0,0 +1,1393 @@ +import base64 +import email.utils +import re +import typing +import typing as t +import warnings +from datetime import date +from datetime import datetime +from datetime import time +from datetime import timedelta +from datetime import timezone +from enum import Enum +from hashlib import sha1 +from time import mktime +from time import struct_time +from urllib.parse import unquote_to_bytes as _unquote +from urllib.request import parse_http_list as _parse_list_header + +from ._internal import _cookie_parse_impl +from ._internal import _cookie_quote +from ._internal import _make_cookie_domain +from ._internal import _to_bytes +from ._internal import _to_str +from ._internal import _wsgi_decoding_dance +from werkzeug._internal import _dt_as_utc + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import WSGIEnvironment + +# for explanation of "media-range", etc. see Sections 5.3.{1,2} of RFC 7231 +_accept_re = re.compile( + r""" + ( # media-range capturing-parenthesis + [^\s;,]+ # type/subtype + (?:[ \t]*;[ \t]* # ";" + (?: # parameter non-capturing-parenthesis + [^\s;,q][^\s;,]* # token that doesn't start with "q" + | # or + q[^\s;,=][^\s;,]* # token that is more than just "q" + ) + )* # zero or more parameters + ) # end of media-range + (?:[ \t]*;[ \t]*q= # weight is a "q" parameter + (\d*(?:\.\d+)?) # qvalue capturing-parentheses + [^,]* # "extension" accept params: who cares? + )? # accept params are optional + """, + re.VERBOSE, +) +_token_chars = frozenset( + "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~" +) +_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)') +_option_header_piece_re = re.compile( + r""" + ;\s*,?\s* # newlines were replaced with commas + (?P + "[^"\\]*(?:\\.[^"\\]*)*" # quoted string + | + [^\s;,=*]+ # token + ) + (?:\*(?P\d+))? # *1, optional continuation index + \s* + (?: # optionally followed by =value + (?: # equals sign, possibly with encoding + \*\s*=\s* # * indicates extended notation + (?: # optional encoding + (?P[^\s]+?) + '(?P[^\s]*?)' + )? + | + =\s* # basic notation + ) + (?P + "[^"\\]*(?:\\.[^"\\]*)*" # quoted string + | + [^;,]+ # token + )? + )? + \s* + """, + flags=re.VERBOSE, +) +_option_header_start_mime_type = re.compile(r",\s*([^;,\s]+)([;,]\s*.+)?") +_entity_headers = frozenset( + [ + "allow", + "content-encoding", + "content-language", + "content-length", + "content-location", + "content-md5", + "content-range", + "content-type", + "expires", + "last-modified", + ] +) +_hop_by_hop_headers = frozenset( + [ + "connection", + "keep-alive", + "proxy-authenticate", + "proxy-authorization", + "te", + "trailer", + "transfer-encoding", + "upgrade", + ] +) +HTTP_STATUS_CODES = { + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing", + 103: "Early Hints", # see RFC 8297 + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi Status", + 208: "Already Reported", # see RFC 5842 + 226: "IM Used", # see RFC 3229 + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 306: "Switch Proxy", # unused + 307: "Temporary Redirect", + 308: "Permanent Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", # unused + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 418: "I'm a teapot", # see RFC 2324 + 421: "Misdirected Request", # see RFC 7540 + 422: "Unprocessable Entity", + 423: "Locked", + 424: "Failed Dependency", + 425: "Too Early", # see RFC 8470 + 426: "Upgrade Required", + 428: "Precondition Required", # see RFC 6585 + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 449: "Retry With", # proprietary MS extension + 451: "Unavailable For Legal Reasons", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates", # see RFC 2295 + 507: "Insufficient Storage", + 508: "Loop Detected", # see RFC 5842 + 510: "Not Extended", + 511: "Network Authentication Failed", +} + + +class COEP(Enum): + """Cross Origin Embedder Policies""" + + UNSAFE_NONE = "unsafe-none" + REQUIRE_CORP = "require-corp" + + +class COOP(Enum): + """Cross Origin Opener Policies""" + + UNSAFE_NONE = "unsafe-none" + SAME_ORIGIN_ALLOW_POPUPS = "same-origin-allow-popups" + SAME_ORIGIN = "same-origin" + + +def quote_header_value( + value: t.Union[str, int], extra_chars: str = "", allow_token: bool = True +) -> str: + """Quote a header value if necessary. + + .. versionadded:: 0.5 + + :param value: the value to quote. + :param extra_chars: a list of extra characters to skip quoting. + :param allow_token: if this is enabled token values are returned + unchanged. + """ + if isinstance(value, bytes): + value = value.decode("latin1") + value = str(value) + if allow_token: + token_chars = _token_chars | set(extra_chars) + if set(value).issubset(token_chars): + return value + value = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{value}"' + + +def unquote_header_value(value: str, is_filename: bool = False) -> str: + r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). + This does not use the real unquoting but what browsers are actually + using for quoting. + + .. versionadded:: 0.5 + + :param value: the header value to unquote. + :param is_filename: The value represents a filename or path. + """ + if value and value[0] == value[-1] == '"': + # this is not the real unquoting, but fixing this so that the + # RFC is met will result in bugs with internet explorer and + # probably some other browsers as well. IE for example is + # uploading files with "C:\foo\bar.txt" as filename + value = value[1:-1] + + # if this is a filename and the starting characters look like + # a UNC path, then just return the value without quotes. Using the + # replace sequence below on a UNC path has the effect of turning + # the leading double slash into a single slash and then + # _fix_ie_filename() doesn't work correctly. See #458. + if not is_filename or value[:2] != "\\\\": + return value.replace("\\\\", "\\").replace('\\"', '"') + return value + + +def dump_options_header( + header: t.Optional[str], options: t.Mapping[str, t.Optional[t.Union[str, int]]] +) -> str: + """The reverse function to :func:`parse_options_header`. + + :param header: the header to dump + :param options: a dict of options to append. + """ + segments = [] + if header is not None: + segments.append(header) + for key, value in options.items(): + if value is None: + segments.append(key) + else: + segments.append(f"{key}={quote_header_value(value)}") + return "; ".join(segments) + + +def dump_header( + iterable: t.Union[t.Dict[str, t.Union[str, int]], t.Iterable[str]], + allow_token: bool = True, +) -> str: + """Dump an HTTP header again. This is the reversal of + :func:`parse_list_header`, :func:`parse_set_header` and + :func:`parse_dict_header`. This also quotes strings that include an + equals sign unless you pass it as dict of key, value pairs. + + >>> dump_header({'foo': 'bar baz'}) + 'foo="bar baz"' + >>> dump_header(('foo', 'bar baz')) + 'foo, "bar baz"' + + :param iterable: the iterable or dict of values to quote. + :param allow_token: if set to `False` tokens as values are disallowed. + See :func:`quote_header_value` for more details. + """ + if isinstance(iterable, dict): + items = [] + for key, value in iterable.items(): + if value is None: + items.append(key) + else: + items.append( + f"{key}={quote_header_value(value, allow_token=allow_token)}" + ) + else: + items = [quote_header_value(x, allow_token=allow_token) for x in iterable] + return ", ".join(items) + + +def dump_csp_header(header: "ds.ContentSecurityPolicy") -> str: + """Dump a Content Security Policy header. + + These are structured into policies such as "default-src 'self'; + script-src 'self'". + + .. versionadded:: 1.0.0 + Support for Content Security Policy headers was added. + + """ + return "; ".join(f"{key} {value}" for key, value in header.items()) + + +def parse_list_header(value: str) -> t.List[str]: + """Parse lists as described by RFC 2068 Section 2. + + In particular, parse comma-separated lists where the elements of + the list may include quoted-strings. A quoted-string could + contain a comma. A non-quoted string could have quotes in the + middle. Quotes are removed automatically after parsing. + + It basically works like :func:`parse_set_header` just that items + may appear multiple times and case sensitivity is preserved. + + The return value is a standard :class:`list`: + + >>> parse_list_header('token, "quoted value"') + ['token', 'quoted value'] + + To create a header from the :class:`list` again, use the + :func:`dump_header` function. + + :param value: a string with a list header. + :return: :class:`list` + """ + result = [] + for item in _parse_list_header(value): + if item[:1] == item[-1:] == '"': + item = unquote_header_value(item[1:-1]) + result.append(item) + return result + + +def parse_dict_header(value: str, cls: t.Type[dict] = dict) -> t.Dict[str, str]: + """Parse lists of key, value pairs as described by RFC 2068 Section 2 and + convert them into a python dict (or any other mapping object created from + the type with a dict like interface provided by the `cls` argument): + + >>> d = parse_dict_header('foo="is a fish", bar="as well"') + >>> type(d) is dict + True + >>> sorted(d.items()) + [('bar', 'as well'), ('foo', 'is a fish')] + + If there is no value for a key it will be `None`: + + >>> parse_dict_header('key_without_value') + {'key_without_value': None} + + To create a header from the :class:`dict` again, use the + :func:`dump_header` function. + + .. versionchanged:: 0.9 + Added support for `cls` argument. + + :param value: a string with a dict header. + :param cls: callable to use for storage of parsed results. + :return: an instance of `cls` + """ + result = cls() + if isinstance(value, bytes): + value = value.decode("latin1") + for item in _parse_list_header(value): + if "=" not in item: + result[item] = None + continue + name, value = item.split("=", 1) + if value[:1] == value[-1:] == '"': + value = unquote_header_value(value[1:-1]) + result[name] = value + return result + + +@typing.overload +def parse_options_header( + value: t.Optional[str], multiple: "te.Literal[False]" = False +) -> t.Tuple[str, t.Dict[str, str]]: + ... + + +@typing.overload +def parse_options_header( + value: t.Optional[str], multiple: "te.Literal[True]" +) -> t.Tuple[t.Any, ...]: + ... + + +def parse_options_header( + value: t.Optional[str], multiple: bool = False +) -> t.Union[t.Tuple[str, t.Dict[str, str]], t.Tuple[t.Any, ...]]: + """Parse a ``Content-Type`` like header into a tuple with the content + type and the options: + + >>> parse_options_header('text/html; charset=utf8') + ('text/html', {'charset': 'utf8'}) + + This should not be used to parse ``Cache-Control`` like headers that use + a slightly different format. For these headers use the + :func:`parse_dict_header` function. + + .. versionchanged:: 0.15 + :rfc:`2231` parameter continuations are handled. + + .. versionadded:: 0.5 + + :param value: the header to parse. + :param multiple: Whether try to parse and return multiple MIME types + :return: (mimetype, options) or (mimetype, options, mimetype, options, …) + if multiple=True + """ + if not value: + return "", {} + + result: t.List[t.Any] = [] + + value = "," + value.replace("\n", ",") + while value: + match = _option_header_start_mime_type.match(value) + if not match: + break + result.append(match.group(1)) # mimetype + options: t.Dict[str, str] = {} + # Parse options + rest = match.group(2) + encoding: t.Optional[str] + continued_encoding: t.Optional[str] = None + while rest: + optmatch = _option_header_piece_re.match(rest) + if not optmatch: + break + option, count, encoding, language, option_value = optmatch.groups() + # Continuations don't have to supply the encoding after the + # first line. If we're in a continuation, track the current + # encoding to use for subsequent lines. Reset it when the + # continuation ends. + if not count: + continued_encoding = None + else: + if not encoding: + encoding = continued_encoding + continued_encoding = encoding + option = unquote_header_value(option) + + if option_value is not None: + option_value = unquote_header_value(option_value, option == "filename") + + if encoding is not None: + option_value = _unquote(option_value).decode(encoding) + + if count: + # Continuations append to the existing value. For + # simplicity, this ignores the possibility of + # out-of-order indices, which shouldn't happen anyway. + if option_value is not None: + options[option] = options.get(option, "") + option_value + else: + options[option] = option_value # type: ignore[assignment] + + rest = rest[optmatch.end() :] + result.append(options) + if multiple is False: + return tuple(result) + value = rest + + return tuple(result) if result else ("", {}) + + +_TAnyAccept = t.TypeVar("_TAnyAccept", bound="ds.Accept") + + +@typing.overload +def parse_accept_header(value: t.Optional[str]) -> "ds.Accept": + ... + + +@typing.overload +def parse_accept_header( + value: t.Optional[str], cls: t.Type[_TAnyAccept] +) -> _TAnyAccept: + ... + + +def parse_accept_header( + value: t.Optional[str], cls: t.Optional[t.Type[_TAnyAccept]] = None +) -> _TAnyAccept: + """Parses an HTTP Accept-* header. This does not implement a complete + valid algorithm but one that supports at least value and quality + extraction. + + Returns a new :class:`Accept` object (basically a list of ``(value, quality)`` + tuples sorted by the quality with some additional accessor methods). + + The second parameter can be a subclass of :class:`Accept` that is created + with the parsed values and returned. + + :param value: the accept header string to be parsed. + :param cls: the wrapper class for the return value (can be + :class:`Accept` or a subclass thereof) + :return: an instance of `cls`. + """ + if cls is None: + cls = t.cast(t.Type[_TAnyAccept], ds.Accept) + + if not value: + return cls(None) + + result = [] + for match in _accept_re.finditer(value): + quality_match = match.group(2) + if not quality_match: + quality: float = 1 + else: + quality = max(min(float(quality_match), 1), 0) + result.append((match.group(1), quality)) + return cls(result) + + +_TAnyCC = t.TypeVar("_TAnyCC", bound="ds._CacheControl") +_t_cc_update = t.Optional[t.Callable[[_TAnyCC], None]] + + +@typing.overload +def parse_cache_control_header( + value: t.Optional[str], on_update: _t_cc_update, cls: None = None +) -> "ds.RequestCacheControl": + ... + + +@typing.overload +def parse_cache_control_header( + value: t.Optional[str], on_update: _t_cc_update, cls: t.Type[_TAnyCC] +) -> _TAnyCC: + ... + + +def parse_cache_control_header( + value: t.Optional[str], + on_update: _t_cc_update = None, + cls: t.Optional[t.Type[_TAnyCC]] = None, +) -> _TAnyCC: + """Parse a cache control header. The RFC differs between response and + request cache control, this method does not. It's your responsibility + to not use the wrong control statements. + + .. versionadded:: 0.5 + The `cls` was added. If not specified an immutable + :class:`~werkzeug.datastructures.RequestCacheControl` is returned. + + :param value: a cache control header to be parsed. + :param on_update: an optional callable that is called every time a value + on the :class:`~werkzeug.datastructures.CacheControl` + object is changed. + :param cls: the class for the returned object. By default + :class:`~werkzeug.datastructures.RequestCacheControl` is used. + :return: a `cls` object. + """ + if cls is None: + cls = t.cast(t.Type[_TAnyCC], ds.RequestCacheControl) + + if not value: + return cls((), on_update) + + return cls(parse_dict_header(value), on_update) + + +_TAnyCSP = t.TypeVar("_TAnyCSP", bound="ds.ContentSecurityPolicy") +_t_csp_update = t.Optional[t.Callable[[_TAnyCSP], None]] + + +@typing.overload +def parse_csp_header( + value: t.Optional[str], on_update: _t_csp_update, cls: None = None +) -> "ds.ContentSecurityPolicy": + ... + + +@typing.overload +def parse_csp_header( + value: t.Optional[str], on_update: _t_csp_update, cls: t.Type[_TAnyCSP] +) -> _TAnyCSP: + ... + + +def parse_csp_header( + value: t.Optional[str], + on_update: _t_csp_update = None, + cls: t.Optional[t.Type[_TAnyCSP]] = None, +) -> _TAnyCSP: + """Parse a Content Security Policy header. + + .. versionadded:: 1.0.0 + Support for Content Security Policy headers was added. + + :param value: a csp header to be parsed. + :param on_update: an optional callable that is called every time a value + on the object is changed. + :param cls: the class for the returned object. By default + :class:`~werkzeug.datastructures.ContentSecurityPolicy` is used. + :return: a `cls` object. + """ + if cls is None: + cls = t.cast(t.Type[_TAnyCSP], ds.ContentSecurityPolicy) + + if value is None: + return cls((), on_update) + + items = [] + + for policy in value.split(";"): + policy = policy.strip() + + # Ignore badly formatted policies (no space) + if " " in policy: + directive, value = policy.strip().split(" ", 1) + items.append((directive.strip(), value.strip())) + + return cls(items, on_update) + + +def parse_set_header( + value: t.Optional[str], + on_update: t.Optional[t.Callable[["ds.HeaderSet"], None]] = None, +) -> "ds.HeaderSet": + """Parse a set-like header and return a + :class:`~werkzeug.datastructures.HeaderSet` object: + + >>> hs = parse_set_header('token, "quoted value"') + + The return value is an object that treats the items case-insensitively + and keeps the order of the items: + + >>> 'TOKEN' in hs + True + >>> hs.index('quoted value') + 1 + >>> hs + HeaderSet(['token', 'quoted value']) + + To create a header from the :class:`HeaderSet` again, use the + :func:`dump_header` function. + + :param value: a set header to be parsed. + :param on_update: an optional callable that is called every time a + value on the :class:`~werkzeug.datastructures.HeaderSet` + object is changed. + :return: a :class:`~werkzeug.datastructures.HeaderSet` + """ + if not value: + return ds.HeaderSet(None, on_update) + return ds.HeaderSet(parse_list_header(value), on_update) + + +def parse_authorization_header( + value: t.Optional[str], +) -> t.Optional["ds.Authorization"]: + """Parse an HTTP basic/digest authorization header transmitted by the web + browser. The return value is either `None` if the header was invalid or + not given, otherwise an :class:`~werkzeug.datastructures.Authorization` + object. + + :param value: the authorization header to parse. + :return: a :class:`~werkzeug.datastructures.Authorization` object or `None`. + """ + if not value: + return None + value = _wsgi_decoding_dance(value) + try: + auth_type, auth_info = value.split(None, 1) + auth_type = auth_type.lower() + except ValueError: + return None + if auth_type == "basic": + try: + username, password = base64.b64decode(auth_info).split(b":", 1) + except Exception: + return None + try: + return ds.Authorization( + "basic", + { + "username": _to_str(username, "utf-8"), + "password": _to_str(password, "utf-8"), + }, + ) + except UnicodeDecodeError: + return None + elif auth_type == "digest": + auth_map = parse_dict_header(auth_info) + for key in "username", "realm", "nonce", "uri", "response": + if key not in auth_map: + return None + if "qop" in auth_map: + if not auth_map.get("nc") or not auth_map.get("cnonce"): + return None + return ds.Authorization("digest", auth_map) + return None + + +def parse_www_authenticate_header( + value: t.Optional[str], + on_update: t.Optional[t.Callable[["ds.WWWAuthenticate"], None]] = None, +) -> "ds.WWWAuthenticate": + """Parse an HTTP WWW-Authenticate header into a + :class:`~werkzeug.datastructures.WWWAuthenticate` object. + + :param value: a WWW-Authenticate header to parse. + :param on_update: an optional callable that is called every time a value + on the :class:`~werkzeug.datastructures.WWWAuthenticate` + object is changed. + :return: a :class:`~werkzeug.datastructures.WWWAuthenticate` object. + """ + if not value: + return ds.WWWAuthenticate(on_update=on_update) + try: + auth_type, auth_info = value.split(None, 1) + auth_type = auth_type.lower() + except (ValueError, AttributeError): + return ds.WWWAuthenticate(value.strip().lower(), on_update=on_update) + return ds.WWWAuthenticate(auth_type, parse_dict_header(auth_info), on_update) + + +def parse_if_range_header(value: t.Optional[str]) -> "ds.IfRange": + """Parses an if-range header which can be an etag or a date. Returns + a :class:`~werkzeug.datastructures.IfRange` object. + + .. versionchanged:: 2.0 + If the value represents a datetime, it is timezone-aware. + + .. versionadded:: 0.7 + """ + if not value: + return ds.IfRange() + date = parse_date(value) + if date is not None: + return ds.IfRange(date=date) + # drop weakness information + return ds.IfRange(unquote_etag(value)[0]) + + +def parse_range_header( + value: t.Optional[str], make_inclusive: bool = True +) -> t.Optional["ds.Range"]: + """Parses a range header into a :class:`~werkzeug.datastructures.Range` + object. If the header is missing or malformed `None` is returned. + `ranges` is a list of ``(start, stop)`` tuples where the ranges are + non-inclusive. + + .. versionadded:: 0.7 + """ + if not value or "=" not in value: + return None + + ranges = [] + last_end = 0 + units, rng = value.split("=", 1) + units = units.strip().lower() + + for item in rng.split(","): + item = item.strip() + if "-" not in item: + return None + if item.startswith("-"): + if last_end < 0: + return None + try: + begin = int(item) + except ValueError: + return None + end = None + last_end = -1 + elif "-" in item: + begin_str, end_str = item.split("-", 1) + begin_str = begin_str.strip() + end_str = end_str.strip() + if not begin_str.isdigit(): + return None + begin = int(begin_str) + if begin < last_end or last_end < 0: + return None + if end_str: + if not end_str.isdigit(): + return None + end = int(end_str) + 1 + if begin >= end: + return None + else: + end = None + last_end = end if end is not None else -1 + ranges.append((begin, end)) + + return ds.Range(units, ranges) + + +def parse_content_range_header( + value: t.Optional[str], + on_update: t.Optional[t.Callable[["ds.ContentRange"], None]] = None, +) -> t.Optional["ds.ContentRange"]: + """Parses a range header into a + :class:`~werkzeug.datastructures.ContentRange` object or `None` if + parsing is not possible. + + .. versionadded:: 0.7 + + :param value: a content range header to be parsed. + :param on_update: an optional callable that is called every time a value + on the :class:`~werkzeug.datastructures.ContentRange` + object is changed. + """ + if value is None: + return None + try: + units, rangedef = (value or "").strip().split(None, 1) + except ValueError: + return None + + if "/" not in rangedef: + return None + rng, length_str = rangedef.split("/", 1) + if length_str == "*": + length = None + elif length_str.isdigit(): + length = int(length_str) + else: + return None + + if rng == "*": + return ds.ContentRange(units, None, None, length, on_update=on_update) + elif "-" not in rng: + return None + + start_str, stop_str = rng.split("-", 1) + try: + start = int(start_str) + stop = int(stop_str) + 1 + except ValueError: + return None + + if is_byte_range_valid(start, stop, length): + return ds.ContentRange(units, start, stop, length, on_update=on_update) + + return None + + +def quote_etag(etag: str, weak: bool = False) -> str: + """Quote an etag. + + :param etag: the etag to quote. + :param weak: set to `True` to tag it "weak". + """ + if '"' in etag: + raise ValueError("invalid etag") + etag = f'"{etag}"' + if weak: + etag = f"W/{etag}" + return etag + + +def unquote_etag( + etag: t.Optional[str], +) -> t.Union[t.Tuple[str, bool], t.Tuple[None, None]]: + """Unquote a single etag: + + >>> unquote_etag('W/"bar"') + ('bar', True) + >>> unquote_etag('"bar"') + ('bar', False) + + :param etag: the etag identifier to unquote. + :return: a ``(etag, weak)`` tuple. + """ + if not etag: + return None, None + etag = etag.strip() + weak = False + if etag.startswith(("W/", "w/")): + weak = True + etag = etag[2:] + if etag[:1] == etag[-1:] == '"': + etag = etag[1:-1] + return etag, weak + + +def parse_etags(value: t.Optional[str]) -> "ds.ETags": + """Parse an etag header. + + :param value: the tag header to parse + :return: an :class:`~werkzeug.datastructures.ETags` object. + """ + if not value: + return ds.ETags() + strong = [] + weak = [] + end = len(value) + pos = 0 + while pos < end: + match = _etag_re.match(value, pos) + if match is None: + break + is_weak, quoted, raw = match.groups() + if raw == "*": + return ds.ETags(star_tag=True) + elif quoted: + raw = quoted + if is_weak: + weak.append(raw) + else: + strong.append(raw) + pos = match.end() + return ds.ETags(strong, weak) + + +def generate_etag(data: bytes) -> str: + """Generate an etag for some data. + + .. versionchanged:: 2.0 + Use SHA-1. MD5 may not be available in some environments. + """ + return sha1(data).hexdigest() + + +def parse_date(value: t.Optional[str]) -> t.Optional[datetime]: + """Parse an :rfc:`2822` date into a timezone-aware + :class:`datetime.datetime` object, or ``None`` if parsing fails. + + This is a wrapper for :func:`email.utils.parsedate_to_datetime`. It + returns ``None`` if parsing fails instead of raising an exception, + and always returns a timezone-aware datetime object. If the string + doesn't have timezone information, it is assumed to be UTC. + + :param value: A string with a supported date format. + + .. versionchanged:: 2.0 + Return a timezone-aware datetime object. Use + ``email.utils.parsedate_to_datetime``. + """ + if value is None: + return None + + try: + dt = email.utils.parsedate_to_datetime(value) + except (TypeError, ValueError): + return None + + if dt.tzinfo is None: + return dt.replace(tzinfo=timezone.utc) + + return dt + + +def cookie_date( + expires: t.Optional[t.Union[datetime, date, int, float, struct_time]] = None +) -> str: + """Format a datetime object or timestamp into an :rfc:`2822` date + string for ``Set-Cookie expires``. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use :func:`http_date` instead. + """ + warnings.warn( + "'cookie_date' is deprecated and will be removed in Werkzeug" + " 2.1. Use 'http_date' instead.", + DeprecationWarning, + stacklevel=2, + ) + return http_date(expires) + + +def http_date( + timestamp: t.Optional[t.Union[datetime, date, int, float, struct_time]] = None +) -> str: + """Format a datetime object or timestamp into an :rfc:`2822` date + string. + + This is a wrapper for :func:`email.utils.format_datetime`. It + assumes naive datetime objects are in UTC instead of raising an + exception. + + :param timestamp: The datetime or timestamp to format. Defaults to + the current time. + + .. versionchanged:: 2.0 + Use ``email.utils.format_datetime``. Accept ``date`` objects. + """ + if isinstance(timestamp, date): + if not isinstance(timestamp, datetime): + # Assume plain date is midnight UTC. + timestamp = datetime.combine(timestamp, time(), tzinfo=timezone.utc) + else: + # Ensure datetime is timezone-aware. + timestamp = _dt_as_utc(timestamp) + + return email.utils.format_datetime(timestamp, usegmt=True) + + if isinstance(timestamp, struct_time): + timestamp = mktime(timestamp) + + return email.utils.formatdate(timestamp, usegmt=True) + + +def parse_age(value: t.Optional[str] = None) -> t.Optional[timedelta]: + """Parses a base-10 integer count of seconds into a timedelta. + + If parsing fails, the return value is `None`. + + :param value: a string consisting of an integer represented in base-10 + :return: a :class:`datetime.timedelta` object or `None`. + """ + if not value: + return None + try: + seconds = int(value) + except ValueError: + return None + if seconds < 0: + return None + try: + return timedelta(seconds=seconds) + except OverflowError: + return None + + +def dump_age(age: t.Optional[t.Union[timedelta, int]] = None) -> t.Optional[str]: + """Formats the duration as a base-10 integer. + + :param age: should be an integer number of seconds, + a :class:`datetime.timedelta` object, or, + if the age is unknown, `None` (default). + """ + if age is None: + return None + if isinstance(age, timedelta): + age = int(age.total_seconds()) + else: + age = int(age) + + if age < 0: + raise ValueError("age cannot be negative") + + return str(age) + + +def is_resource_modified( + environ: "WSGIEnvironment", + etag: t.Optional[str] = None, + data: t.Optional[bytes] = None, + last_modified: t.Optional[t.Union[datetime, str]] = None, + ignore_if_range: bool = True, +) -> bool: + """Convenience method for conditional requests. + + :param environ: the WSGI environment of the request to be checked. + :param etag: the etag for the response for comparison. + :param data: or alternatively the data of the response to automatically + generate an etag using :func:`generate_etag`. + :param last_modified: an optional date of the last modification. + :param ignore_if_range: If `False`, `If-Range` header will be taken into + account. + :return: `True` if the resource was modified, otherwise `False`. + + .. versionchanged:: 2.0 + SHA-1 is used to generate an etag value for the data. MD5 may + not be available in some environments. + + .. versionchanged:: 1.0.0 + The check is run for methods other than ``GET`` and ``HEAD``. + """ + if etag is None and data is not None: + etag = generate_etag(data) + elif data is not None: + raise TypeError("both data and etag given") + + unmodified = False + if isinstance(last_modified, str): + last_modified = parse_date(last_modified) + + # HTTP doesn't use microsecond, remove it to avoid false positive + # comparisons. Mark naive datetimes as UTC. + if last_modified is not None: + last_modified = _dt_as_utc(last_modified.replace(microsecond=0)) + + if_range = None + if not ignore_if_range and "HTTP_RANGE" in environ: + # https://tools.ietf.org/html/rfc7233#section-3.2 + # A server MUST ignore an If-Range header field received in a request + # that does not contain a Range header field. + if_range = parse_if_range_header(environ.get("HTTP_IF_RANGE")) + + if if_range is not None and if_range.date is not None: + modified_since: t.Optional[datetime] = if_range.date + else: + modified_since = parse_date(environ.get("HTTP_IF_MODIFIED_SINCE")) + + if modified_since and last_modified and last_modified <= modified_since: + unmodified = True + + if etag: + etag, _ = unquote_etag(etag) + etag = t.cast(str, etag) + + if if_range is not None and if_range.etag is not None: + unmodified = parse_etags(if_range.etag).contains(etag) + else: + if_none_match = parse_etags(environ.get("HTTP_IF_NONE_MATCH")) + if if_none_match: + # https://tools.ietf.org/html/rfc7232#section-3.2 + # "A recipient MUST use the weak comparison function when comparing + # entity-tags for If-None-Match" + unmodified = if_none_match.contains_weak(etag) + + # https://tools.ietf.org/html/rfc7232#section-3.1 + # "Origin server MUST use the strong comparison function when + # comparing entity-tags for If-Match" + if_match = parse_etags(environ.get("HTTP_IF_MATCH")) + if if_match: + unmodified = not if_match.is_strong(etag) + + return not unmodified + + +def remove_entity_headers( + headers: t.Union["ds.Headers", t.List[t.Tuple[str, str]]], + allowed: t.Iterable[str] = ("expires", "content-location"), +) -> None: + """Remove all entity headers from a list or :class:`Headers` object. This + operation works in-place. `Expires` and `Content-Location` headers are + by default not removed. The reason for this is :rfc:`2616` section + 10.3.5 which specifies some entity headers that should be sent. + + .. versionchanged:: 0.5 + added `allowed` parameter. + + :param headers: a list or :class:`Headers` object. + :param allowed: a list of headers that should still be allowed even though + they are entity headers. + """ + allowed = {x.lower() for x in allowed} + headers[:] = [ + (key, value) + for key, value in headers + if not is_entity_header(key) or key.lower() in allowed + ] + + +def remove_hop_by_hop_headers( + headers: t.Union["ds.Headers", t.List[t.Tuple[str, str]]] +) -> None: + """Remove all HTTP/1.1 "Hop-by-Hop" headers from a list or + :class:`Headers` object. This operation works in-place. + + .. versionadded:: 0.5 + + :param headers: a list or :class:`Headers` object. + """ + headers[:] = [ + (key, value) for key, value in headers if not is_hop_by_hop_header(key) + ] + + +def is_entity_header(header: str) -> bool: + """Check if a header is an entity header. + + .. versionadded:: 0.5 + + :param header: the header to test. + :return: `True` if it's an entity header, `False` otherwise. + """ + return header.lower() in _entity_headers + + +def is_hop_by_hop_header(header: str) -> bool: + """Check if a header is an HTTP/1.1 "Hop-by-Hop" header. + + .. versionadded:: 0.5 + + :param header: the header to test. + :return: `True` if it's an HTTP/1.1 "Hop-by-Hop" header, `False` otherwise. + """ + return header.lower() in _hop_by_hop_headers + + +def parse_cookie( + header: t.Union["WSGIEnvironment", str, bytes, None], + charset: str = "utf-8", + errors: str = "replace", + cls: t.Optional[t.Type["ds.MultiDict"]] = None, +) -> "ds.MultiDict[str, str]": + """Parse a cookie from a string or WSGI environ. + + The same key can be provided multiple times, the values are stored + in-order. The default :class:`MultiDict` will have the first value + first, and all values can be retrieved with + :meth:`MultiDict.getlist`. + + :param header: The cookie header as a string, or a WSGI environ dict + with a ``HTTP_COOKIE`` key. + :param charset: The charset for the cookie values. + :param errors: The error behavior for the charset decoding. + :param cls: A dict-like class to store the parsed cookies in. + Defaults to :class:`MultiDict`. + + .. versionchanged:: 1.0.0 + Returns a :class:`MultiDict` instead of a + ``TypeConversionDict``. + + .. versionchanged:: 0.5 + Returns a :class:`TypeConversionDict` instead of a regular dict. + The ``cls`` parameter was added. + """ + if isinstance(header, dict): + header = header.get("HTTP_COOKIE", "") + elif header is None: + header = "" + + # PEP 3333 sends headers through the environ as latin1 decoded + # strings. Encode strings back to bytes for parsing. + if isinstance(header, str): + header = header.encode("latin1", "replace") + + if cls is None: + cls = ds.MultiDict + + def _parse_pairs() -> t.Iterator[t.Tuple[str, str]]: + for key, val in _cookie_parse_impl(header): # type: ignore + key_str = _to_str(key, charset, errors, allow_none_charset=True) + + if not key_str: + continue + + val_str = _to_str(val, charset, errors, allow_none_charset=True) + yield key_str, val_str + + return cls(_parse_pairs()) + + +def dump_cookie( + key: str, + value: t.Union[bytes, str] = "", + max_age: t.Optional[t.Union[timedelta, int]] = None, + expires: t.Optional[t.Union[str, datetime, int, float]] = None, + path: t.Optional[str] = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + charset: str = "utf-8", + sync_expires: bool = True, + max_size: int = 4093, + samesite: t.Optional[str] = None, +) -> str: + """Create a Set-Cookie header without the ``Set-Cookie`` prefix. + + The return value is usually restricted to ascii as the vast majority + of values are properly escaped, but that is no guarantee. It's + tunneled through latin1 as required by :pep:`3333`. + + The return value is not ASCII safe if the key contains unicode + characters. This is technically against the specification but + happens in the wild. It's strongly recommended to not use + non-ASCII values for the keys. + + :param max_age: should be a number of seconds, or `None` (default) if + the cookie should last only as long as the client's + browser session. Additionally `timedelta` objects + are accepted, too. + :param expires: should be a `datetime` object or unix timestamp. + :param path: limits the cookie to a given path, per default it will + span the whole domain. + :param domain: Use this if you want to set a cross-domain cookie. For + example, ``domain=".example.com"`` will set a cookie + that is readable by the domain ``www.example.com``, + ``foo.example.com`` etc. Otherwise, a cookie will only + be readable by the domain that set it. + :param secure: The cookie will only be available via HTTPS + :param httponly: disallow JavaScript to access the cookie. This is an + extension to the cookie standard and probably not + supported by all browsers. + :param charset: the encoding for string values. + :param sync_expires: automatically set expires if max_age is defined + but expires not. + :param max_size: Warn if the final header value exceeds this size. The + default, 4093, should be safely `supported by most browsers + `_. Set to 0 to disable this check. + :param samesite: Limits the scope of the cookie such that it will + only be attached to requests if those requests are same-site. + + .. _`cookie`: http://browsercookielimits.squawky.net/ + + .. versionchanged:: 1.0.0 + The string ``'None'`` is accepted for ``samesite``. + """ + key = _to_bytes(key, charset) + value = _to_bytes(value, charset) + + if path is not None: + from .urls import iri_to_uri + + path = iri_to_uri(path, charset) + + domain = _make_cookie_domain(domain) + + if isinstance(max_age, timedelta): + max_age = int(max_age.total_seconds()) + + if expires is not None: + if not isinstance(expires, str): + expires = http_date(expires) + elif max_age is not None and sync_expires: + expires = http_date(datetime.now(tz=timezone.utc).timestamp() + max_age) + + if samesite is not None: + samesite = samesite.title() + + if samesite not in {"Strict", "Lax", "None"}: + raise ValueError("SameSite must be 'Strict', 'Lax', or 'None'.") + + buf = [key + b"=" + _cookie_quote(value)] + + # XXX: In theory all of these parameters that are not marked with `None` + # should be quoted. Because stdlib did not quote it before I did not + # want to introduce quoting there now. + for k, v, q in ( + (b"Domain", domain, True), + (b"Expires", expires, False), + (b"Max-Age", max_age, False), + (b"Secure", secure, None), + (b"HttpOnly", httponly, None), + (b"Path", path, False), + (b"SameSite", samesite, False), + ): + if q is None: + if v: + buf.append(k) + continue + + if v is None: + continue + + tmp = bytearray(k) + if not isinstance(v, (bytes, bytearray)): + v = _to_bytes(str(v), charset) + if q: + v = _cookie_quote(v) + tmp += b"=" + v + buf.append(bytes(tmp)) + + # The return value will be an incorrectly encoded latin1 header for + # consistency with the headers object. + rv = b"; ".join(buf) + rv = rv.decode("latin1") + + # Warn if the final value of the cookie is larger than the limit. If the + # cookie is too large, then it may be silently ignored by the browser, + # which can be quite hard to debug. + cookie_size = len(rv) + + if max_size and cookie_size > max_size: + value_size = len(value) + warnings.warn( + f"The {key.decode(charset)!r} cookie is too large: the value was" + f" {value_size} bytes but the" + f" header required {cookie_size - value_size} extra bytes. The final size" + f" was {cookie_size} bytes but the limit is {max_size} bytes. Browsers may" + f" silently ignore cookies larger than this.", + stacklevel=2, + ) + + return rv + + +def is_byte_range_valid( + start: t.Optional[int], stop: t.Optional[int], length: t.Optional[int] +) -> bool: + """Checks if a given byte content range is valid for the given length. + + .. versionadded:: 0.7 + """ + if (start is None) != (stop is None): + return False + elif start is None: + return length is None or length >= 0 + elif length is None: + return 0 <= start < stop # type: ignore + elif start >= stop: # type: ignore + return False + return 0 <= start < length + + +# circular dependencies +from . import datastructures as ds diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/local.py b/myvenv/lib/python3.10/site-packages/werkzeug/local.py new file mode 100644 index 0000000..2b22227 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/local.py @@ -0,0 +1,690 @@ +import copy +import math +import operator +import sys +import typing as t +import warnings +from functools import partial +from functools import update_wrapper + +from .wsgi import ClosingIterator + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + +try: + from greenlet import getcurrent as _get_ident +except ImportError: + from threading import get_ident as _get_ident + + +def get_ident() -> int: + warnings.warn( + "'get_ident' is deprecated and will be removed in Werkzeug" + " 2.1. Use 'greenlet.getcurrent' or 'threading.get_ident' for" + " previous behavior.", + DeprecationWarning, + stacklevel=2, + ) + return _get_ident() # type: ignore + + +class _CannotUseContextVar(Exception): + pass + + +try: + from contextvars import ContextVar + + if "gevent" in sys.modules or "eventlet" in sys.modules: + # Both use greenlet, so first check it has patched + # ContextVars, Greenlet <0.4.17 does not. + import greenlet + + greenlet_patched = getattr(greenlet, "GREENLET_USE_CONTEXT_VARS", False) + + if not greenlet_patched: + # If Gevent is used, check it has patched ContextVars, + # <20.5 does not. + try: + from gevent.monkey import is_object_patched + except ImportError: + # Gevent isn't used, but Greenlet is and hasn't patched + raise _CannotUseContextVar() from None + else: + if is_object_patched("threading", "local") and not is_object_patched( + "contextvars", "ContextVar" + ): + raise _CannotUseContextVar() + + def __release_local__(storage: t.Any) -> None: + # Can remove when support for non-stdlib ContextVars is + # removed, see "Fake" version below. + storage.set({}) + +except (ImportError, _CannotUseContextVar): + + class ContextVar: # type: ignore + """A fake ContextVar based on the previous greenlet/threading + ident function. Used on Python 3.6, eventlet, and old versions + of gevent. + """ + + def __init__(self, _name: str) -> None: + self.storage: t.Dict[int, t.Dict[str, t.Any]] = {} + + def get(self, default: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: + return self.storage.get(_get_ident(), default) + + def set(self, value: t.Dict[str, t.Any]) -> None: + self.storage[_get_ident()] = value + + def __release_local__(storage: t.Any) -> None: + # Special version to ensure that the storage is cleaned up on + # release. + storage.storage.pop(_get_ident(), None) + + +def release_local(local: t.Union["Local", "LocalStack"]) -> None: + """Releases the contents of the local for the current context. + This makes it possible to use locals without a manager. + + Example:: + + >>> loc = Local() + >>> loc.foo = 42 + >>> release_local(loc) + >>> hasattr(loc, 'foo') + False + + With this function one can release :class:`Local` objects as well + as :class:`LocalStack` objects. However it is not possible to + release data held by proxies that way, one always has to retain + a reference to the underlying local object in order to be able + to release it. + + .. versionadded:: 0.6.1 + """ + local.__release_local__() + + +class Local: + __slots__ = ("_storage",) + + def __init__(self) -> None: + object.__setattr__(self, "_storage", ContextVar("local_storage")) + + @property + def __storage__(self) -> t.Dict[str, t.Any]: + warnings.warn( + "'__storage__' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + return self._storage.get({}) # type: ignore + + @property + def __ident_func__(self) -> t.Callable[[], int]: + warnings.warn( + "'__ident_func__' is deprecated and will be removed in" + " Werkzeug 2.1. It should not be used in Python 3.7+.", + DeprecationWarning, + stacklevel=2, + ) + return _get_ident # type: ignore + + @__ident_func__.setter + def __ident_func__(self, func: t.Callable[[], int]) -> None: + warnings.warn( + "'__ident_func__' is deprecated and will be removed in" + " Werkzeug 2.1. Setting it no longer has any effect.", + DeprecationWarning, + stacklevel=2, + ) + + def __iter__(self) -> t.Iterator[t.Tuple[int, t.Any]]: + return iter(self._storage.get({}).items()) + + def __call__(self, proxy: str) -> "LocalProxy": + """Create a proxy for a name.""" + return LocalProxy(self, proxy) + + def __release_local__(self) -> None: + __release_local__(self._storage) + + def __getattr__(self, name: str) -> t.Any: + values = self._storage.get({}) + try: + return values[name] + except KeyError: + raise AttributeError(name) from None + + def __setattr__(self, name: str, value: t.Any) -> None: + values = self._storage.get({}).copy() + values[name] = value + self._storage.set(values) + + def __delattr__(self, name: str) -> None: + values = self._storage.get({}).copy() + try: + del values[name] + self._storage.set(values) + except KeyError: + raise AttributeError(name) from None + + +class LocalStack: + """This class works similar to a :class:`Local` but keeps a stack + of objects instead. This is best explained with an example:: + + >>> ls = LocalStack() + >>> ls.push(42) + >>> ls.top + 42 + >>> ls.push(23) + >>> ls.top + 23 + >>> ls.pop() + 23 + >>> ls.top + 42 + + They can be force released by using a :class:`LocalManager` or with + the :func:`release_local` function but the correct way is to pop the + item from the stack after using. When the stack is empty it will + no longer be bound to the current context (and as such released). + + By calling the stack without arguments it returns a proxy that resolves to + the topmost item on the stack. + + .. versionadded:: 0.6.1 + """ + + def __init__(self) -> None: + self._local = Local() + + def __release_local__(self) -> None: + self._local.__release_local__() + + @property + def __ident_func__(self) -> t.Callable[[], int]: + return self._local.__ident_func__ + + @__ident_func__.setter + def __ident_func__(self, value: t.Callable[[], int]) -> None: + object.__setattr__(self._local, "__ident_func__", value) + + def __call__(self) -> "LocalProxy": + def _lookup() -> t.Any: + rv = self.top + if rv is None: + raise RuntimeError("object unbound") + return rv + + return LocalProxy(_lookup) + + def push(self, obj: t.Any) -> t.List[t.Any]: + """Pushes a new item to the stack""" + rv = getattr(self._local, "stack", []).copy() + rv.append(obj) + self._local.stack = rv + return rv + + def pop(self) -> t.Any: + """Removes the topmost item from the stack, will return the + old value or `None` if the stack was already empty. + """ + stack = getattr(self._local, "stack", None) + if stack is None: + return None + elif len(stack) == 1: + release_local(self._local) + return stack[-1] + else: + return stack.pop() + + @property + def top(self) -> t.Any: + """The topmost item on the stack. If the stack is empty, + `None` is returned. + """ + try: + return self._local.stack[-1] + except (AttributeError, IndexError): + return None + + +class LocalManager: + """Local objects cannot manage themselves. For that you need a local + manager. You can pass a local manager multiple locals or add them + later by appending them to `manager.locals`. Every time the manager + cleans up, it will clean up all the data left in the locals for this + context. + + .. versionchanged:: 2.0 + ``ident_func`` is deprecated and will be removed in Werkzeug + 2.1. + + .. versionchanged:: 0.6.1 + The :func:`release_local` function can be used instead of a + manager. + + .. versionchanged:: 0.7 + The ``ident_func`` parameter was added. + """ + + def __init__( + self, + locals: t.Optional[t.Iterable[t.Union[Local, LocalStack]]] = None, + ident_func: None = None, + ) -> None: + if locals is None: + self.locals = [] + elif isinstance(locals, Local): + self.locals = [locals] + else: + self.locals = list(locals) + + if ident_func is not None: + warnings.warn( + "'ident_func' is deprecated and will be removed in" + " Werkzeug 2.1. Setting it no longer has any effect.", + DeprecationWarning, + stacklevel=2, + ) + + @property + def ident_func(self) -> t.Callable[[], int]: + warnings.warn( + "'ident_func' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + return _get_ident # type: ignore + + @ident_func.setter + def ident_func(self, func: t.Callable[[], int]) -> None: + warnings.warn( + "'ident_func' is deprecated and will be removedin Werkzeug" + " 2.1. Setting it no longer has any effect.", + DeprecationWarning, + stacklevel=2, + ) + + def get_ident(self) -> int: + """Return the context identifier the local objects use internally for + this context. You cannot override this method to change the behavior + but use it to link other context local objects (such as SQLAlchemy's + scoped sessions) to the Werkzeug locals. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. + + .. versionchanged:: 0.7 + You can pass a different ident function to the local manager that + will then be propagated to all the locals passed to the + constructor. + """ + warnings.warn( + "'get_ident' is deprecated and will be removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + return self.ident_func() + + def cleanup(self) -> None: + """Manually clean up the data in the locals for this context. Call + this at the end of the request or use `make_middleware()`. + """ + for local in self.locals: + release_local(local) + + def make_middleware(self, app: "WSGIApplication") -> "WSGIApplication": + """Wrap a WSGI application so that cleaning up happens after + request end. + """ + + def application( + environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + return ClosingIterator(app(environ, start_response), self.cleanup) + + return application + + def middleware(self, func: "WSGIApplication") -> "WSGIApplication": + """Like `make_middleware` but for decorating functions. + + Example usage:: + + @manager.middleware + def application(environ, start_response): + ... + + The difference to `make_middleware` is that the function passed + will have all the arguments copied from the inner application + (name, docstring, module). + """ + return update_wrapper(self.make_middleware(func), func) + + def __repr__(self) -> str: + return f"<{type(self).__name__} storages: {len(self.locals)}>" + + +class _ProxyLookup: + """Descriptor that handles proxied attribute lookup for + :class:`LocalProxy`. + + :param f: The built-in function this attribute is accessed through. + Instead of looking up the special method, the function call + is redone on the object. + :param fallback: Return this function if the proxy is unbound + instead of raising a :exc:`RuntimeError`. + :param is_attr: This proxied name is an attribute, not a function. + Call the fallback immediately to get the value. + :param class_value: Value to return when accessed from the + ``LocalProxy`` class directly. Used for ``__doc__`` so building + docs still works. + """ + + __slots__ = ("bind_f", "fallback", "is_attr", "class_value", "name") + + def __init__( + self, + f: t.Optional[t.Callable] = None, + fallback: t.Optional[t.Callable] = None, + class_value: t.Optional[t.Any] = None, + is_attr: bool = False, + ) -> None: + bind_f: t.Optional[t.Callable[["LocalProxy", t.Any], t.Callable]] + + if hasattr(f, "__get__"): + # A Python function, can be turned into a bound method. + + def bind_f(instance: "LocalProxy", obj: t.Any) -> t.Callable: + return f.__get__(obj, type(obj)) # type: ignore + + elif f is not None: + # A C function, use partial to bind the first argument. + + def bind_f(instance: "LocalProxy", obj: t.Any) -> t.Callable: + return partial(f, obj) # type: ignore + + else: + # Use getattr, which will produce a bound method. + bind_f = None + + self.bind_f = bind_f + self.fallback = fallback + self.class_value = class_value + self.is_attr = is_attr + + def __set_name__(self, owner: "LocalProxy", name: str) -> None: + self.name = name + + def __get__(self, instance: "LocalProxy", owner: t.Optional[type] = None) -> t.Any: + if instance is None: + if self.class_value is not None: + return self.class_value + + return self + + try: + obj = instance._get_current_object() + except RuntimeError: + if self.fallback is None: + raise + + fallback = self.fallback.__get__(instance, owner) # type: ignore + + if self.is_attr: + # __class__ and __doc__ are attributes, not methods. + # Call the fallback to get the value. + return fallback() + + return fallback + + if self.bind_f is not None: + return self.bind_f(instance, obj) + + return getattr(obj, self.name) + + def __repr__(self) -> str: + return f"proxy {self.name}" + + def __call__(self, instance: "LocalProxy", *args: t.Any, **kwargs: t.Any) -> t.Any: + """Support calling unbound methods from the class. For example, + this happens with ``copy.copy``, which does + ``type(x).__copy__(x)``. ``type(x)`` can't be proxied, so it + returns the proxy type and descriptor. + """ + return self.__get__(instance, type(instance))(*args, **kwargs) + + +class _ProxyIOp(_ProxyLookup): + """Look up an augmented assignment method on a proxied object. The + method is wrapped to return the proxy instead of the object. + """ + + __slots__ = () + + def __init__( + self, f: t.Optional[t.Callable] = None, fallback: t.Optional[t.Callable] = None + ) -> None: + super().__init__(f, fallback) + + def bind_f(instance: "LocalProxy", obj: t.Any) -> t.Callable: + def i_op(self: t.Any, other: t.Any) -> "LocalProxy": + f(self, other) # type: ignore + return instance + + return i_op.__get__(obj, type(obj)) # type: ignore + + self.bind_f = bind_f + + +def _l_to_r_op(op: F) -> F: + """Swap the argument order to turn an l-op into an r-op.""" + + def r_op(obj: t.Any, other: t.Any) -> t.Any: + return op(other, obj) + + return t.cast(F, r_op) + + +class LocalProxy: + """A proxy to the object bound to a :class:`Local`. All operations + on the proxy are forwarded to the bound object. If no object is + bound, a :exc:`RuntimeError` is raised. + + .. code-block:: python + + from werkzeug.local import Local + l = Local() + + # a proxy to whatever l.user is set to + user = l("user") + + from werkzeug.local import LocalStack + _request_stack = LocalStack() + + # a proxy to _request_stack.top + request = _request_stack() + + # a proxy to the session attribute of the request proxy + session = LocalProxy(lambda: request.session) + + ``__repr__`` and ``__class__`` are forwarded, so ``repr(x)`` and + ``isinstance(x, cls)`` will look like the proxied object. Use + ``issubclass(type(x), LocalProxy)`` to check if an object is a + proxy. + + .. code-block:: python + + repr(user) # + isinstance(user, User) # True + issubclass(type(user), LocalProxy) # True + + :param local: The :class:`Local` or callable that provides the + proxied object. + :param name: The attribute name to look up on a :class:`Local`. Not + used if a callable is given. + + .. versionchanged:: 2.0 + Updated proxied attributes and methods to reflect the current + data model. + + .. versionchanged:: 0.6.1 + The class can be instantiated with a callable. + """ + + __slots__ = ("__local", "__name", "__wrapped__") + + def __init__( + self, + local: t.Union["Local", t.Callable[[], t.Any]], + name: t.Optional[str] = None, + ) -> None: + object.__setattr__(self, "_LocalProxy__local", local) + object.__setattr__(self, "_LocalProxy__name", name) + + if callable(local) and not hasattr(local, "__release_local__"): + # "local" is a callable that is not an instance of Local or + # LocalManager: mark it as a wrapped function. + object.__setattr__(self, "__wrapped__", local) + + def _get_current_object(self) -> t.Any: + """Return the current object. This is useful if you want the real + object behind the proxy at a time for performance reasons or because + you want to pass the object into a different context. + """ + if not hasattr(self.__local, "__release_local__"): # type: ignore + return self.__local() # type: ignore + + try: + return getattr(self.__local, self.__name) # type: ignore + except AttributeError: + name = self.__name # type: ignore + raise RuntimeError(f"no object bound to {name}") from None + + __doc__ = _ProxyLookup( # type: ignore + class_value=__doc__, fallback=lambda self: type(self).__doc__, is_attr=True + ) + # __del__ should only delete the proxy + __repr__ = _ProxyLookup( # type: ignore + repr, fallback=lambda self: f"<{type(self).__name__} unbound>" + ) + __str__ = _ProxyLookup(str) # type: ignore + __bytes__ = _ProxyLookup(bytes) + __format__ = _ProxyLookup() # type: ignore + __lt__ = _ProxyLookup(operator.lt) + __le__ = _ProxyLookup(operator.le) + __eq__ = _ProxyLookup(operator.eq) # type: ignore + __ne__ = _ProxyLookup(operator.ne) # type: ignore + __gt__ = _ProxyLookup(operator.gt) + __ge__ = _ProxyLookup(operator.ge) + __hash__ = _ProxyLookup(hash) # type: ignore + __bool__ = _ProxyLookup(bool, fallback=lambda self: False) + __getattr__ = _ProxyLookup(getattr) + # __getattribute__ triggered through __getattr__ + __setattr__ = _ProxyLookup(setattr) # type: ignore + __delattr__ = _ProxyLookup(delattr) # type: ignore + __dir__ = _ProxyLookup(dir, fallback=lambda self: []) # type: ignore + # __get__ (proxying descriptor not supported) + # __set__ (descriptor) + # __delete__ (descriptor) + # __set_name__ (descriptor) + # __objclass__ (descriptor) + # __slots__ used by proxy itself + # __dict__ (__getattr__) + # __weakref__ (__getattr__) + # __init_subclass__ (proxying metaclass not supported) + # __prepare__ (metaclass) + __class__ = _ProxyLookup( + fallback=lambda self: type(self), is_attr=True + ) # type: ignore + __instancecheck__ = _ProxyLookup(lambda self, other: isinstance(other, self)) + __subclasscheck__ = _ProxyLookup(lambda self, other: issubclass(other, self)) + # __class_getitem__ triggered through __getitem__ + __call__ = _ProxyLookup(lambda self, *args, **kwargs: self(*args, **kwargs)) + __len__ = _ProxyLookup(len) + __length_hint__ = _ProxyLookup(operator.length_hint) + __getitem__ = _ProxyLookup(operator.getitem) + __setitem__ = _ProxyLookup(operator.setitem) + __delitem__ = _ProxyLookup(operator.delitem) + # __missing__ triggered through __getitem__ + __iter__ = _ProxyLookup(iter) + __next__ = _ProxyLookup(next) + __reversed__ = _ProxyLookup(reversed) + __contains__ = _ProxyLookup(operator.contains) + __add__ = _ProxyLookup(operator.add) + __sub__ = _ProxyLookup(operator.sub) + __mul__ = _ProxyLookup(operator.mul) + __matmul__ = _ProxyLookup(operator.matmul) + __truediv__ = _ProxyLookup(operator.truediv) + __floordiv__ = _ProxyLookup(operator.floordiv) + __mod__ = _ProxyLookup(operator.mod) + __divmod__ = _ProxyLookup(divmod) + __pow__ = _ProxyLookup(pow) + __lshift__ = _ProxyLookup(operator.lshift) + __rshift__ = _ProxyLookup(operator.rshift) + __and__ = _ProxyLookup(operator.and_) + __xor__ = _ProxyLookup(operator.xor) + __or__ = _ProxyLookup(operator.or_) + __radd__ = _ProxyLookup(_l_to_r_op(operator.add)) + __rsub__ = _ProxyLookup(_l_to_r_op(operator.sub)) + __rmul__ = _ProxyLookup(_l_to_r_op(operator.mul)) + __rmatmul__ = _ProxyLookup(_l_to_r_op(operator.matmul)) + __rtruediv__ = _ProxyLookup(_l_to_r_op(operator.truediv)) + __rfloordiv__ = _ProxyLookup(_l_to_r_op(operator.floordiv)) + __rmod__ = _ProxyLookup(_l_to_r_op(operator.mod)) + __rdivmod__ = _ProxyLookup(_l_to_r_op(divmod)) + __rpow__ = _ProxyLookup(_l_to_r_op(pow)) + __rlshift__ = _ProxyLookup(_l_to_r_op(operator.lshift)) + __rrshift__ = _ProxyLookup(_l_to_r_op(operator.rshift)) + __rand__ = _ProxyLookup(_l_to_r_op(operator.and_)) + __rxor__ = _ProxyLookup(_l_to_r_op(operator.xor)) + __ror__ = _ProxyLookup(_l_to_r_op(operator.or_)) + __iadd__ = _ProxyIOp(operator.iadd) + __isub__ = _ProxyIOp(operator.isub) + __imul__ = _ProxyIOp(operator.imul) + __imatmul__ = _ProxyIOp(operator.imatmul) + __itruediv__ = _ProxyIOp(operator.itruediv) + __ifloordiv__ = _ProxyIOp(operator.ifloordiv) + __imod__ = _ProxyIOp(operator.imod) + __ipow__ = _ProxyIOp(operator.ipow) + __ilshift__ = _ProxyIOp(operator.ilshift) + __irshift__ = _ProxyIOp(operator.irshift) + __iand__ = _ProxyIOp(operator.iand) + __ixor__ = _ProxyIOp(operator.ixor) + __ior__ = _ProxyIOp(operator.ior) + __neg__ = _ProxyLookup(operator.neg) + __pos__ = _ProxyLookup(operator.pos) + __abs__ = _ProxyLookup(abs) + __invert__ = _ProxyLookup(operator.invert) + __complex__ = _ProxyLookup(complex) + __int__ = _ProxyLookup(int) + __float__ = _ProxyLookup(float) + __index__ = _ProxyLookup(operator.index) + __round__ = _ProxyLookup(round) + __trunc__ = _ProxyLookup(math.trunc) + __floor__ = _ProxyLookup(math.floor) + __ceil__ = _ProxyLookup(math.ceil) + __enter__ = _ProxyLookup() + __exit__ = _ProxyLookup() + __await__ = _ProxyLookup() + __aiter__ = _ProxyLookup() + __anext__ = _ProxyLookup() + __aenter__ = _ProxyLookup() + __aexit__ = _ProxyLookup() + __copy__ = _ProxyLookup(copy.copy) + __deepcopy__ = _ProxyLookup(copy.deepcopy) + # __getnewargs_ex__ (pickle through proxy not supported) + # __getnewargs__ (pickle) + # __getstate__ (pickle) + # __setstate__ (pickle) + # __reduce__ (pickle) + # __reduce_ex__ (pickle) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__init__.py b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__init__.py new file mode 100644 index 0000000..6ddcf7f --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__init__.py @@ -0,0 +1,22 @@ +""" +Middleware +========== + +A WSGI middleware is a WSGI application that wraps another application +in order to observe or change its behavior. Werkzeug provides some +middleware for common use cases. + +.. toctree:: + :maxdepth: 1 + + proxy_fix + shared_data + dispatcher + http_proxy + lint + profiler + +The :doc:`interactive debugger ` is also a middleware that can +be applied manually, although it is typically used automatically with +the :doc:`development server `. +""" diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..808b4f30ed57f277a2e1ba161d8574bda85b8b1d GIT binary patch literal 707 zcmYjPv2NQi5S6pkAdr9H8lV+xbQ4meor(?(3b;UkF7!z}i%cYv6;HC=Kk8rf8#-pq z+^timp6DR;0V3|+=uPht#C*oD}mCz-vTrOb)*?Rr~^ zo2$jc3$0nW)<25jf}cZP{@y9eR;#-)k@KiF;7uJ4>hqDMc(wU9x|=|lRDYb%PpWgZ xikW*;u3oF;2wz2L?xE#9`={Ti{!gs5cHWWJWjsDCulr!e4%hRuOtXe%@ef_9!T2rzS7zy08z`!pq3z>A{hX8C+M3r4jZmbBV+9E`O#c*dRuCq7K z3?(zAp5o{JfuMl%n19jV(8qo4Q~rX{r2WpUR-_YmiJhI9Gw1%DGc2~ZLkG`4|MkK2 zCT9Pp#pdH<@hM(?fI&DDN4UaE-H9i?XTEwLD?bgg)}-YUCBVErY1?@V^UkE>I*%OD z7M+)l=zQ%>y22Yf{Vx9^aGY?jEYl?BCMk0EAkif^@m#9#lea!$crs5k%Mu|{dCrw& zv69?K%{bFZews@5m+_-R#y5nTIX6r+k~K3$m@QJ1lsH@%==vE86cmfxNWm=UY$lb= z4g1s6<>`>p>XqvAD=Gm@HrLj3mLTbCnp_08_@bDaZU&vJL-s**Vlnwk#HG&d(&{ z;grW;%3QF$V;GGuXERkmHKrsE6PD!cF+bzuSS6)lwKoMkhUrWd3sM>43@xXsIM;AU z!Ag8^HfKscUr22Y6G`Jr3ClF8$}Asv&ZHV3xN@x@9$f|X;N;0?*1pGMHcQ~HHAByf zMJm`-GAlr^S)p)h8B&z6trGz)w}-q;ZX;$kbxpk9NC=qf2$UsTmATWjOR`6ow2B>q92BbIV)8m7BOLc*P7%J2(fSFP5}piZ_5?XH~N)*MKsxnih4P4Hyy~ zUO}#jFA!|=DS1rUV?vf`u>|XRf^-54vgAP4Wr@|&cSA}7S*i_cIKH&17PJP2NR*6C zx@6PkPu&7A1_QM zy?=K<hBp zc*_B8TkO0HCf963yiwg;H^4Ox`D52{_KR$q+q^_1X@~t$FPk0KfuMt#&`jw&Cv zDj#{Q+O@E%t)y9~Jf_X&p}B{5AFsZNVdbuz$XR)1J8>`r0>;jF?or=UZBSJQL*K6g zEz{Y0=Q~GT1L1W#oEMoKK3h;HmcucsW?H-$9xu(j$cN+Si{z-lH8#waXEHwXfMM<8RD7p{b8n>*|Ztb{V z{pvw;3#v7|2F=;31I<(biWsUL6tpstx~v;Tv3MguR1$R32I%jYtlU@jpTW1@g)h8| zK=>>F%u&Bt`77s{i`jehUHP}1i`MfGSNK<{t za;yX&a$8nMOL&-fR-Kjq9<++q$Kb=)#TO@5-q-a1qJ1>kI^H`uhz^hb@OkCDc8A}i zketc2@&41psx|B2KN4c9SLZEY{$$rd3J;l;H= zd7o^dYnccM$|g4p^rs}}X;hz$a-K<>|9a?=9ONcO=Zd1=Vx@nHq2~u~*9+ay_1qiY zj@x~GqucX(ZWlZMH)pjBwFUlukA#x0D5}CJ%5cY|H19>x^94^YDV-=1MT~9NRNc=@ zx`cRI1rL(gRK7M!ZCmpWjZ}P^%BnS80+H%n>~8o#XjbHiYGLT()weNpoxt^a!4ID2 z{rHaW{on=Rf4r{edjF-j>+SqlpWjly!0D=OmL)1uy-$nnlP``RM*9a3_y2f!^k}jZ sfhhG{iov-)O)T(ixmsLWF>kY{>V8~1{yWODro;tKm$eZ{`n}`-4<-&bT>t<8 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90a0c00231a9420959f41e367909087e14b8f3a9 GIT binary patch literal 6881 zcmbVR&2t>bb)V^(o!uQQ79jW`LLbx^`XE;Uu%u+iWrU_pkr2UH0vQ6fqOCZK!S*b6 zfZ3Tv_bfobyH$xnn^09$k?rzHaTRb>{)JqesvJ_4TPml?B~>}`mYb_+o6PU^%q~78 zl`0w3^!B{&dHwpm-}~r6G&*W)xPJTZ*H(Z2il+THJsiIbJbWLw{1gq>Y8ux$3-lVp zuMrq+vu5fVH+e3|wXK?^`WEQnlPRmOH>`(0Q14QF9_`Mz^s zL=SiD@1DABdv?}wyK&SATq)<~(rF*~oCn^vD?HntYebzL;jgvgIp^)GSHJDtcZ0x- zr9Bt;4KI}5ob&F|?U{FGZUt^vdiE3OLR+lpiPZ>vFN~isJmaxEA8z>~3Ku+=dqRS8 z&X@I8)TyuTsGpV^0KM21LA~P&=?xw>yHQLJ<7-}A_rp#%uFF_>ZX2}GrPvkm15b7$ z2m`{{hf8(4w3-fU!iqJc$chHPNH*k_ zXRhZwYV)csG})v^H_v-TUcg=CcGKn~y!0qnv-#-BnY`>-d<=Jy=b8mR&d)qDYa@Ju zpXKNHd0xcqk~a#D7Qet>z*w2vu;OGgrYv`468E3;nx<%u@HV@ijHS~Jp;O0oI&R!@ z;>dAB$9ouiB6I^sdSc5H(~gL`u^+BEQQY!`?cf@|jyFJb+)iiKRwK(TUpnm}3Y-QQ zMq#kybO=Q$paySGJFZMAc!$`~M8yS@0jwx+T5bqDac6Z$O&|khV$C}DNF3s;rpjqY zb{j3QR2)9^R+pm2h8GWIohDhrD0WsoC5#WYZVa|qJaBhBG3zMdWh?3iTrp9CV>-~u zon|B)?rnKN)FC8HJAO!fc(*}VfeHbuAlqiDImIJoM~f1dp_aVkN{ZbLPl5yY8?n=d zGD+K+O*+DB`VW<9)VN~GJ=qZcDorE3wIYd)M8|g)M{{$jMaW)Y>3sLJhh717+WCoM zNR#NAs>&GHZ&at|PK~HhI1C&KV^gg-?#xxI*WUj2>{a|-oBP()tJkVi(I*Pc?a*--j(vVF--UIZC2T<4qB@)iW)%>cRZpa5)m!kB4$) zUXY4La$u&{bh|<9K%AA8_uN2wD=PzeX#dB>SXr66PLcG+)XIu;C3~}Sv~Ai2@l5rm zf*tXtSK4GOgw-8vU|aZc?1iKj>`G}UYEpM7Co#%|9MV=+7NZcteeH7HupM!~Np3S# zO&X|`!Vyi<{m~*f!<{r*I9qPe^}d!j%p(TkZ~GL9umSbzP-kiIKQ8A@WwIj5f@J`KbNMy6-=02T6+IhHivBQ zhXj#Uv`s5wbAFR7l*L^Cq*XFR!=jXKm%ao)sfDuGaTgYbcD#pfyAvSvu0na!4g!_aU1l}MASA#>O@FN` zRPL6u&XPxNNP$uXmQ+(G_%vGGaHB5$U6dkAXbDA%ra**vp2@5V2PZ8YkRj9LK=Ke~ z5t86=Dlnd49{X(%i4&YXNXF$LhEPK6c&U{1symRs>H5JyEvT(vP)XqC+~X*(5M#fo zbnt}gjVDxJCI%EGR4<>YB(ab7l;krJ5=(e-SA>g=9NrpS)VFXOxaD7>;d-pC>oG%X zsOp4z$DW%eH@Nx8{8;PhJ*{p&)|xt1H;)QEc91*NZfko)w|WNJe9uH%;2J8C{9<26^H$tuMPVa&-y9<(GxNLy~HE{Kef`Z zn&h+4kc=GjWmI>z^x{Fd)x|li|G6e!!q3y7+KSp z^2;I$OIdC2Y^2~^B<+{T8xhB@1w*x2>DYt^H&s7mP*2?-_9 zY-cAKtwWG{CW-tJDTAz9($hav->u%KxkshkY#r6CAJ^-%*MkTNK;D==!Q+IM$--o5 zQop={#)ocyJpNa#ulE^;HKB9uQ2z|=Z;XAd$M*DJu}wM&=uh=3^ppei{m$n`!e*}~ z`bMH}eXa|fYf@#2m(fe?>r}loqIT!TU(-6YpryU6ThDi=zh3yGmn+3Yj}!AYm?Z|* zO$;OxF-A+|pw_k&P!ph#M-{j^+~wI$GKQPV6m~ETL+Xs z);J?KBFBmK^;~RG;r>{AtUqRtjmPHW9Lo4SFFY#s&7KJp6c-aKb?zh&sL?@4(4iwK zqf!%bU1aAMQ3NsdFgiC4E>c_px3c{|U?kRJc^hzg z3~n6iy0*`_iIYlhgNZj4)!Nsaj7GxAY)tEF;!o4D9?mp$wo&87Y`)@<(m*lsR$)@3 z^%Z}kZ-7#q)CBJtacDTeddTC$B^JwSq`2>tq z=4U@K`c`;J%{hlT|A;wb*_=aup5{Q}3((37pO~0gjK}#!n*AB$lfN)_0x zrfcy;56}HP^z!1Q)-Uu5;$M4t?Bk_gLG2^ov)0dI^yNvdm&e)el}`*rK3L<&6Y$Ss z-|iI;&K+v}66E+V{wkiWgY&&&&!$MY`5$+v?`lFCg8T}k21xT#V-V|VS1Pbiq|M`kzxScm;}%zL-^96APwe6 zzsH*F7g_MCu-&pM+`@P!@WQpYB`$zTg$Z)kB!_c{CxnB38Yn384}w|X%QTpUAW8~3 zmS!QXyellRQ8k91IzlG7Mi8MQBN@dD zXcBV)S*%v1var5zZ)sUvq@nZFT%z&vt$T~h^NY*%_vaVyEHAtVe%j)087y%Qh-cnc zdxjRyEDJZ3O;60shYb|*;aZYcb%Mv2kt&gkp4aSc{|Z(lhR-OaQ?wZ^`+$pcNgzG z)xVdNAIyWav|RsSetF^EZ6ukZ-0(a2o+D!vF+-(@Z$SV_5$uhJqxKte)E1P^D0#o@ z@;h#fk4-yIb?2*bL*ZZHru=2NIl_y=zz`d;9|!olfOk(}L%ajpzu=Z7G(%7T5NYrQ zad=UAL!#3wat_VkYDI0g0H;89Sl8jA9%`Fl(ALPP?ZFu5DMusud)NBjxaNoA&-C+?v6H{6n7#r}rrOdm&G0VF>+ z4O6!m``zxJ{XdaR(p26^OainZM-^|ONi4wL3wdJEmn8feaK6L}R1qO&)C{2>5(8fo z5-Wv~ptO~g3GByw!lZ*q3coRRJUTX3de9B2#8r9#qEpZa%EO6`Pkq^eOT0~lA_!?2 znAi|1s|W>|N9iclDoP0mJOZ(Xd38at^By+5^!tRkO$3X+lXNAO)~<}g#KtQFo^R5G z!d-ktbK^)Pxz!z%ij^^OmDajO4f%0xglg6p-zBK17zC<%7O1{MJPd3pv9oFTwpkm& z2~zBk?!q-OMVwxvc@PF-w8dSbka%iGpId1tp}JP7UCdHX;iHNk-2Kmqph}~WW1(st zRP|+g+yG2i`I&8%7=h-TKEWoKsTbG<{eo_*-nd@SZQaB?YRjmkY`}_v3UKL#Kd?Nb z-gEPLAy*(OE9>W2iIr$JE3g9IL?65?b`d<;?@A+OtToQqA6af3JS=8DGoQU!v`V`( z|0keU@`9>Rq3mHcTxjVEo@=%%8@`{0n$%3I@by&;RdT6%PN}R#TvT>gqh4A|4x&8= zNzw}?ql~@4!JgAyU>Bd6(HASq!D}fRsFF(|LFHS6LfhaX7?b$7a7zMVPZg9@`K)Uw z4skZ48Y-(4a($e;4vb#zP#2d_8<{;ihvg29KK_%i`RZl~=P@M5J*w*2Vy$@p=JG=Q z?&5p*uw|+d)5=-7udND8N#9Py28N(Ug_$Gxk`JeEDMTr4DfJv>P?XyAU8-LHPs~;M zvj8wn03>A?o;|b8FREtn3WD$`tw05OWh?=!&~1D5dSch>_y*Mts9&nrH@j|-jpXZf z9yRLq)YkbAfM#+LGPk%)O$AM2_+c!lj!^p&gmYzf(Wc(f4u;zh6yqrc${T138YKS? z;QV`-`PoGS07Wgpa_slavY)c^FuyXrf&jChvF#%;l%FYHm9t_KoKVv5C;$*TLJ9 uEqwn}bJV%OrX>{EXh&bR)ueDe)$I++TJl9SrdBB81Pb>||E#PxVf-&tlQiZ4 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/lint.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/lint.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ab4b299d4975d1540e81e1fe71c3411b28e8957 GIT binary patch literal 12748 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c698126f0de2d826f84dd8502d32edea398282db GIT binary patch literal 4998 zcmb_g-EZ606(=c*qG|avPTRBtcDD{_g~gJ)V%VytMY7aQjUkR#rwA%wpeWuen+`>) zmsfV=$WI9n_o3S#&d<(7kIDX#R~ zmTMY^F5c{TrsuUh(|BMc3$`Ij)V4M zYf-l=XfL&vOyiEhT|V>F;4@FG)-t!YjhYv{V;Y8ctKW~KFi=sNuq~N(qgY7qy-P07 zyT2P{te5gZELdNr`w~iiU_w#zYYAGATYAh)l7ANbO==cujC< zw!`sW?J>hT)7--#VY?#kGw@DS)E%-;s#Giz5kAaVl(1nskSt8O@bq5205b&3;`AU& zb{J0&##4fGwd$T%ud`sF(l8FPtl2!%^X{4Cp4SZ1{!m6cyQ;~4wYK&fwiU#&P?^_+ zc|?+lCi~s?2di(d-j0JoCcJ;bzs6?G%*)#;f)_B`Q=SyAnlt{E7gCD7>cs&M%nx4ZLvo-aHtx90gGU*gOB3ZK&}7p@sy zhp+IfPwmzsw*i@#^5wCP^4xU6m5O0JkU<|%AT8OH=~nu%#p2qsvb7F!*m-{dkaY%xmU=$O;csR&Z+KUdstTSYN zAej)~Zcpa<-Bk7h)ozo>T98PQ83G2QVCr=!i71R?Vphym;5E=}nmwU*n`FNFn2iot z&4&EeP2+Khbt4gTaNQ50IOxQpSxCGJ5478liA~CnV53z8w3w*HivD-mo%Q?I+19;n zP@%%Q=09HA57h393#pf-lD#3*RK3Bn-E6TNW++^Db&a(tH6}UQFVXw1r zyt|NNoB*L+AP9&g0MOzAL}kz)a#Q2U(Dub z@&R+~y-Oy%iGf*J_hTBv{xYcy$N#lkPnY^6+)!002O!8y>vCek&$K&kOtgm&CdRhH3EzmF8Re1@9Mu})_!~C7}QDaL-Kx6x}LA04Fku`clAZ@=9N1a9y9dFm) zT5Dv;h^ze|d>HIt&Llz_=MmBvODpUBA(Zl?BvQVA7u#nfmowv-#m_SjU)vg&%jdOF zeX=62j1#_euq>ZP-a-O|WpqI(ZFGhukjX`w(@g5i-bY=&V|;G$m1ov7yQ@DPp3Z!3 z@T<>^XD0eeU7KpZr`4m9GM*SmwlYu5Q{$*SvQMm0>C{B=@0^s5tyA;JDaJW@G3C3*r4#4a#7sANjr#T~$0n~_GeA*Kp5U|L)I6EtGu%71`gi&4 zsKTo&M!}WOePJKD%G;a8Zs0v~Ppu;tQdH4`6zH4N`+cS6N49>O|H3+&8O`i1;BE0w z_^eS&oZ54I@e2zp%#&q&sw{zzhmqxx_oek0>wFw5ui$NEvhquVU%4>u?3h%kZeB1i znU!pgFE%4v1~~@U0eI_430oR_fVEsgH_CDcxE2Yol_=x_HOTVHt_V2tT<(fTArR3` z5!!PTU;3St59Lp=xIpPOwDOnnK^5*Iozs~&<@gQ;GZLf<7#-fY6hM?Me7}AJxjl%p z_v=5vN-ly;i2LjUY7B~Mt>kaffM=m7JOubd*Y8+c9h;8zXLUeQMi6vhh87Jja z`oM%4=&qGEHA3@p7^nDraU_{6H&t$jL8fv?JVM#bVVEEbl#=pw4A;z-3)A_CFj=$Z z&(S4cBl0sMuMp8utnK5-L4wI$^kx+0wplgp@4b>|(ev<^Kguu#kAGn~0_@}UKYX&j zec!*ke*dF;AGBt+Zr%UL-`M={-Zy3=H|x1g_2UlBHE#a-X3fr>q9o02%ESoQqHdJ* z{a!P5COX_&{XbuTmrV&UHx3;Vu*q`MD}ZNde{R;UO2VoZqg8>1gofPSOQS@-gn`_p z4l3Q`%k=6L)p}m0(gXQe3RDcC$gMP!4NPcxe9-F`m6s%7NXq#w@4Oz9E5xU)%cZ=u zBUB4#$ywL9Ki# zKs0X+5TxEB4*m#s8G+d@Em|wKGvk^bepS=6h&q;IzEY&*Zxj&9Rq*I74Ui2BLg}F@I+`UnlosK;tV<5KS&bcQ2s-|Tvf-XWO Mr&L9&YA%=l2j`ifjQ{`u literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b9961cb8528cba98461423d611d99987b49a14b GIT binary patch literal 6209 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..841b379a3b9c726a4b5efe91e8943eecbd8c8a5a GIT binary patch literal 9925 zcmbVS-E-SkcE=Y<5F)7$%b)o|zASTVNlvo)sAioyk)1@{L{1~6nXtPrU+c&H+G4ww0!Z zjLVCQi*wIC=XXAEV82|p6dYgw!%J&_{{uz&C%QO%-L6Yi0CJ zQS>U^@!B}|EpMVbS)1hk82VGSDejlN6W!_BH22Hi$?mDzDehOi)7>+*Gu$6X|7`87 zs<`=$bL-STf%8fCsk4e(aG!4JXO)(AR;fKh_i@_pvuBlcHJ!&-Jo-#BkLNyzk>};F zu&W90h3<>B7qOBmCec4%JJ0Ux$8!Vg@^Ob{&CS`)AoQD_69fwj>C;!8(D|Svgy(L7m}TQw=(^oC zR|X4qC#XA~=Wn^fvKE?te_M9e+u?%!@}*0!+aKbV8wS>b*J--Fz+JH4Ub#B^%Iu=& z3NLGd)xk2B@{xC59$;!J+Z}t1wF+}bY zRUW$5yV;R`uM5ge+V$yeJ6MH8yfM^MDog zs5Q|R2r}7;v0^%Y?3`q_C<;wjt|i9YF;Q|0qAavlMO4K2?Od%SCTPU9T85YuQ@4#; zIUT8BgaBvM#?z4rjGPpwFfu7l3maEl(%dWzug_7 z)!u4%nr*u;{hJ-(26m7bLaJlC_OnHyRZQJ#-I~o22WgAu$T;)2?l%_+I}z^X-U6pZ@F@F=nmHB4)~l4*iZ?r zPWIU2c6mVC>G$o+kG0SXxOLI~*=SATWM)2DU}gc_%n_XD2=f=R3(=81r|Vv*GqCIR z>P34deWE%dv{Ox%Uv0Z~DnUr7Wrw(K`JRAy8jZPhrAEWv>Uf^r^SA6Z*LH3?9nV>V zApIT_Y&6I`7v|^PTMh|$uIYEvN9RGswpZJbEdKSS8;08uw%ZzbwirlQ_ajPMbDQK+ zY=<=56OIQ5Vs~B9!9b!oQ0n=iEnO%JE^#h#hIZ5G*^U?Z;E~zt`U9B10GGTi>EoGn zZ=eh6v?tnpr#Eoz&?mllCWM~ZJZLdDOpt_=NYZBI9_XCV2ahTOFyPsPUKYi7%Wm{H*GKL!Xpoo> z6twLR4#X0=;Enchh;Q)4LS@d__u7o>zSG?FEv93kG|-F&zmwxRlTC#k?zv3Mty|?Uz)}Ys1X#8QT*LA{XdoKM=7LfylE7HE52rq0qp-uWD zfI7{l8w4G)6c#GUe#>2hL6F{>PP6T@zTk)e5N5jZ4iJpdo)1QvZL*KH>%bhLRZtD! z+0vaX4y44DxBak6g%6Hs`C!mzpVjkwvu~{|E-f*SFYlfgJ9r?0=Eol`*_{v*wwd0RFE{PA0ro^@-}DEb$n4Ty+a{^Ne`6o^Cs-y# zBwfcQg2DPaECn`5duDc-nBT)q=7L?a|S$giyR=OPD3d= z8eOC)bSqIOvI!_OQBWtGL0ssuqr>)Fo%I1U;PbxVG~wh)Z`e04)*{1SgViwCnBSlg z8mNz!NO@ggu-TC80&{oBEJ)6jlO!CF^$Nc?gC(3EL4@zwPU3Yb7G%d^5}D2Sk@yjN z3+^qzci9i$fj<vbjXHR2wZq}_+v6n(syb_N9a7F zp#?-L*lMac_{kJDL}bw0MEvU|;z%`#92<#50B-^~^wwR0MEj+=ODtiM5m6Iv%Nckf z!f8hL01#yTqsfiNAuZ1PO{CGYFb~)1QVfN?I=nWSf$jPJW&jVhNscOXZzbstEt&Cr zk$2r_48qpz8}OGSyuy(P0kr~6oy?aU?1S%N7nkN<8!`44&wnic-yw%fbFV%^fwhz= z4!hzB%d6jf{g;Yz@62)Ak9FLU6UeILLJC;CW~8n=&N0!re1vv9`G~ONJd%%r>@^D@ zlpXXoo5QK&4E_d9q((|zSy#}ik%3l=a%lC?h_o=btI3m*wx7SN>}Zj8Rr%?wKhbsy zJ0|X$VF7o~N6Lm7nZHzaioK_Kw20Bmp|vr#Uy97Ts(d|C_RG}UDc-n>ekJMm3gNg= zBl8RGOKn&EV{oyd?oUL8sMyl(s;YA1^d8=)2dbbdhP@DgkOt@*^c&aWUpgWqw3`R+FIoSXAr zXKlXEVfmH0moCi*NVR9v_#DhC1p6j z!uj^mSBSYf-tjtNy*~FN&u=28zKKrqY;{_mqz#;+<_tAw(Zp5jrTSZwa0oPbDvpoEFtRpJH;X!#-op4`PGT@HpQI$XUkY*^6v8Z<+>>m%Gi-m)qHB zaTRgrnv}kb^FX-U6D(QcBzqB;=fGOkijBbaT8G$XHH>)YS+Tz9ZpXQsjyG`Qe8M0r zNj^(^e2(^LW>Rr+6`V(tFXdJGvoupJX_i`2>BOH_(WW$0E2$Hjr466@P8r80ZJQiZ zlXQ|D)F)&u;tUKlLIp6j;a{x`*0?NzJcY6?A3jtfC4k35z5tio_SrpUCY}UI&ZXeB zMoy*a%FuzQPtpL}eh-xkdaXl>k-Q^PB5^3iOH)OJ1!!$Rni!Efyf9cGhPGrUMC~AP zQRG-xS(q?^m$4K1IyGbhNkh_=FQNGyCj-;S4WD_e&<>RQh_J{6*%Ofo-cedyHM?y> zX!>#!Lq`rOwXwZkLMMc^3UyZt)eSAwH;nzWbS8eG`FxKQ6 ztXd0)gfAcPlccb?OsOgbQrJSbFHz-FgFwvOmDYc>+-wx*%i1ORiBl2+}edbSak+=kP z_~dsHKVIw=kVsuB%9fenA9j?4WEMO*kdXYeMuOc__jUShp=u|e!mX-tSa@p=a(7sW zT*NK;3>rX*CB=9;Dy~1nZQjc%W%$}-_wta3`QHV0jh1D7%%b;2wm?N4zVLk(hzqHo zj!S8W3u#S)LUvFfrQk|q!QY|@m3=kTHuSsdXKDycMk;!GnBPPB7h&<`J$=8xV}<}m zH;SQkRuMXSW7!q|mePutaYwzQ-7#7^2fWgb9s$XqQ6?HYMp)SxN60j=hQj?xZl`Eu zPrIvqmiwNoAgJEa@8s^}TRGI~a<|i!s)gnF1j&6=dqi%AD8SYspTK^P#8eFfgm{KZ zgib^OT#+bsuaLcJ5#sAkHaf|pfKmhq?Wn^7_N7z*GvyPdXBkRpY@ zc$kMAUyD?5jH)4}2DeQDAV$c27}<3-xBQj1{HPNll`(@;=QY-Tcf$nO6dWdDsMF{RvFIu>UcqLQF>faQm;vk2Y7z2Bn|w(3{W`!&vh9yWD@BDzMcrd1Hn z^Q!j1(lx|$^zkU)Q{_w&tqAFXO*Rmzd%1n2v56yP zJ188n19SV0qjo@8zoQ*Sf7Mu)OW@@ZoRCaMaHp8L3%4Bt_DA5(qL;smxf#+8DuX5B zKkBIenI2eVb+Yx89a+StDMpMnjW#k2%$?&%iUMn`! zTDOc1DqeEtQB~z9^w>!0e}}8UMsrx{1}R#t98&(2UOkZncpa~?9KspUaO|7w1C&Bz zi+o#Ra&e*U1Wp)A`5p+zY8ac>)1sNI(nxNVb7Hf;dYuDyT*?9xo6Bh1ao)X!G8z(O zDpALUv^HJMC0W!vpv8ce1mD_3<_NNMGLe5leoFI3=$AFR{C#TvfEo^N?!5U<#zYf5jP4 z7%_EACD)@pF!YjkN;S0MZyzh;kxWzMJ?#8}lm=)@hZ=&OT%(388xD~*&kPX6EiF2It#zlf;owD4x4BzXqt@0ZWDJDB8C#RDn7FkD_Fge1wye zcs{P+twM4N#a~$^eJ?$dL{gcD%!x(#OCTR2#7oPLE+8d7{8Rhmhfz&WKALWGlI)n@*=n)bEc^Q=ws_En)e&rYV%Vqi7V`zG>ZC9F){_H^gjiMme3LM@a(Sg6Me_p z8QUodBOKdBg0`;ilrf_ue~q@hQ{np0SX2TPeP=u>3nZM;H~^6UQb$0;XX2=Wt0G;k zYuG_K9LMV8(U`D4*Mnb0*8ap@q=byNp8DLq@*;iLm|etoHR*dpv+LZN#h0YZL-V5DAFScST2-x;@xKzn(YO2I z#4DG6XroeOe~^C7uNAJ{>Z4u~R5iIpxXCAv4ogHA9}i}!b~3DFSMR&M^{_oGzYnsr z8IfTg|8v2<{^RN?Sq2R*_{1j4EWuU>)f$VT$o(YPzeJNx(rU%5iW(p)(pLoj9|W>Q z6R091i_{#{UvAK)g$C6~&&LNfdMB5Zl~Mo%zasHnWJ1-6#2h6}38*2*S{q{rlZM~1 zz;$G^&97J-8rfuPCFaFN&W+92m`U!&YDn!yqoC>8_4^H76Lam|Y zQA4s+?OWr4Rj|}!>qg1S@NTP;@-aD$rh10zQ{+?Y_1LNtI`Az9{ZhStW8iq{okG1X z{ARr_8+azpecVI!9T-Hx?jSzI<`O>T(RYN{So3{PUcp@X8a1rsCiTd}V8zwq!$D>WH)e1Kl%AN1p=pa6WP|koH^@Bh8@jEYM<~SK zw^~8_FC^vvsTH;VfUtg}jamObbz!P7RXC+jeQ@ps#%Lx^Q>)yUM7dg(d-#z*qlRPi zPpL=Rit|7LN?a_5gk6dhvDWdE1eW1v3{i8H6b54JP`ss()>WlAP zTm13T^1HPP<2GpH<2_TRFu)aljuO1}$4MIn-~<(KQc4uOjK)w*%T$v;E?g2sQNBFX OS8eU0nomcj^#1`CmP-%- literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/dispatcher.py b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/dispatcher.py new file mode 100644 index 0000000..ace1c75 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/dispatcher.py @@ -0,0 +1,78 @@ +""" +Application Dispatcher +====================== + +This middleware creates a single WSGI application that dispatches to +multiple other WSGI applications mounted at different URL paths. + +A common example is writing a Single Page Application, where you have a +backend API and a frontend written in JavaScript that does the routing +in the browser rather than requesting different pages from the server. +The frontend is a single HTML and JS file that should be served for any +path besides "/api". + +This example dispatches to an API app under "/api", an admin app +under "/admin", and an app that serves frontend files for all other +requests:: + + app = DispatcherMiddleware(serve_frontend, { + '/api': api_app, + '/admin': admin_app, + }) + +In production, you might instead handle this at the HTTP server level, +serving files or proxying to application servers based on location. The +API and admin apps would each be deployed with a separate WSGI server, +and the static files would be served directly by the HTTP server. + +.. autoclass:: DispatcherMiddleware + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import typing as t + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class DispatcherMiddleware: + """Combine multiple applications as a single WSGI application. + Requests are dispatched to an application based on the path it is + mounted under. + + :param app: The WSGI application to dispatch to if the request + doesn't match a mounted path. + :param mounts: Maps path prefixes to applications for dispatching. + """ + + def __init__( + self, + app: "WSGIApplication", + mounts: t.Optional[t.Dict[str, "WSGIApplication"]] = None, + ) -> None: + self.app = app + self.mounts = mounts or {} + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + script = environ.get("PATH_INFO", "") + path_info = "" + + while "/" in script: + if script in self.mounts: + app = self.mounts[script] + break + + script, last_item = script.rsplit("/", 1) + path_info = f"/{last_item}{path_info}" + else: + app = self.mounts.get(script, self.app) + + original_script_name = environ.get("SCRIPT_NAME", "") + environ["SCRIPT_NAME"] = original_script_name + script + environ["PATH_INFO"] = path_info + return app(environ, start_response) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/http_proxy.py b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/http_proxy.py new file mode 100644 index 0000000..1cde458 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/http_proxy.py @@ -0,0 +1,230 @@ +""" +Basic HTTP Proxy +================ + +.. autoclass:: ProxyMiddleware + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import typing as t +from http import client + +from ..datastructures import EnvironHeaders +from ..http import is_hop_by_hop_header +from ..urls import url_parse +from ..urls import url_quote +from ..wsgi import get_input_stream + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class ProxyMiddleware: + """Proxy requests under a path to an external server, routing other + requests to the app. + + This middleware can only proxy HTTP requests, as HTTP is the only + protocol handled by the WSGI server. Other protocols, such as + WebSocket requests, cannot be proxied at this layer. This should + only be used for development, in production a real proxy server + should be used. + + The middleware takes a dict mapping a path prefix to a dict + describing the host to be proxied to:: + + app = ProxyMiddleware(app, { + "/static/": { + "target": "http://127.0.0.1:5001/", + } + }) + + Each host has the following options: + + ``target``: + The target URL to dispatch to. This is required. + ``remove_prefix``: + Whether to remove the prefix from the URL before dispatching it + to the target. The default is ``False``. + ``host``: + ``""`` (default): + The host header is automatically rewritten to the URL of the + target. + ``None``: + The host header is unmodified from the client request. + Any other value: + The host header is overwritten with the value. + ``headers``: + A dictionary of headers to be sent with the request to the + target. The default is ``{}``. + ``ssl_context``: + A :class:`ssl.SSLContext` defining how to verify requests if the + target is HTTPS. The default is ``None``. + + In the example above, everything under ``"/static/"`` is proxied to + the server on port 5001. The host header is rewritten to the target, + and the ``"/static/"`` prefix is removed from the URLs. + + :param app: The WSGI application to wrap. + :param targets: Proxy target configurations. See description above. + :param chunk_size: Size of chunks to read from input stream and + write to target. + :param timeout: Seconds before an operation to a target fails. + + .. versionadded:: 0.14 + """ + + def __init__( + self, + app: "WSGIApplication", + targets: t.Mapping[str, t.Dict[str, t.Any]], + chunk_size: int = 2 << 13, + timeout: int = 10, + ) -> None: + def _set_defaults(opts: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: + opts.setdefault("remove_prefix", False) + opts.setdefault("host", "") + opts.setdefault("headers", {}) + opts.setdefault("ssl_context", None) + return opts + + self.app = app + self.targets = { + f"/{k.strip('/')}/": _set_defaults(v) for k, v in targets.items() + } + self.chunk_size = chunk_size + self.timeout = timeout + + def proxy_to( + self, opts: t.Dict[str, t.Any], path: str, prefix: str + ) -> "WSGIApplication": + target = url_parse(opts["target"]) + host = t.cast(str, target.ascii_host) + + def application( + environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + headers = list(EnvironHeaders(environ).items()) + headers[:] = [ + (k, v) + for k, v in headers + if not is_hop_by_hop_header(k) + and k.lower() not in ("content-length", "host") + ] + headers.append(("Connection", "close")) + + if opts["host"] == "": + headers.append(("Host", host)) + elif opts["host"] is None: + headers.append(("Host", environ["HTTP_HOST"])) + else: + headers.append(("Host", opts["host"])) + + headers.extend(opts["headers"].items()) + remote_path = path + + if opts["remove_prefix"]: + remote_path = remote_path[len(prefix) :].lstrip("/") + remote_path = f"{target.path.rstrip('/')}/{remote_path}" + + content_length = environ.get("CONTENT_LENGTH") + chunked = False + + if content_length not in ("", None): + headers.append(("Content-Length", content_length)) # type: ignore + elif content_length is not None: + headers.append(("Transfer-Encoding", "chunked")) + chunked = True + + try: + if target.scheme == "http": + con = client.HTTPConnection( + host, target.port or 80, timeout=self.timeout + ) + elif target.scheme == "https": + con = client.HTTPSConnection( + host, + target.port or 443, + timeout=self.timeout, + context=opts["ssl_context"], + ) + else: + raise RuntimeError( + "Target scheme must be 'http' or 'https', got" + f" {target.scheme!r}." + ) + + con.connect() + remote_url = url_quote(remote_path) + querystring = environ["QUERY_STRING"] + + if querystring: + remote_url = f"{remote_url}?{querystring}" + + con.putrequest(environ["REQUEST_METHOD"], remote_url, skip_host=True) + + for k, v in headers: + if k.lower() == "connection": + v = "close" + + con.putheader(k, v) + + con.endheaders() + stream = get_input_stream(environ) + + while True: + data = stream.read(self.chunk_size) + + if not data: + break + + if chunked: + con.send(b"%x\r\n%s\r\n" % (len(data), data)) + else: + con.send(data) + + resp = con.getresponse() + except OSError: + from ..exceptions import BadGateway + + return BadGateway()(environ, start_response) + + start_response( + f"{resp.status} {resp.reason}", + [ + (k.title(), v) + for k, v in resp.getheaders() + if not is_hop_by_hop_header(k) + ], + ) + + def read() -> t.Iterator[bytes]: + while True: + try: + data = resp.read(self.chunk_size) + except OSError: + break + + if not data: + break + + yield data + + return read() + + return application + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + path = environ["PATH_INFO"] + app = self.app + + for prefix, opts in self.targets.items(): + if path.startswith(prefix): + app = self.proxy_to(opts, path, prefix) + break + + return app(environ, start_response) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/lint.py b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/lint.py new file mode 100644 index 0000000..c74703b --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/lint.py @@ -0,0 +1,420 @@ +""" +WSGI Protocol Linter +==================== + +This module provides a middleware that performs sanity checks on the +behavior of the WSGI server and application. It checks that the +:pep:`3333` WSGI spec is properly implemented. It also warns on some +common HTTP errors such as non-empty responses for 304 status codes. + +.. autoclass:: LintMiddleware + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import typing as t +from types import TracebackType +from urllib.parse import urlparse +from warnings import warn + +from ..datastructures import Headers +from ..http import is_entity_header +from ..wsgi import FileWrapper + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class WSGIWarning(Warning): + """Warning class for WSGI warnings.""" + + +class HTTPWarning(Warning): + """Warning class for HTTP warnings.""" + + +def check_type(context: str, obj: object, need: t.Type = str) -> None: + if type(obj) is not need: + warn( + f"{context!r} requires {need.__name__!r}, got {type(obj).__name__!r}.", + WSGIWarning, + stacklevel=3, + ) + + +class InputStream: + def __init__(self, stream: t.IO[bytes]) -> None: + self._stream = stream + + def read(self, *args: t.Any) -> bytes: + if len(args) == 0: + warn( + "WSGI does not guarantee an EOF marker on the input stream, thus making" + " calls to 'wsgi.input.read()' unsafe. Conforming servers may never" + " return from this call.", + WSGIWarning, + stacklevel=2, + ) + elif len(args) != 1: + warn( + "Too many parameters passed to 'wsgi.input.read()'.", + WSGIWarning, + stacklevel=2, + ) + return self._stream.read(*args) + + def readline(self, *args: t.Any) -> bytes: + if len(args) == 0: + warn( + "Calls to 'wsgi.input.readline()' without arguments are unsafe. Use" + " 'wsgi.input.read()' instead.", + WSGIWarning, + stacklevel=2, + ) + elif len(args) == 1: + warn( + "'wsgi.input.readline()' was called with a size hint. WSGI does not" + " support this, although it's available on all major servers.", + WSGIWarning, + stacklevel=2, + ) + else: + raise TypeError("Too many arguments passed to 'wsgi.input.readline()'.") + return self._stream.readline(*args) + + def __iter__(self) -> t.Iterator[bytes]: + try: + return iter(self._stream) + except TypeError: + warn("'wsgi.input' is not iterable.", WSGIWarning, stacklevel=2) + return iter(()) + + def close(self) -> None: + warn("The application closed the input stream!", WSGIWarning, stacklevel=2) + self._stream.close() + + +class ErrorStream: + def __init__(self, stream: t.IO[str]) -> None: + self._stream = stream + + def write(self, s: str) -> None: + check_type("wsgi.error.write()", s, str) + self._stream.write(s) + + def flush(self) -> None: + self._stream.flush() + + def writelines(self, seq: t.Iterable[str]) -> None: + for line in seq: + self.write(line) + + def close(self) -> None: + warn("The application closed the error stream!", WSGIWarning, stacklevel=2) + self._stream.close() + + +class GuardedWrite: + def __init__(self, write: t.Callable[[bytes], None], chunks: t.List[int]) -> None: + self._write = write + self._chunks = chunks + + def __call__(self, s: bytes) -> None: + check_type("write()", s, bytes) + self._write(s) + self._chunks.append(len(s)) + + +class GuardedIterator: + def __init__( + self, + iterator: t.Iterable[bytes], + headers_set: t.Tuple[int, Headers], + chunks: t.List[int], + ) -> None: + self._iterator = iterator + self._next = iter(iterator).__next__ + self.closed = False + self.headers_set = headers_set + self.chunks = chunks + + def __iter__(self) -> "GuardedIterator": + return self + + def __next__(self) -> bytes: + if self.closed: + warn("Iterated over closed 'app_iter'.", WSGIWarning, stacklevel=2) + + rv = self._next() + + if not self.headers_set: + warn( + "The application returned before it started the response.", + WSGIWarning, + stacklevel=2, + ) + + check_type("application iterator items", rv, bytes) + self.chunks.append(len(rv)) + return rv + + def close(self) -> None: + self.closed = True + + if hasattr(self._iterator, "close"): + self._iterator.close() # type: ignore + + if self.headers_set: + status_code, headers = self.headers_set + bytes_sent = sum(self.chunks) + content_length = headers.get("content-length", type=int) + + if status_code == 304: + for key, _value in headers: + key = key.lower() + if key not in ("expires", "content-location") and is_entity_header( + key + ): + warn( + f"Entity header {key!r} found in 304 response.", HTTPWarning + ) + if bytes_sent: + warn("304 responses must not have a body.", HTTPWarning) + elif 100 <= status_code < 200 or status_code == 204: + if content_length != 0: + warn( + f"{status_code} responses must have an empty content length.", + HTTPWarning, + ) + if bytes_sent: + warn(f"{status_code} responses must not have a body.", HTTPWarning) + elif content_length is not None and content_length != bytes_sent: + warn( + "Content-Length and the number of bytes sent to the" + " client do not match.", + WSGIWarning, + ) + + def __del__(self) -> None: + if not self.closed: + try: + warn( + "Iterator was garbage collected before it was closed.", WSGIWarning + ) + except Exception: + pass + + +class LintMiddleware: + """Warns about common errors in the WSGI and HTTP behavior of the + server and wrapped application. Some of the issues it checks are: + + - invalid status codes + - non-bytes sent to the WSGI server + - strings returned from the WSGI application + - non-empty conditional responses + - unquoted etags + - relative URLs in the Location header + - unsafe calls to wsgi.input + - unclosed iterators + + Error information is emitted using the :mod:`warnings` module. + + :param app: The WSGI application to wrap. + + .. code-block:: python + + from werkzeug.middleware.lint import LintMiddleware + app = LintMiddleware(app) + """ + + def __init__(self, app: "WSGIApplication") -> None: + self.app = app + + def check_environ(self, environ: "WSGIEnvironment") -> None: + if type(environ) is not dict: + warn( + "WSGI environment is not a standard Python dict.", + WSGIWarning, + stacklevel=4, + ) + for key in ( + "REQUEST_METHOD", + "SERVER_NAME", + "SERVER_PORT", + "wsgi.version", + "wsgi.input", + "wsgi.errors", + "wsgi.multithread", + "wsgi.multiprocess", + "wsgi.run_once", + ): + if key not in environ: + warn( + f"Required environment key {key!r} not found", + WSGIWarning, + stacklevel=3, + ) + if environ["wsgi.version"] != (1, 0): + warn("Environ is not a WSGI 1.0 environ.", WSGIWarning, stacklevel=3) + + script_name = environ.get("SCRIPT_NAME", "") + path_info = environ.get("PATH_INFO", "") + + if script_name and script_name[0] != "/": + warn( + f"'SCRIPT_NAME' does not start with a slash: {script_name!r}", + WSGIWarning, + stacklevel=3, + ) + + if path_info and path_info[0] != "/": + warn( + f"'PATH_INFO' does not start with a slash: {path_info!r}", + WSGIWarning, + stacklevel=3, + ) + + def check_start_response( + self, + status: str, + headers: t.List[t.Tuple[str, str]], + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ], + ) -> t.Tuple[int, Headers]: + check_type("status", status, str) + status_code_str = status.split(None, 1)[0] + + if len(status_code_str) != 3 or not status_code_str.isdigit(): + warn("Status code must be three digits.", WSGIWarning, stacklevel=3) + + if len(status) < 4 or status[3] != " ": + warn( + f"Invalid value for status {status!r}. Valid status strings are three" + " digits, a space and a status explanation.", + WSGIWarning, + stacklevel=3, + ) + + status_code = int(status_code_str) + + if status_code < 100: + warn("Status code < 100 detected.", WSGIWarning, stacklevel=3) + + if type(headers) is not list: + warn("Header list is not a list.", WSGIWarning, stacklevel=3) + + for item in headers: + if type(item) is not tuple or len(item) != 2: + warn("Header items must be 2-item tuples.", WSGIWarning, stacklevel=3) + name, value = item + if type(name) is not str or type(value) is not str: + warn( + "Header keys and values must be strings.", WSGIWarning, stacklevel=3 + ) + if name.lower() == "status": + warn( + "The status header is not supported due to" + " conflicts with the CGI spec.", + WSGIWarning, + stacklevel=3, + ) + + if exc_info is not None and not isinstance(exc_info, tuple): + warn("Invalid value for exc_info.", WSGIWarning, stacklevel=3) + + headers = Headers(headers) + self.check_headers(headers) + + return status_code, headers + + def check_headers(self, headers: Headers) -> None: + etag = headers.get("etag") + + if etag is not None: + if etag.startswith(("W/", "w/")): + if etag.startswith("w/"): + warn( + "Weak etag indicator should be upper case.", + HTTPWarning, + stacklevel=4, + ) + + etag = etag[2:] + + if not (etag[:1] == etag[-1:] == '"'): + warn("Unquoted etag emitted.", HTTPWarning, stacklevel=4) + + location = headers.get("location") + + if location is not None: + if not urlparse(location).netloc: + warn( + "Absolute URLs required for location header.", + HTTPWarning, + stacklevel=4, + ) + + def check_iterator(self, app_iter: t.Iterable[bytes]) -> None: + if isinstance(app_iter, bytes): + warn( + "The application returned a bytestring. The response will send one" + " character at a time to the client, which will kill performance." + " Return a list or iterable instead.", + WSGIWarning, + stacklevel=3, + ) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Iterable[bytes]: + if len(args) != 2: + warn("A WSGI app takes two arguments.", WSGIWarning, stacklevel=2) + + if kwargs: + warn( + "A WSGI app does not take keyword arguments.", WSGIWarning, stacklevel=2 + ) + + environ: "WSGIEnvironment" = args[0] + start_response: "StartResponse" = args[1] + + self.check_environ(environ) + environ["wsgi.input"] = InputStream(environ["wsgi.input"]) + environ["wsgi.errors"] = ErrorStream(environ["wsgi.errors"]) + + # Hook our own file wrapper in so that applications will always + # iterate to the end and we can check the content length. + environ["wsgi.file_wrapper"] = FileWrapper + + headers_set: t.List[t.Any] = [] + chunks: t.List[int] = [] + + def checking_start_response( + *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[bytes], None]: + if len(args) not in {2, 3}: + warn( + f"Invalid number of arguments: {len(args)}, expected 2 or 3.", + WSGIWarning, + stacklevel=2, + ) + + if kwargs: + warn("'start_response' does not take keyword arguments.", WSGIWarning) + + status: str = args[0] + headers: t.List[t.Tuple[str, str]] = args[1] + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ] = (args[2] if len(args) == 3 else None) + + headers_set[:] = self.check_start_response(status, headers, exc_info) + return GuardedWrite(start_response(status, headers, exc_info), chunks) + + app_iter = self.app(environ, t.cast("StartResponse", checking_start_response)) + self.check_iterator(app_iter) + return GuardedIterator( + app_iter, t.cast(t.Tuple[int, Headers], headers_set), chunks + ) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/profiler.py b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/profiler.py new file mode 100644 index 0000000..200dae0 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/profiler.py @@ -0,0 +1,139 @@ +""" +Application Profiler +==================== + +This module provides a middleware that profiles each request with the +:mod:`cProfile` module. This can help identify bottlenecks in your code +that may be slowing down your application. + +.. autoclass:: ProfilerMiddleware + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import os.path +import sys +import time +import typing as t +from pstats import Stats + +try: + from cProfile import Profile +except ImportError: + from profile import Profile # type: ignore + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class ProfilerMiddleware: + """Wrap a WSGI application and profile the execution of each + request. Responses are buffered so that timings are more exact. + + If ``stream`` is given, :class:`pstats.Stats` are written to it + after each request. If ``profile_dir`` is given, :mod:`cProfile` + data files are saved to that directory, one file per request. + + The filename can be customized by passing ``filename_format``. If + it is a string, it will be formatted using :meth:`str.format` with + the following fields available: + + - ``{method}`` - The request method; GET, POST, etc. + - ``{path}`` - The request path or 'root' should one not exist. + - ``{elapsed}`` - The elapsed time of the request. + - ``{time}`` - The time of the request. + + If it is a callable, it will be called with the WSGI ``environ`` + dict and should return a filename. + + :param app: The WSGI application to wrap. + :param stream: Write stats to this stream. Disable with ``None``. + :param sort_by: A tuple of columns to sort stats by. See + :meth:`pstats.Stats.sort_stats`. + :param restrictions: A tuple of restrictions to filter stats by. See + :meth:`pstats.Stats.print_stats`. + :param profile_dir: Save profile data files to this directory. + :param filename_format: Format string for profile data file names, + or a callable returning a name. See explanation above. + + .. code-block:: python + + from werkzeug.middleware.profiler import ProfilerMiddleware + app = ProfilerMiddleware(app) + + .. versionchanged:: 0.15 + Stats are written even if ``profile_dir`` is given, and can be + disable by passing ``stream=None``. + + .. versionadded:: 0.15 + Added ``filename_format``. + + .. versionadded:: 0.9 + Added ``restrictions`` and ``profile_dir``. + """ + + def __init__( + self, + app: "WSGIApplication", + stream: t.IO[str] = sys.stdout, + sort_by: t.Iterable[str] = ("time", "calls"), + restrictions: t.Iterable[t.Union[str, int, float]] = (), + profile_dir: t.Optional[str] = None, + filename_format: str = "{method}.{path}.{elapsed:.0f}ms.{time:.0f}.prof", + ) -> None: + self._app = app + self._stream = stream + self._sort_by = sort_by + self._restrictions = restrictions + self._profile_dir = profile_dir + self._filename_format = filename_format + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + response_body: t.List[bytes] = [] + + def catching_start_response(status, headers, exc_info=None): # type: ignore + start_response(status, headers, exc_info) + return response_body.append + + def runapp() -> None: + app_iter = self._app( + environ, t.cast("StartResponse", catching_start_response) + ) + response_body.extend(app_iter) + + if hasattr(app_iter, "close"): + app_iter.close() # type: ignore + + profile = Profile() + start = time.time() + profile.runcall(runapp) + body = b"".join(response_body) + elapsed = time.time() - start + + if self._profile_dir is not None: + if callable(self._filename_format): + filename = self._filename_format(environ) + else: + filename = self._filename_format.format( + method=environ["REQUEST_METHOD"], + path=environ["PATH_INFO"].strip("/").replace("/", ".") or "root", + elapsed=elapsed * 1000.0, + time=time.time(), + ) + filename = os.path.join(self._profile_dir, filename) + profile.dump_stats(filename) + + if self._stream is not None: + stats = Stats(profile, stream=self._stream) + stats.sort_stats(*self._sort_by) + print("-" * 80, file=self._stream) + path_info = environ.get("PATH_INFO", "") + print(f"PATH: {path_info!r}", file=self._stream) + stats.print_stats(*self._restrictions) + print(f"{'-' * 80}\n", file=self._stream) + + return [body] diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/proxy_fix.py b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/proxy_fix.py new file mode 100644 index 0000000..4cef7cc --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/proxy_fix.py @@ -0,0 +1,187 @@ +""" +X-Forwarded-For Proxy Fix +========================= + +This module provides a middleware that adjusts the WSGI environ based on +``X-Forwarded-`` headers that proxies in front of an application may +set. + +When an application is running behind a proxy server, WSGI may see the +request as coming from that server rather than the real client. Proxies +set various headers to track where the request actually came from. + +This middleware should only be used if the application is actually +behind such a proxy, and should be configured with the number of proxies +that are chained in front of it. Not all proxies set all the headers. +Since incoming headers can be faked, you must set how many proxies are +setting each header so the middleware knows what to trust. + +.. autoclass:: ProxyFix + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import typing as t + +from ..http import parse_list_header + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class ProxyFix: + """Adjust the WSGI environ based on ``X-Forwarded-`` that proxies in + front of the application may set. + + - ``X-Forwarded-For`` sets ``REMOTE_ADDR``. + - ``X-Forwarded-Proto`` sets ``wsgi.url_scheme``. + - ``X-Forwarded-Host`` sets ``HTTP_HOST``, ``SERVER_NAME``, and + ``SERVER_PORT``. + - ``X-Forwarded-Port`` sets ``HTTP_HOST`` and ``SERVER_PORT``. + - ``X-Forwarded-Prefix`` sets ``SCRIPT_NAME``. + + You must tell the middleware how many proxies set each header so it + knows what values to trust. It is a security issue to trust values + that came from the client rather than a proxy. + + The original values of the headers are stored in the WSGI + environ as ``werkzeug.proxy_fix.orig``, a dict. + + :param app: The WSGI application to wrap. + :param x_for: Number of values to trust for ``X-Forwarded-For``. + :param x_proto: Number of values to trust for ``X-Forwarded-Proto``. + :param x_host: Number of values to trust for ``X-Forwarded-Host``. + :param x_port: Number of values to trust for ``X-Forwarded-Port``. + :param x_prefix: Number of values to trust for + ``X-Forwarded-Prefix``. + + .. code-block:: python + + from werkzeug.middleware.proxy_fix import ProxyFix + # App is behind one proxy that sets the -For and -Host headers. + app = ProxyFix(app, x_for=1, x_host=1) + + .. versionchanged:: 1.0 + Deprecated code has been removed: + + * The ``num_proxies`` argument and attribute. + * The ``get_remote_addr`` method. + * The environ keys ``orig_remote_addr``, + ``orig_wsgi_url_scheme``, and ``orig_http_host``. + + .. versionchanged:: 0.15 + All headers support multiple values. The ``num_proxies`` + argument is deprecated. Each header is configured with a + separate number of trusted proxies. + + .. versionchanged:: 0.15 + Original WSGI environ values are stored in the + ``werkzeug.proxy_fix.orig`` dict. ``orig_remote_addr``, + ``orig_wsgi_url_scheme``, and ``orig_http_host`` are deprecated + and will be removed in 1.0. + + .. versionchanged:: 0.15 + Support ``X-Forwarded-Port`` and ``X-Forwarded-Prefix``. + + .. versionchanged:: 0.15 + ``X-Forwarded-Host`` and ``X-Forwarded-Port`` modify + ``SERVER_NAME`` and ``SERVER_PORT``. + """ + + def __init__( + self, + app: "WSGIApplication", + x_for: int = 1, + x_proto: int = 1, + x_host: int = 0, + x_port: int = 0, + x_prefix: int = 0, + ) -> None: + self.app = app + self.x_for = x_for + self.x_proto = x_proto + self.x_host = x_host + self.x_port = x_port + self.x_prefix = x_prefix + + def _get_real_value(self, trusted: int, value: t.Optional[str]) -> t.Optional[str]: + """Get the real value from a list header based on the configured + number of trusted proxies. + + :param trusted: Number of values to trust in the header. + :param value: Comma separated list header value to parse. + :return: The real value, or ``None`` if there are fewer values + than the number of trusted proxies. + + .. versionchanged:: 1.0 + Renamed from ``_get_trusted_comma``. + + .. versionadded:: 0.15 + """ + if not (trusted and value): + return None + values = parse_list_header(value) + if len(values) >= trusted: + return values[-trusted] + return None + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + """Modify the WSGI environ based on the various ``Forwarded`` + headers before calling the wrapped application. Store the + original environ values in ``werkzeug.proxy_fix.orig_{key}``. + """ + environ_get = environ.get + orig_remote_addr = environ_get("REMOTE_ADDR") + orig_wsgi_url_scheme = environ_get("wsgi.url_scheme") + orig_http_host = environ_get("HTTP_HOST") + environ.update( + { + "werkzeug.proxy_fix.orig": { + "REMOTE_ADDR": orig_remote_addr, + "wsgi.url_scheme": orig_wsgi_url_scheme, + "HTTP_HOST": orig_http_host, + "SERVER_NAME": environ_get("SERVER_NAME"), + "SERVER_PORT": environ_get("SERVER_PORT"), + "SCRIPT_NAME": environ_get("SCRIPT_NAME"), + } + } + ) + + x_for = self._get_real_value(self.x_for, environ_get("HTTP_X_FORWARDED_FOR")) + if x_for: + environ["REMOTE_ADDR"] = x_for + + x_proto = self._get_real_value( + self.x_proto, environ_get("HTTP_X_FORWARDED_PROTO") + ) + if x_proto: + environ["wsgi.url_scheme"] = x_proto + + x_host = self._get_real_value(self.x_host, environ_get("HTTP_X_FORWARDED_HOST")) + if x_host: + environ["HTTP_HOST"] = environ["SERVER_NAME"] = x_host + # "]" to check for IPv6 address without port + if ":" in x_host and not x_host.endswith("]"): + environ["SERVER_NAME"], environ["SERVER_PORT"] = x_host.rsplit(":", 1) + + x_port = self._get_real_value(self.x_port, environ_get("HTTP_X_FORWARDED_PORT")) + if x_port: + host = environ.get("HTTP_HOST") + if host: + # "]" to check for IPv6 address without port + if ":" in host and not host.endswith("]"): + host = host.rsplit(":", 1)[0] + environ["HTTP_HOST"] = f"{host}:{x_port}" + environ["SERVER_PORT"] = x_port + + x_prefix = self._get_real_value( + self.x_prefix, environ_get("HTTP_X_FORWARDED_PREFIX") + ) + if x_prefix: + environ["SCRIPT_NAME"] = x_prefix + + return self.app(environ, start_response) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/middleware/shared_data.py b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/shared_data.py new file mode 100644 index 0000000..62da672 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/middleware/shared_data.py @@ -0,0 +1,320 @@ +""" +Serve Shared Static Files +========================= + +.. autoclass:: SharedDataMiddleware + :members: is_allowed + +:copyright: 2007 Pallets +:license: BSD-3-Clause +""" +import mimetypes +import os +import pkgutil +import posixpath +import typing as t +from datetime import datetime +from datetime import timezone +from io import BytesIO +from time import time +from zlib import adler32 + +from ..filesystem import get_filesystem_encoding +from ..http import http_date +from ..http import is_resource_modified +from ..security import safe_join +from ..utils import get_content_type +from ..wsgi import get_path_info +from ..wsgi import wrap_file + +_TOpener = t.Callable[[], t.Tuple[t.IO[bytes], datetime, int]] +_TLoader = t.Callable[[t.Optional[str]], t.Tuple[t.Optional[str], t.Optional[_TOpener]]] + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class SharedDataMiddleware: + + """A WSGI middleware which provides static content for development + environments or simple server setups. Its usage is quite simple:: + + import os + from werkzeug.middleware.shared_data import SharedDataMiddleware + + app = SharedDataMiddleware(app, { + '/shared': os.path.join(os.path.dirname(__file__), 'shared') + }) + + The contents of the folder ``./shared`` will now be available on + ``http://example.com/shared/``. This is pretty useful during development + because a standalone media server is not required. Files can also be + mounted on the root folder and still continue to use the application because + the shared data middleware forwards all unhandled requests to the + application, even if the requests are below one of the shared folders. + + If `pkg_resources` is available you can also tell the middleware to serve + files from package data:: + + app = SharedDataMiddleware(app, { + '/static': ('myapplication', 'static') + }) + + This will then serve the ``static`` folder in the `myapplication` + Python package. + + The optional `disallow` parameter can be a list of :func:`~fnmatch.fnmatch` + rules for files that are not accessible from the web. If `cache` is set to + `False` no caching headers are sent. + + Currently the middleware does not support non-ASCII filenames. If the + encoding on the file system happens to match the encoding of the URI it may + work but this could also be by accident. We strongly suggest using ASCII + only file names for static files. + + The middleware will guess the mimetype using the Python `mimetype` + module. If it's unable to figure out the charset it will fall back + to `fallback_mimetype`. + + :param app: the application to wrap. If you don't want to wrap an + application you can pass it :exc:`NotFound`. + :param exports: a list or dict of exported files and folders. + :param disallow: a list of :func:`~fnmatch.fnmatch` rules. + :param cache: enable or disable caching headers. + :param cache_timeout: the cache timeout in seconds for the headers. + :param fallback_mimetype: The fallback mimetype for unknown files. + + .. versionchanged:: 1.0 + The default ``fallback_mimetype`` is + ``application/octet-stream``. If a filename looks like a text + mimetype, the ``utf-8`` charset is added to it. + + .. versionadded:: 0.6 + Added ``fallback_mimetype``. + + .. versionchanged:: 0.5 + Added ``cache_timeout``. + """ + + def __init__( + self, + app: "WSGIApplication", + exports: t.Union[ + t.Dict[str, t.Union[str, t.Tuple[str, str]]], + t.Iterable[t.Tuple[str, t.Union[str, t.Tuple[str, str]]]], + ], + disallow: None = None, + cache: bool = True, + cache_timeout: int = 60 * 60 * 12, + fallback_mimetype: str = "application/octet-stream", + ) -> None: + self.app = app + self.exports: t.List[t.Tuple[str, _TLoader]] = [] + self.cache = cache + self.cache_timeout = cache_timeout + + if isinstance(exports, dict): + exports = exports.items() + + for key, value in exports: + if isinstance(value, tuple): + loader = self.get_package_loader(*value) + elif isinstance(value, str): + if os.path.isfile(value): + loader = self.get_file_loader(value) + else: + loader = self.get_directory_loader(value) + else: + raise TypeError(f"unknown def {value!r}") + + self.exports.append((key, loader)) + + if disallow is not None: + from fnmatch import fnmatch + + self.is_allowed = lambda x: not fnmatch(x, disallow) + + self.fallback_mimetype = fallback_mimetype + + def is_allowed(self, filename: str) -> bool: + """Subclasses can override this method to disallow the access to + certain files. However by providing `disallow` in the constructor + this method is overwritten. + """ + return True + + def _opener(self, filename: str) -> _TOpener: + return lambda: ( + open(filename, "rb"), + datetime.fromtimestamp(os.path.getmtime(filename), tz=timezone.utc), + int(os.path.getsize(filename)), + ) + + def get_file_loader(self, filename: str) -> _TLoader: + return lambda x: (os.path.basename(filename), self._opener(filename)) + + def get_package_loader(self, package: str, package_path: str) -> _TLoader: + load_time = datetime.now(timezone.utc) + provider = pkgutil.get_loader(package) + + if hasattr(provider, "get_resource_reader"): + # Python 3 + reader = provider.get_resource_reader(package) # type: ignore + + def loader( + path: t.Optional[str], + ) -> t.Tuple[t.Optional[str], t.Optional[_TOpener]]: + if path is None: + return None, None + + path = safe_join(package_path, path) + + if path is None: + return None, None + + basename = posixpath.basename(path) + + try: + resource = reader.open_resource(path) + except OSError: + return None, None + + if isinstance(resource, BytesIO): + return ( + basename, + lambda: (resource, load_time, len(resource.getvalue())), + ) + + return ( + basename, + lambda: ( + resource, + datetime.fromtimestamp( + os.path.getmtime(resource.name), tz=timezone.utc + ), + os.path.getsize(resource.name), + ), + ) + + else: + # Python 3.6 + package_filename = provider.get_filename(package) # type: ignore + is_filesystem = os.path.exists(package_filename) + root = os.path.join(os.path.dirname(package_filename), package_path) + + def loader( + path: t.Optional[str], + ) -> t.Tuple[t.Optional[str], t.Optional[_TOpener]]: + if path is None: + return None, None + + path = safe_join(root, path) + + if path is None: + return None, None + + basename = posixpath.basename(path) + + if is_filesystem: + if not os.path.isfile(path): + return None, None + + return basename, self._opener(path) + + try: + data = provider.get_data(path) # type: ignore + except OSError: + return None, None + + return basename, lambda: (BytesIO(data), load_time, len(data)) + + return loader + + def get_directory_loader(self, directory: str) -> _TLoader: + def loader( + path: t.Optional[str], + ) -> t.Tuple[t.Optional[str], t.Optional[_TOpener]]: + if path is not None: + path = safe_join(directory, path) + + if path is None: + return None, None + else: + path = directory + + if os.path.isfile(path): + return os.path.basename(path), self._opener(path) + + return None, None + + return loader + + def generate_etag(self, mtime: datetime, file_size: int, real_filename: str) -> str: + if not isinstance(real_filename, bytes): + real_filename = real_filename.encode( # type: ignore + get_filesystem_encoding() + ) + + timestamp = mtime.timestamp() + checksum = adler32(real_filename) & 0xFFFFFFFF # type: ignore + return f"wzsdm-{timestamp}-{file_size}-{checksum}" + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + path = get_path_info(environ) + file_loader = None + + for search_path, loader in self.exports: + if search_path == path: + real_filename, file_loader = loader(None) + + if file_loader is not None: + break + + if not search_path.endswith("/"): + search_path += "/" + + if path.startswith(search_path): + real_filename, file_loader = loader(path[len(search_path) :]) + + if file_loader is not None: + break + + if file_loader is None or not self.is_allowed(real_filename): # type: ignore + return self.app(environ, start_response) + + guessed_type = mimetypes.guess_type(real_filename) # type: ignore + mime_type = get_content_type(guessed_type[0] or self.fallback_mimetype, "utf-8") + f, mtime, file_size = file_loader() + + headers = [("Date", http_date())] + + if self.cache: + timeout = self.cache_timeout + etag = self.generate_etag(mtime, file_size, real_filename) # type: ignore + headers += [ + ("Etag", f'"{etag}"'), + ("Cache-Control", f"max-age={timeout}, public"), + ] + + if not is_resource_modified(environ, etag, last_modified=mtime): + f.close() + start_response("304 Not Modified", headers) + return [] + + headers.append(("Expires", http_date(time() + timeout))) + else: + headers.append(("Cache-Control", "public")) + + headers.extend( + ( + ("Content-Type", mime_type), + ("Content-Length", str(file_size)), + ("Last-Modified", http_date(mtime)), + ) + ) + start_response("200 OK", headers) + return wrap_file(environ, f) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/py.typed b/myvenv/lib/python3.10/site-packages/werkzeug/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/routing.py b/myvenv/lib/python3.10/site-packages/werkzeug/routing.py new file mode 100644 index 0000000..937b42b --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/routing.py @@ -0,0 +1,2342 @@ +"""When it comes to combining multiple controller or view functions +(however you want to call them) you need a dispatcher. A simple way +would be applying regular expression tests on the ``PATH_INFO`` and +calling registered callback functions that return the value then. + +This module implements a much more powerful system than simple regular +expression matching because it can also convert values in the URLs and +build URLs. + +Here a simple example that creates a URL map for an application with +two subdomains (www and kb) and some URL rules: + +.. code-block:: python + + m = Map([ + # Static URLs + Rule('/', endpoint='static/index'), + Rule('/about', endpoint='static/about'), + Rule('/help', endpoint='static/help'), + # Knowledge Base + Subdomain('kb', [ + Rule('/', endpoint='kb/index'), + Rule('/browse/', endpoint='kb/browse'), + Rule('/browse//', endpoint='kb/browse'), + Rule('/browse//', endpoint='kb/browse') + ]) + ], default_subdomain='www') + +If the application doesn't use subdomains it's perfectly fine to not set +the default subdomain and not use the `Subdomain` rule factory. The +endpoint in the rules can be anything, for example import paths or +unique identifiers. The WSGI application can use those endpoints to get the +handler for that URL. It doesn't have to be a string at all but it's +recommended. + +Now it's possible to create a URL adapter for one of the subdomains and +build URLs: + +.. code-block:: python + + c = m.bind('example.com') + + c.build("kb/browse", dict(id=42)) + 'http://kb.example.com/browse/42/' + + c.build("kb/browse", dict()) + 'http://kb.example.com/browse/' + + c.build("kb/browse", dict(id=42, page=3)) + 'http://kb.example.com/browse/42/3' + + c.build("static/about") + '/about' + + c.build("static/index", force_external=True) + 'http://www.example.com/' + + c = m.bind('example.com', subdomain='kb') + + c.build("static/about") + 'http://www.example.com/about' + +The first argument to bind is the server name *without* the subdomain. +Per default it will assume that the script is mounted on the root, but +often that's not the case so you can provide the real mount point as +second argument: + +.. code-block:: python + + c = m.bind('example.com', '/applications/example') + +The third argument can be the subdomain, if not given the default +subdomain is used. For more details about binding have a look at the +documentation of the `MapAdapter`. + +And here is how you can match URLs: + +.. code-block:: python + + c = m.bind('example.com') + + c.match("/") + ('static/index', {}) + + c.match("/about") + ('static/about', {}) + + c = m.bind('example.com', '/', 'kb') + + c.match("/") + ('kb/index', {}) + + c.match("/browse/42/23") + ('kb/browse', {'id': 42, 'page': 23}) + +If matching fails you get a ``NotFound`` exception, if the rule thinks +it's a good idea to redirect (for example because the URL was defined +to have a slash at the end but the request was missing that slash) it +will raise a ``RequestRedirect`` exception. Both are subclasses of +``HTTPException`` so you can use those errors as responses in the +application. + +If matching succeeded but the URL rule was incompatible to the given +method (for example there were only rules for ``GET`` and ``HEAD`` but +routing tried to match a ``POST`` request) a ``MethodNotAllowed`` +exception is raised. +""" +import ast +import difflib +import posixpath +import re +import typing +import typing as t +import uuid +import warnings +from pprint import pformat +from string import Template +from threading import Lock +from types import CodeType + +from ._internal import _encode_idna +from ._internal import _get_environ +from ._internal import _to_bytes +from ._internal import _to_str +from ._internal import _wsgi_decoding_dance +from .datastructures import ImmutableDict +from .datastructures import MultiDict +from .exceptions import BadHost +from .exceptions import BadRequest +from .exceptions import HTTPException +from .exceptions import MethodNotAllowed +from .exceptions import NotFound +from .urls import _fast_url_quote +from .urls import url_encode +from .urls import url_join +from .urls import url_quote +from .urls import url_unquote +from .utils import cached_property +from .utils import redirect +from .wsgi import get_host + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + from .wrappers.request import Request + from .wrappers.response import Response + +_rule_re = re.compile( + r""" + (?P[^<]*) # static rule data + < + (?: + (?P[a-zA-Z_][a-zA-Z0-9_]*) # converter name + (?:\((?P.*?)\))? # converter arguments + \: # variable delimiter + )? + (?P[a-zA-Z_][a-zA-Z0-9_]*) # variable name + > + """, + re.VERBOSE, +) +_simple_rule_re = re.compile(r"<([^>]+)>") +_converter_args_re = re.compile( + r""" + ((?P\w+)\s*=\s*)? + (?P + True|False| + \d+.\d+| + \d+.| + \d+| + [\w\d_.]+| + [urUR]?(?P"[^"]*?"|'[^']*') + )\s*, + """, + re.VERBOSE, +) + + +_PYTHON_CONSTANTS = {"None": None, "True": True, "False": False} + + +def _pythonize(value: str) -> t.Union[None, bool, int, float, str]: + if value in _PYTHON_CONSTANTS: + return _PYTHON_CONSTANTS[value] + for convert in int, float: + try: + return convert(value) # type: ignore + except ValueError: + pass + if value[:1] == value[-1:] and value[0] in "\"'": + value = value[1:-1] + return str(value) + + +def parse_converter_args(argstr: str) -> t.Tuple[t.Tuple, t.Dict[str, t.Any]]: + argstr += "," + args = [] + kwargs = {} + + for item in _converter_args_re.finditer(argstr): + value = item.group("stringval") + if value is None: + value = item.group("value") + value = _pythonize(value) + if not item.group("name"): + args.append(value) + else: + name = item.group("name") + kwargs[name] = value + + return tuple(args), kwargs + + +def parse_rule(rule: str) -> t.Iterator[t.Tuple[t.Optional[str], t.Optional[str], str]]: + """Parse a rule and return it as generator. Each iteration yields tuples + in the form ``(converter, arguments, variable)``. If the converter is + `None` it's a static url part, otherwise it's a dynamic one. + + :internal: + """ + pos = 0 + end = len(rule) + do_match = _rule_re.match + used_names = set() + while pos < end: + m = do_match(rule, pos) + if m is None: + break + data = m.groupdict() + if data["static"]: + yield None, None, data["static"] + variable = data["variable"] + converter = data["converter"] or "default" + if variable in used_names: + raise ValueError(f"variable name {variable!r} used twice.") + used_names.add(variable) + yield converter, data["args"] or None, variable + pos = m.end() + if pos < end: + remaining = rule[pos:] + if ">" in remaining or "<" in remaining: + raise ValueError(f"malformed url rule: {rule!r}") + yield None, None, remaining + + +class RoutingException(Exception): + """Special exceptions that require the application to redirect, notifying + about missing urls, etc. + + :internal: + """ + + +class RequestRedirect(HTTPException, RoutingException): + """Raise if the map requests a redirect. This is for example the case if + `strict_slashes` are activated and an url that requires a trailing slash. + + The attribute `new_url` contains the absolute destination url. + """ + + code = 308 + + def __init__(self, new_url: str) -> None: + super().__init__(new_url) + self.new_url = new_url + + def get_response( + self, + environ: t.Optional[t.Union["WSGIEnvironment", "Request"]] = None, + scope: t.Optional[dict] = None, + ) -> "Response": + return redirect(self.new_url, self.code) + + +class RequestPath(RoutingException): + """Internal exception.""" + + __slots__ = ("path_info",) + + def __init__(self, path_info: str) -> None: + super().__init__() + self.path_info = path_info + + +class RequestAliasRedirect(RoutingException): # noqa: B903 + """This rule is an alias and wants to redirect to the canonical URL.""" + + def __init__(self, matched_values: t.Mapping[str, t.Any]) -> None: + super().__init__() + self.matched_values = matched_values + + +class BuildError(RoutingException, LookupError): + """Raised if the build system cannot find a URL for an endpoint with the + values provided. + """ + + def __init__( + self, + endpoint: str, + values: t.Mapping[str, t.Any], + method: t.Optional[str], + adapter: t.Optional["MapAdapter"] = None, + ) -> None: + super().__init__(endpoint, values, method) + self.endpoint = endpoint + self.values = values + self.method = method + self.adapter = adapter + + @cached_property + def suggested(self) -> t.Optional["Rule"]: + return self.closest_rule(self.adapter) + + def closest_rule(self, adapter: t.Optional["MapAdapter"]) -> t.Optional["Rule"]: + def _score_rule(rule: "Rule") -> float: + return sum( + [ + 0.98 + * difflib.SequenceMatcher( + None, rule.endpoint, self.endpoint + ).ratio(), + 0.01 * bool(set(self.values or ()).issubset(rule.arguments)), + 0.01 * bool(rule.methods and self.method in rule.methods), + ] + ) + + if adapter and adapter.map._rules: + return max(adapter.map._rules, key=_score_rule) + + return None + + def __str__(self) -> str: + message = [f"Could not build url for endpoint {self.endpoint!r}"] + if self.method: + message.append(f" ({self.method!r})") + if self.values: + message.append(f" with values {sorted(self.values)!r}") + message.append(".") + if self.suggested: + if self.endpoint == self.suggested.endpoint: + if ( + self.method + and self.suggested.methods is not None + and self.method not in self.suggested.methods + ): + message.append( + " Did you mean to use methods" + f" {sorted(self.suggested.methods)!r}?" + ) + missing_values = self.suggested.arguments.union( + set(self.suggested.defaults or ()) + ) - set(self.values.keys()) + if missing_values: + message.append( + f" Did you forget to specify values {sorted(missing_values)!r}?" + ) + else: + message.append(f" Did you mean {self.suggested.endpoint!r} instead?") + return "".join(message) + + +class WebsocketMismatch(BadRequest): + """The only matched rule is either a WebSocket and the request is + HTTP, or the rule is HTTP and the request is a WebSocket. + """ + + +class ValidationError(ValueError): + """Validation error. If a rule converter raises this exception the rule + does not match the current URL and the next URL is tried. + """ + + +class RuleFactory: + """As soon as you have more complex URL setups it's a good idea to use rule + factories to avoid repetitive tasks. Some of them are builtin, others can + be added by subclassing `RuleFactory` and overriding `get_rules`. + """ + + def get_rules(self, map: "Map") -> t.Iterable["Rule"]: + """Subclasses of `RuleFactory` have to override this method and return + an iterable of rules.""" + raise NotImplementedError() + + +class Subdomain(RuleFactory): + """All URLs provided by this factory have the subdomain set to a + specific domain. For example if you want to use the subdomain for + the current language this can be a good setup:: + + url_map = Map([ + Rule('/', endpoint='#select_language'), + Subdomain('', [ + Rule('/', endpoint='index'), + Rule('/about', endpoint='about'), + Rule('/help', endpoint='help') + ]) + ]) + + All the rules except for the ``'#select_language'`` endpoint will now + listen on a two letter long subdomain that holds the language code + for the current request. + """ + + def __init__(self, subdomain: str, rules: t.Iterable[RuleFactory]) -> None: + self.subdomain = subdomain + self.rules = rules + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + for rulefactory in self.rules: + for rule in rulefactory.get_rules(map): + rule = rule.empty() + rule.subdomain = self.subdomain + yield rule + + +class Submount(RuleFactory): + """Like `Subdomain` but prefixes the URL rule with a given string:: + + url_map = Map([ + Rule('/', endpoint='index'), + Submount('/blog', [ + Rule('/', endpoint='blog/index'), + Rule('/entry/', endpoint='blog/show') + ]) + ]) + + Now the rule ``'blog/show'`` matches ``/blog/entry/``. + """ + + def __init__(self, path: str, rules: t.Iterable[RuleFactory]) -> None: + self.path = path.rstrip("/") + self.rules = rules + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + for rulefactory in self.rules: + for rule in rulefactory.get_rules(map): + rule = rule.empty() + rule.rule = self.path + rule.rule + yield rule + + +class EndpointPrefix(RuleFactory): + """Prefixes all endpoints (which must be strings for this factory) with + another string. This can be useful for sub applications:: + + url_map = Map([ + Rule('/', endpoint='index'), + EndpointPrefix('blog/', [Submount('/blog', [ + Rule('/', endpoint='index'), + Rule('/entry/', endpoint='show') + ])]) + ]) + """ + + def __init__(self, prefix: str, rules: t.Iterable[RuleFactory]) -> None: + self.prefix = prefix + self.rules = rules + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + for rulefactory in self.rules: + for rule in rulefactory.get_rules(map): + rule = rule.empty() + rule.endpoint = self.prefix + rule.endpoint + yield rule + + +class RuleTemplate: + """Returns copies of the rules wrapped and expands string templates in + the endpoint, rule, defaults or subdomain sections. + + Here a small example for such a rule template:: + + from werkzeug.routing import Map, Rule, RuleTemplate + + resource = RuleTemplate([ + Rule('/$name/', endpoint='$name.list'), + Rule('/$name/', endpoint='$name.show') + ]) + + url_map = Map([resource(name='user'), resource(name='page')]) + + When a rule template is called the keyword arguments are used to + replace the placeholders in all the string parameters. + """ + + def __init__(self, rules: t.Iterable["Rule"]) -> None: + self.rules = list(rules) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> "RuleTemplateFactory": + return RuleTemplateFactory(self.rules, dict(*args, **kwargs)) + + +class RuleTemplateFactory(RuleFactory): + """A factory that fills in template variables into rules. Used by + `RuleTemplate` internally. + + :internal: + """ + + def __init__( + self, rules: t.Iterable[RuleFactory], context: t.Dict[str, t.Any] + ) -> None: + self.rules = rules + self.context = context + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + for rulefactory in self.rules: + for rule in rulefactory.get_rules(map): + new_defaults = subdomain = None + if rule.defaults: + new_defaults = {} + for key, value in rule.defaults.items(): + if isinstance(value, str): + value = Template(value).substitute(self.context) + new_defaults[key] = value + if rule.subdomain is not None: + subdomain = Template(rule.subdomain).substitute(self.context) + new_endpoint = rule.endpoint + if isinstance(new_endpoint, str): + new_endpoint = Template(new_endpoint).substitute(self.context) + yield Rule( + Template(rule.rule).substitute(self.context), + new_defaults, + subdomain, + rule.methods, + rule.build_only, + new_endpoint, + rule.strict_slashes, + ) + + +def _prefix_names(src: str) -> ast.stmt: + """ast parse and prefix names with `.` to avoid collision with user vars""" + tree = ast.parse(src).body[0] + if isinstance(tree, ast.Expr): + tree = tree.value # type: ignore + for node in ast.walk(tree): + if isinstance(node, ast.Name): + node.id = f".{node.id}" + return tree + + +_CALL_CONVERTER_CODE_FMT = "self._converters[{elem!r}].to_url()" +_IF_KWARGS_URL_ENCODE_CODE = """\ +if kwargs: + q = '?' + params = self._encode_query_vars(kwargs) +else: + q = params = '' +""" +_IF_KWARGS_URL_ENCODE_AST = _prefix_names(_IF_KWARGS_URL_ENCODE_CODE) +_URL_ENCODE_AST_NAMES = (_prefix_names("q"), _prefix_names("params")) + + +class Rule(RuleFactory): + """A Rule represents one URL pattern. There are some options for `Rule` + that change the way it behaves and are passed to the `Rule` constructor. + Note that besides the rule-string all arguments *must* be keyword arguments + in order to not break the application on Werkzeug upgrades. + + `string` + Rule strings basically are just normal URL paths with placeholders in + the format ```` where the converter and the + arguments are optional. If no converter is defined the `default` + converter is used which means `string` in the normal configuration. + + URL rules that end with a slash are branch URLs, others are leaves. + If you have `strict_slashes` enabled (which is the default), all + branch URLs that are matched without a trailing slash will trigger a + redirect to the same URL with the missing slash appended. + + The converters are defined on the `Map`. + + `endpoint` + The endpoint for this rule. This can be anything. A reference to a + function, a string, a number etc. The preferred way is using a string + because the endpoint is used for URL generation. + + `defaults` + An optional dict with defaults for other rules with the same endpoint. + This is a bit tricky but useful if you want to have unique URLs:: + + url_map = Map([ + Rule('/all/', defaults={'page': 1}, endpoint='all_entries'), + Rule('/all/page/', endpoint='all_entries') + ]) + + If a user now visits ``http://example.com/all/page/1`` he will be + redirected to ``http://example.com/all/``. If `redirect_defaults` is + disabled on the `Map` instance this will only affect the URL + generation. + + `subdomain` + The subdomain rule string for this rule. If not specified the rule + only matches for the `default_subdomain` of the map. If the map is + not bound to a subdomain this feature is disabled. + + Can be useful if you want to have user profiles on different subdomains + and all subdomains are forwarded to your application:: + + url_map = Map([ + Rule('/', subdomain='', endpoint='user/homepage'), + Rule('/stats', subdomain='', endpoint='user/stats') + ]) + + `methods` + A sequence of http methods this rule applies to. If not specified, all + methods are allowed. For example this can be useful if you want different + endpoints for `POST` and `GET`. If methods are defined and the path + matches but the method matched against is not in this list or in the + list of another rule for that path the error raised is of the type + `MethodNotAllowed` rather than `NotFound`. If `GET` is present in the + list of methods and `HEAD` is not, `HEAD` is added automatically. + + `strict_slashes` + Override the `Map` setting for `strict_slashes` only for this rule. If + not specified the `Map` setting is used. + + `merge_slashes` + Override :attr:`Map.merge_slashes` for this rule. + + `build_only` + Set this to True and the rule will never match but will create a URL + that can be build. This is useful if you have resources on a subdomain + or folder that are not handled by the WSGI application (like static data) + + `redirect_to` + If given this must be either a string or callable. In case of a + callable it's called with the url adapter that triggered the match and + the values of the URL as keyword arguments and has to return the target + for the redirect, otherwise it has to be a string with placeholders in + rule syntax:: + + def foo_with_slug(adapter, id): + # ask the database for the slug for the old id. this of + # course has nothing to do with werkzeug. + return f'foo/{Foo.get_slug_for_id(id)}' + + url_map = Map([ + Rule('/foo/', endpoint='foo'), + Rule('/some/old/url/', redirect_to='foo/'), + Rule('/other/old/url/', redirect_to=foo_with_slug) + ]) + + When the rule is matched the routing system will raise a + `RequestRedirect` exception with the target for the redirect. + + Keep in mind that the URL will be joined against the URL root of the + script so don't use a leading slash on the target URL unless you + really mean root of that domain. + + `alias` + If enabled this rule serves as an alias for another rule with the same + endpoint and arguments. + + `host` + If provided and the URL map has host matching enabled this can be + used to provide a match rule for the whole host. This also means + that the subdomain feature is disabled. + + `websocket` + If ``True``, this rule is only matches for WebSocket (``ws://``, + ``wss://``) requests. By default, rules will only match for HTTP + requests. + + .. versionadded:: 1.0 + Added ``websocket``. + + .. versionadded:: 1.0 + Added ``merge_slashes``. + + .. versionadded:: 0.7 + Added ``alias`` and ``host``. + + .. versionchanged:: 0.6.1 + ``HEAD`` is added to ``methods`` if ``GET`` is present. + """ + + def __init__( + self, + string: str, + defaults: t.Optional[t.Mapping[str, t.Any]] = None, + subdomain: t.Optional[str] = None, + methods: t.Optional[t.Iterable[str]] = None, + build_only: bool = False, + endpoint: t.Optional[str] = None, + strict_slashes: t.Optional[bool] = None, + merge_slashes: t.Optional[bool] = None, + redirect_to: t.Optional[t.Union[str, t.Callable[..., str]]] = None, + alias: bool = False, + host: t.Optional[str] = None, + websocket: bool = False, + ) -> None: + if not string.startswith("/"): + raise ValueError("urls must start with a leading slash") + self.rule = string + self.is_leaf = not string.endswith("/") + + self.map: "Map" = None # type: ignore + self.strict_slashes = strict_slashes + self.merge_slashes = merge_slashes + self.subdomain = subdomain + self.host = host + self.defaults = defaults + self.build_only = build_only + self.alias = alias + self.websocket = websocket + + if methods is not None: + if isinstance(methods, str): + raise TypeError("'methods' should be a list of strings.") + + methods = {x.upper() for x in methods} + + if "HEAD" not in methods and "GET" in methods: + methods.add("HEAD") + + if websocket and methods - {"GET", "HEAD", "OPTIONS"}: + raise ValueError( + "WebSocket rules can only use 'GET', 'HEAD', and 'OPTIONS' methods." + ) + + self.methods = methods + self.endpoint: str = endpoint # type: ignore + self.redirect_to = redirect_to + + if defaults: + self.arguments = set(map(str, defaults)) + else: + self.arguments = set() + + self._trace: t.List[t.Tuple[bool, str]] = [] + + def empty(self) -> "Rule": + """ + Return an unbound copy of this rule. + + This can be useful if want to reuse an already bound URL for another + map. See ``get_empty_kwargs`` to override what keyword arguments are + provided to the new copy. + """ + return type(self)(self.rule, **self.get_empty_kwargs()) + + def get_empty_kwargs(self) -> t.Mapping[str, t.Any]: + """ + Provides kwargs for instantiating empty copy with empty() + + Use this method to provide custom keyword arguments to the subclass of + ``Rule`` when calling ``some_rule.empty()``. Helpful when the subclass + has custom keyword arguments that are needed at instantiation. + + Must return a ``dict`` that will be provided as kwargs to the new + instance of ``Rule``, following the initial ``self.rule`` value which + is always provided as the first, required positional argument. + """ + defaults = None + if self.defaults: + defaults = dict(self.defaults) + return dict( + defaults=defaults, + subdomain=self.subdomain, + methods=self.methods, + build_only=self.build_only, + endpoint=self.endpoint, + strict_slashes=self.strict_slashes, + redirect_to=self.redirect_to, + alias=self.alias, + host=self.host, + ) + + def get_rules(self, map: "Map") -> t.Iterator["Rule"]: + yield self + + def refresh(self) -> None: + """Rebinds and refreshes the URL. Call this if you modified the + rule in place. + + :internal: + """ + self.bind(self.map, rebind=True) + + def bind(self, map: "Map", rebind: bool = False) -> None: + """Bind the url to a map and create a regular expression based on + the information from the rule itself and the defaults from the map. + + :internal: + """ + if self.map is not None and not rebind: + raise RuntimeError(f"url rule {self!r} already bound to map {self.map!r}") + self.map = map + if self.strict_slashes is None: + self.strict_slashes = map.strict_slashes + if self.merge_slashes is None: + self.merge_slashes = map.merge_slashes + if self.subdomain is None: + self.subdomain = map.default_subdomain + self.compile() + + def get_converter( + self, + variable_name: str, + converter_name: str, + args: t.Tuple, + kwargs: t.Mapping[str, t.Any], + ) -> "BaseConverter": + """Looks up the converter for the given parameter. + + .. versionadded:: 0.9 + """ + if converter_name not in self.map.converters: + raise LookupError(f"the converter {converter_name!r} does not exist") + return self.map.converters[converter_name](self.map, *args, **kwargs) + + def _encode_query_vars(self, query_vars: t.Mapping[str, t.Any]) -> str: + return url_encode( + query_vars, + charset=self.map.charset, + sort=self.map.sort_parameters, + key=self.map.sort_key, + ) + + def compile(self) -> None: + """Compiles the regular expression and stores it.""" + assert self.map is not None, "rule not bound" + + if self.map.host_matching: + domain_rule = self.host or "" + else: + domain_rule = self.subdomain or "" + + self._trace = [] + self._converters: t.Dict[str, "BaseConverter"] = {} + self._static_weights: t.List[t.Tuple[int, int]] = [] + self._argument_weights: t.List[int] = [] + regex_parts = [] + + def _build_regex(rule: str) -> None: + index = 0 + for converter, arguments, variable in parse_rule(rule): + if converter is None: + for match in re.finditer(r"/+|[^/]+", variable): + part = match.group(0) + if part.startswith("/"): + if self.merge_slashes: + regex_parts.append(r"/+?") + self._trace.append((False, "/")) + else: + regex_parts.append(part) + self._trace.append((False, part)) + continue + self._trace.append((False, part)) + regex_parts.append(re.escape(part)) + if part: + self._static_weights.append((index, -len(part))) + else: + if arguments: + c_args, c_kwargs = parse_converter_args(arguments) + else: + c_args = () + c_kwargs = {} + convobj = self.get_converter(variable, converter, c_args, c_kwargs) + regex_parts.append(f"(?P<{variable}>{convobj.regex})") + self._converters[variable] = convobj + self._trace.append((True, variable)) + self._argument_weights.append(convobj.weight) + self.arguments.add(str(variable)) + index = index + 1 + + _build_regex(domain_rule) + regex_parts.append("\\|") + self._trace.append((False, "|")) + _build_regex(self.rule if self.is_leaf else self.rule.rstrip("/")) + if not self.is_leaf: + self._trace.append((False, "/")) + + self._build: t.Callable[..., t.Tuple[str, str]] + self._build = self._compile_builder(False).__get__(self, None) # type: ignore + self._build_unknown: t.Callable[..., t.Tuple[str, str]] + self._build_unknown = self._compile_builder(True).__get__( # type: ignore + self, None + ) + + if self.build_only: + return + + if not (self.is_leaf and self.strict_slashes): + reps = "*" if self.merge_slashes else "?" + tail = f"(?/{reps})" + else: + tail = "" + + regex = f"^{''.join(regex_parts)}{tail}$" + self._regex = re.compile(regex) + + def match( + self, path: str, method: t.Optional[str] = None + ) -> t.Optional[t.MutableMapping[str, t.Any]]: + """Check if the rule matches a given path. Path is a string in the + form ``"subdomain|/path"`` and is assembled by the map. If + the map is doing host matching the subdomain part will be the host + instead. + + If the rule matches a dict with the converted values is returned, + otherwise the return value is `None`. + + :internal: + """ + if not self.build_only: + require_redirect = False + + m = self._regex.search(path) + if m is not None: + groups = m.groupdict() + # we have a folder like part of the url without a trailing + # slash and strict slashes enabled. raise an exception that + # tells the map to redirect to the same url but with a + # trailing slash + if ( + self.strict_slashes + and not self.is_leaf + and not groups.pop("__suffix__") + and ( + method is None or self.methods is None or method in self.methods + ) + ): + path += "/" + require_redirect = True + # if we are not in strict slashes mode we have to remove + # a __suffix__ + elif not self.strict_slashes: + del groups["__suffix__"] + + result = {} + for name, value in groups.items(): + try: + value = self._converters[name].to_python(value) + except ValidationError: + return None + result[str(name)] = value + if self.defaults: + result.update(self.defaults) + + if self.merge_slashes: + new_path = "|".join(self.build(result, False)) # type: ignore + if path.endswith("/") and not new_path.endswith("/"): + new_path += "/" + if new_path.count("/") < path.count("/"): + # The URL will be encoded when MapAdapter.match + # handles the RequestPath raised below. Decode + # the URL here to avoid a double encoding. + path = url_unquote(new_path) + require_redirect = True + + if require_redirect: + path = path.split("|", 1)[1] + raise RequestPath(path) + + if self.alias and self.map.redirect_defaults: + raise RequestAliasRedirect(result) + + return result + + return None + + @staticmethod + def _get_func_code(code: CodeType, name: str) -> t.Callable[..., t.Tuple[str, str]]: + globs: t.Dict[str, t.Any] = {} + locs: t.Dict[str, t.Any] = {} + exec(code, globs, locs) + return locs[name] # type: ignore + + def _compile_builder( + self, append_unknown: bool = True + ) -> t.Callable[..., t.Tuple[str, str]]: + defaults = self.defaults or {} + dom_ops: t.List[t.Tuple[bool, str]] = [] + url_ops: t.List[t.Tuple[bool, str]] = [] + + opl = dom_ops + for is_dynamic, data in self._trace: + if data == "|" and opl is dom_ops: + opl = url_ops + continue + # this seems like a silly case to ever come up but: + # if a default is given for a value that appears in the rule, + # resolve it to a constant ahead of time + if is_dynamic and data in defaults: + data = self._converters[data].to_url(defaults[data]) + opl.append((False, data)) + elif not is_dynamic: + opl.append( + (False, url_quote(_to_bytes(data, self.map.charset), safe="/:|+")) + ) + else: + opl.append((True, data)) + + def _convert(elem: str) -> ast.stmt: + ret = _prefix_names(_CALL_CONVERTER_CODE_FMT.format(elem=elem)) + ret.args = [ast.Name(str(elem), ast.Load())] # type: ignore # str for py2 + return ret + + def _parts(ops: t.List[t.Tuple[bool, str]]) -> t.List[ast.AST]: + parts = [ + _convert(elem) if is_dynamic else ast.Str(s=elem) + for is_dynamic, elem in ops + ] + parts = parts or [ast.Str("")] + # constant fold + ret = [parts[0]] + for p in parts[1:]: + if isinstance(p, ast.Str) and isinstance(ret[-1], ast.Str): + ret[-1] = ast.Str(ret[-1].s + p.s) + else: + ret.append(p) + return ret + + dom_parts = _parts(dom_ops) + url_parts = _parts(url_ops) + if not append_unknown: + body = [] + else: + body = [_IF_KWARGS_URL_ENCODE_AST] + url_parts.extend(_URL_ENCODE_AST_NAMES) + + def _join(parts: t.List[ast.AST]) -> ast.AST: + if len(parts) == 1: # shortcut + return parts[0] + return ast.JoinedStr(parts) + + body.append( + ast.Return(ast.Tuple([_join(dom_parts), _join(url_parts)], ast.Load())) + ) + + pargs = [ + elem + for is_dynamic, elem in dom_ops + url_ops + if is_dynamic and elem not in defaults + ] + kargs = [str(k) for k in defaults] + + func_ast: ast.FunctionDef = _prefix_names("def _(): pass") # type: ignore + func_ast.name = f"" + func_ast.args.args.append(ast.arg(".self", None)) + for arg in pargs + kargs: + func_ast.args.args.append(ast.arg(arg, None)) + func_ast.args.kwarg = ast.arg(".kwargs", None) + for _ in kargs: + func_ast.args.defaults.append(ast.Str("")) + func_ast.body = body + + # use `ast.parse` instead of `ast.Module` for better portability + # Python 3.8 changes the signature of `ast.Module` + module = ast.parse("") + module.body = [func_ast] + + # mark everything as on line 1, offset 0 + # less error-prone than `ast.fix_missing_locations` + # bad line numbers cause an assert to fail in debug builds + for node in ast.walk(module): + if "lineno" in node._attributes: + node.lineno = 1 + if "col_offset" in node._attributes: + node.col_offset = 0 + + code = compile(module, "", "exec") + return self._get_func_code(code, func_ast.name) + + def build( + self, values: t.Mapping[str, t.Any], append_unknown: bool = True + ) -> t.Optional[t.Tuple[str, str]]: + """Assembles the relative url for that rule and the subdomain. + If building doesn't work for some reasons `None` is returned. + + :internal: + """ + try: + if append_unknown: + return self._build_unknown(**values) + else: + return self._build(**values) + except ValidationError: + return None + + def provides_defaults_for(self, rule: "Rule") -> bool: + """Check if this rule has defaults for a given rule. + + :internal: + """ + return bool( + not self.build_only + and self.defaults + and self.endpoint == rule.endpoint + and self != rule + and self.arguments == rule.arguments + ) + + def suitable_for( + self, values: t.Mapping[str, t.Any], method: t.Optional[str] = None + ) -> bool: + """Check if the dict of values has enough data for url generation. + + :internal: + """ + # if a method was given explicitly and that method is not supported + # by this rule, this rule is not suitable. + if ( + method is not None + and self.methods is not None + and method not in self.methods + ): + return False + + defaults = self.defaults or () + + # all arguments required must be either in the defaults dict or + # the value dictionary otherwise it's not suitable + for key in self.arguments: + if key not in defaults and key not in values: + return False + + # in case defaults are given we ensure that either the value was + # skipped or the value is the same as the default value. + if defaults: + for key, value in defaults.items(): + if key in values and value != values[key]: + return False + + return True + + def match_compare_key( + self, + ) -> t.Tuple[bool, int, t.Iterable[t.Tuple[int, int]], int, t.Iterable[int]]: + """The match compare key for sorting. + + Current implementation: + + 1. rules without any arguments come first for performance + reasons only as we expect them to match faster and some + common ones usually don't have any arguments (index pages etc.) + 2. rules with more static parts come first so the second argument + is the negative length of the number of the static weights. + 3. we order by static weights, which is a combination of index + and length + 4. The more complex rules come first so the next argument is the + negative length of the number of argument weights. + 5. lastly we order by the actual argument weights. + + :internal: + """ + return ( + bool(self.arguments), + -len(self._static_weights), + self._static_weights, + -len(self._argument_weights), + self._argument_weights, + ) + + def build_compare_key(self) -> t.Tuple[int, int, int]: + """The build compare key for sorting. + + :internal: + """ + return (1 if self.alias else 0, -len(self.arguments), -len(self.defaults or ())) + + def __eq__(self, other: object) -> bool: + return isinstance(other, type(self)) and self._trace == other._trace + + __hash__ = None # type: ignore + + def __str__(self) -> str: + return self.rule + + def __repr__(self) -> str: + if self.map is None: + return f"<{type(self).__name__} (unbound)>" + parts = [] + for is_dynamic, data in self._trace: + if is_dynamic: + parts.append(f"<{data}>") + else: + parts.append(data) + parts = "".join(parts).lstrip("|") + methods = f" ({', '.join(self.methods)})" if self.methods is not None else "" + return f"<{type(self).__name__} {parts!r}{methods} -> {self.endpoint}>" + + +class BaseConverter: + """Base class for all converters.""" + + regex = "[^/]+" + weight = 100 + + def __init__(self, map: "Map", *args: t.Any, **kwargs: t.Any) -> None: + self.map = map + + def to_python(self, value: str) -> t.Any: + return value + + def to_url(self, value: t.Any) -> str: + if isinstance(value, (bytes, bytearray)): + return _fast_url_quote(value) + return _fast_url_quote(str(value).encode(self.map.charset)) + + +class UnicodeConverter(BaseConverter): + """This converter is the default converter and accepts any string but + only one path segment. Thus the string can not include a slash. + + This is the default validator. + + Example:: + + Rule('/pages/'), + Rule('/') + + :param map: the :class:`Map`. + :param minlength: the minimum length of the string. Must be greater + or equal 1. + :param maxlength: the maximum length of the string. + :param length: the exact length of the string. + """ + + def __init__( + self, + map: "Map", + minlength: int = 1, + maxlength: t.Optional[int] = None, + length: t.Optional[int] = None, + ) -> None: + super().__init__(map) + if length is not None: + length_regex = f"{{{int(length)}}}" + else: + if maxlength is None: + maxlength_value = "" + else: + maxlength_value = str(int(maxlength)) + length_regex = f"{{{int(minlength)},{maxlength_value}}}" + self.regex = f"[^/]{length_regex}" + + +class AnyConverter(BaseConverter): + """Matches one of the items provided. Items can either be Python + identifiers or strings:: + + Rule('/') + + :param map: the :class:`Map`. + :param items: this function accepts the possible items as positional + arguments. + """ + + def __init__(self, map: "Map", *items: str) -> None: + super().__init__(map) + self.regex = f"(?:{'|'.join([re.escape(x) for x in items])})" + + +class PathConverter(BaseConverter): + """Like the default :class:`UnicodeConverter`, but it also matches + slashes. This is useful for wikis and similar applications:: + + Rule('/') + Rule('//edit') + + :param map: the :class:`Map`. + """ + + regex = "[^/].*?" + weight = 200 + + +class NumberConverter(BaseConverter): + """Baseclass for `IntegerConverter` and `FloatConverter`. + + :internal: + """ + + weight = 50 + num_convert: t.Callable = int + + def __init__( + self, + map: "Map", + fixed_digits: int = 0, + min: t.Optional[int] = None, + max: t.Optional[int] = None, + signed: bool = False, + ) -> None: + if signed: + self.regex = self.signed_regex + super().__init__(map) + self.fixed_digits = fixed_digits + self.min = min + self.max = max + self.signed = signed + + def to_python(self, value: str) -> t.Any: + if self.fixed_digits and len(value) != self.fixed_digits: + raise ValidationError() + value = self.num_convert(value) + if (self.min is not None and value < self.min) or ( + self.max is not None and value > self.max + ): + raise ValidationError() + return value + + def to_url(self, value: t.Any) -> str: + value = str(self.num_convert(value)) + if self.fixed_digits: + value = value.zfill(self.fixed_digits) + return value + + @property + def signed_regex(self) -> str: + return f"-?{self.regex}" + + +class IntegerConverter(NumberConverter): + """This converter only accepts integer values:: + + Rule("/page/") + + By default it only accepts unsigned, positive values. The ``signed`` + parameter will enable signed, negative values. :: + + Rule("/page/") + + :param map: The :class:`Map`. + :param fixed_digits: The number of fixed digits in the URL. If you + set this to ``4`` for example, the rule will only match if the + URL looks like ``/0001/``. The default is variable length. + :param min: The minimal value. + :param max: The maximal value. + :param signed: Allow signed (negative) values. + + .. versionadded:: 0.15 + The ``signed`` parameter. + """ + + regex = r"\d+" + + +class FloatConverter(NumberConverter): + """This converter only accepts floating point values:: + + Rule("/probability/") + + By default it only accepts unsigned, positive values. The ``signed`` + parameter will enable signed, negative values. :: + + Rule("/offset/") + + :param map: The :class:`Map`. + :param min: The minimal value. + :param max: The maximal value. + :param signed: Allow signed (negative) values. + + .. versionadded:: 0.15 + The ``signed`` parameter. + """ + + regex = r"\d+\.\d+" + num_convert = float + + def __init__( + self, + map: "Map", + min: t.Optional[float] = None, + max: t.Optional[float] = None, + signed: bool = False, + ) -> None: + super().__init__(map, min=min, max=max, signed=signed) # type: ignore + + +class UUIDConverter(BaseConverter): + """This converter only accepts UUID strings:: + + Rule('/object/') + + .. versionadded:: 0.10 + + :param map: the :class:`Map`. + """ + + regex = ( + r"[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-" + r"[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}" + ) + + def to_python(self, value: str) -> uuid.UUID: + return uuid.UUID(value) + + def to_url(self, value: uuid.UUID) -> str: + return str(value) + + +#: the default converter mapping for the map. +DEFAULT_CONVERTERS: t.Mapping[str, t.Type[BaseConverter]] = { + "default": UnicodeConverter, + "string": UnicodeConverter, + "any": AnyConverter, + "path": PathConverter, + "int": IntegerConverter, + "float": FloatConverter, + "uuid": UUIDConverter, +} + + +class Map: + """The map class stores all the URL rules and some configuration + parameters. Some of the configuration values are only stored on the + `Map` instance since those affect all rules, others are just defaults + and can be overridden for each rule. Note that you have to specify all + arguments besides the `rules` as keyword arguments! + + :param rules: sequence of url rules for this map. + :param default_subdomain: The default subdomain for rules without a + subdomain defined. + :param charset: charset of the url. defaults to ``"utf-8"`` + :param strict_slashes: If a rule ends with a slash but the matched + URL does not, redirect to the URL with a trailing slash. + :param merge_slashes: Merge consecutive slashes when matching or + building URLs. Matches will redirect to the normalized URL. + Slashes in variable parts are not merged. + :param redirect_defaults: This will redirect to the default rule if it + wasn't visited that way. This helps creating + unique URLs. + :param converters: A dict of converters that adds additional converters + to the list of converters. If you redefine one + converter this will override the original one. + :param sort_parameters: If set to `True` the url parameters are sorted. + See `url_encode` for more details. + :param sort_key: The sort key function for `url_encode`. + :param encoding_errors: the error method to use for decoding + :param host_matching: if set to `True` it enables the host matching + feature and disables the subdomain one. If + enabled the `host` parameter to rules is used + instead of the `subdomain` one. + + .. versionchanged:: 1.0 + If ``url_scheme`` is ``ws`` or ``wss``, only WebSocket rules + will match. + + .. versionchanged:: 1.0 + Added ``merge_slashes``. + + .. versionchanged:: 0.7 + Added ``encoding_errors`` and ``host_matching``. + + .. versionchanged:: 0.5 + Added ``sort_parameters`` and ``sort_key``. + """ + + #: A dict of default converters to be used. + default_converters = ImmutableDict(DEFAULT_CONVERTERS) + + #: The type of lock to use when updating. + #: + #: .. versionadded:: 1.0 + lock_class = Lock + + def __init__( + self, + rules: t.Optional[t.Iterable[RuleFactory]] = None, + default_subdomain: str = "", + charset: str = "utf-8", + strict_slashes: bool = True, + merge_slashes: bool = True, + redirect_defaults: bool = True, + converters: t.Optional[t.Mapping[str, t.Type[BaseConverter]]] = None, + sort_parameters: bool = False, + sort_key: t.Optional[t.Callable[[t.Any], t.Any]] = None, + encoding_errors: str = "replace", + host_matching: bool = False, + ) -> None: + self._rules: t.List[Rule] = [] + self._rules_by_endpoint: t.Dict[str, t.List[Rule]] = {} + self._remap = True + self._remap_lock = self.lock_class() + + self.default_subdomain = default_subdomain + self.charset = charset + self.encoding_errors = encoding_errors + self.strict_slashes = strict_slashes + self.merge_slashes = merge_slashes + self.redirect_defaults = redirect_defaults + self.host_matching = host_matching + + self.converters = self.default_converters.copy() + if converters: + self.converters.update(converters) + + self.sort_parameters = sort_parameters + self.sort_key = sort_key + + for rulefactory in rules or (): + self.add(rulefactory) + + def is_endpoint_expecting(self, endpoint: str, *arguments: str) -> bool: + """Iterate over all rules and check if the endpoint expects + the arguments provided. This is for example useful if you have + some URLs that expect a language code and others that do not and + you want to wrap the builder a bit so that the current language + code is automatically added if not provided but endpoints expect + it. + + :param endpoint: the endpoint to check. + :param arguments: this function accepts one or more arguments + as positional arguments. Each one of them is + checked. + """ + self.update() + arguments = set(arguments) + for rule in self._rules_by_endpoint[endpoint]: + if arguments.issubset(rule.arguments): + return True + return False + + def iter_rules(self, endpoint: t.Optional[str] = None) -> t.Iterator[Rule]: + """Iterate over all rules or the rules of an endpoint. + + :param endpoint: if provided only the rules for that endpoint + are returned. + :return: an iterator + """ + self.update() + if endpoint is not None: + return iter(self._rules_by_endpoint[endpoint]) + return iter(self._rules) + + def add(self, rulefactory: RuleFactory) -> None: + """Add a new rule or factory to the map and bind it. Requires that the + rule is not bound to another map. + + :param rulefactory: a :class:`Rule` or :class:`RuleFactory` + """ + for rule in rulefactory.get_rules(self): + rule.bind(self) + self._rules.append(rule) + self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule) + self._remap = True + + def bind( + self, + server_name: str, + script_name: t.Optional[str] = None, + subdomain: t.Optional[str] = None, + url_scheme: str = "http", + default_method: str = "GET", + path_info: t.Optional[str] = None, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + ) -> "MapAdapter": + """Return a new :class:`MapAdapter` with the details specified to the + call. Note that `script_name` will default to ``'/'`` if not further + specified or `None`. The `server_name` at least is a requirement + because the HTTP RFC requires absolute URLs for redirects and so all + redirect exceptions raised by Werkzeug will contain the full canonical + URL. + + If no path_info is passed to :meth:`match` it will use the default path + info passed to bind. While this doesn't really make sense for + manual bind calls, it's useful if you bind a map to a WSGI + environment which already contains the path info. + + `subdomain` will default to the `default_subdomain` for this map if + no defined. If there is no `default_subdomain` you cannot use the + subdomain feature. + + .. versionchanged:: 1.0 + If ``url_scheme`` is ``ws`` or ``wss``, only WebSocket rules + will match. + + .. versionchanged:: 0.15 + ``path_info`` defaults to ``'/'`` if ``None``. + + .. versionchanged:: 0.8 + ``query_args`` can be a string. + + .. versionchanged:: 0.7 + Added ``query_args``. + """ + server_name = server_name.lower() + if self.host_matching: + if subdomain is not None: + raise RuntimeError("host matching enabled and a subdomain was provided") + elif subdomain is None: + subdomain = self.default_subdomain + if script_name is None: + script_name = "/" + if path_info is None: + path_info = "/" + + try: + server_name = _encode_idna(server_name) # type: ignore + except UnicodeError as e: + raise BadHost() from e + + return MapAdapter( + self, + server_name, + script_name, + subdomain, + url_scheme, + path_info, + default_method, + query_args, + ) + + def bind_to_environ( + self, + environ: t.Union["WSGIEnvironment", "Request"], + server_name: t.Optional[str] = None, + subdomain: t.Optional[str] = None, + ) -> "MapAdapter": + """Like :meth:`bind` but you can pass it an WSGI environment and it + will fetch the information from that dictionary. Note that because of + limitations in the protocol there is no way to get the current + subdomain and real `server_name` from the environment. If you don't + provide it, Werkzeug will use `SERVER_NAME` and `SERVER_PORT` (or + `HTTP_HOST` if provided) as used `server_name` with disabled subdomain + feature. + + If `subdomain` is `None` but an environment and a server name is + provided it will calculate the current subdomain automatically. + Example: `server_name` is ``'example.com'`` and the `SERVER_NAME` + in the wsgi `environ` is ``'staging.dev.example.com'`` the calculated + subdomain will be ``'staging.dev'``. + + If the object passed as environ has an environ attribute, the value of + this attribute is used instead. This allows you to pass request + objects. Additionally `PATH_INFO` added as a default of the + :class:`MapAdapter` so that you don't have to pass the path info to + the match method. + + .. versionchanged:: 1.0.0 + If the passed server name specifies port 443, it will match + if the incoming scheme is ``https`` without a port. + + .. versionchanged:: 1.0.0 + A warning is shown when the passed server name does not + match the incoming WSGI server name. + + .. versionchanged:: 0.8 + This will no longer raise a ValueError when an unexpected server + name was passed. + + .. versionchanged:: 0.5 + previously this method accepted a bogus `calculate_subdomain` + parameter that did not have any effect. It was removed because + of that. + + :param environ: a WSGI environment. + :param server_name: an optional server name hint (see above). + :param subdomain: optionally the current subdomain (see above). + """ + env = _get_environ(environ) + wsgi_server_name = get_host(env).lower() + scheme = env["wsgi.url_scheme"] + upgrade = any( + v.strip() == "upgrade" + for v in env.get("HTTP_CONNECTION", "").lower().split(",") + ) + + if upgrade and env.get("HTTP_UPGRADE", "").lower() == "websocket": + scheme = "wss" if scheme == "https" else "ws" + + if server_name is None: + server_name = wsgi_server_name + else: + server_name = server_name.lower() + + # strip standard port to match get_host() + if scheme in {"http", "ws"} and server_name.endswith(":80"): + server_name = server_name[:-3] + elif scheme in {"https", "wss"} and server_name.endswith(":443"): + server_name = server_name[:-4] + + if subdomain is None and not self.host_matching: + cur_server_name = wsgi_server_name.split(".") + real_server_name = server_name.split(".") + offset = -len(real_server_name) + + if cur_server_name[offset:] != real_server_name: + # This can happen even with valid configs if the server was + # accessed directly by IP address under some situations. + # Instead of raising an exception like in Werkzeug 0.7 or + # earlier we go by an invalid subdomain which will result + # in a 404 error on matching. + warnings.warn( + f"Current server name {wsgi_server_name!r} doesn't match configured" + f" server name {server_name!r}", + stacklevel=2, + ) + subdomain = "" + else: + subdomain = ".".join(filter(None, cur_server_name[:offset])) + + def _get_wsgi_string(name: str) -> t.Optional[str]: + val = env.get(name) + if val is not None: + return _wsgi_decoding_dance(val, self.charset) + return None + + script_name = _get_wsgi_string("SCRIPT_NAME") + path_info = _get_wsgi_string("PATH_INFO") + query_args = _get_wsgi_string("QUERY_STRING") + return Map.bind( + self, + server_name, + script_name, + subdomain, + scheme, + env["REQUEST_METHOD"], + path_info, + query_args=query_args, + ) + + def update(self) -> None: + """Called before matching and building to keep the compiled rules + in the correct order after things changed. + """ + if not self._remap: + return + + with self._remap_lock: + if not self._remap: + return + + self._rules.sort(key=lambda x: x.match_compare_key()) + for rules in self._rules_by_endpoint.values(): + rules.sort(key=lambda x: x.build_compare_key()) + self._remap = False + + def __repr__(self) -> str: + rules = self.iter_rules() + return f"{type(self).__name__}({pformat(list(rules))})" + + +class MapAdapter: + + """Returned by :meth:`Map.bind` or :meth:`Map.bind_to_environ` and does + the URL matching and building based on runtime information. + """ + + def __init__( + self, + map: Map, + server_name: str, + script_name: str, + subdomain: t.Optional[str], + url_scheme: str, + path_info: str, + default_method: str, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + ): + self.map = map + self.server_name = _to_str(server_name) + script_name = _to_str(script_name) + if not script_name.endswith("/"): + script_name += "/" + self.script_name = script_name + self.subdomain = _to_str(subdomain) + self.url_scheme = _to_str(url_scheme) + self.path_info = _to_str(path_info) + self.default_method = _to_str(default_method) + self.query_args = query_args + self.websocket = self.url_scheme in {"ws", "wss"} + + def dispatch( + self, + view_func: t.Callable[[str, t.Mapping[str, t.Any]], "WSGIApplication"], + path_info: t.Optional[str] = None, + method: t.Optional[str] = None, + catch_http_exceptions: bool = False, + ) -> "WSGIApplication": + """Does the complete dispatching process. `view_func` is called with + the endpoint and a dict with the values for the view. It should + look up the view function, call it, and return a response object + or WSGI application. http exceptions are not caught by default + so that applications can display nicer error messages by just + catching them by hand. If you want to stick with the default + error messages you can pass it ``catch_http_exceptions=True`` and + it will catch the http exceptions. + + Here a small example for the dispatch usage:: + + from werkzeug.wrappers import Request, Response + from werkzeug.wsgi import responder + from werkzeug.routing import Map, Rule + + def on_index(request): + return Response('Hello from the index') + + url_map = Map([Rule('/', endpoint='index')]) + views = {'index': on_index} + + @responder + def application(environ, start_response): + request = Request(environ) + urls = url_map.bind_to_environ(environ) + return urls.dispatch(lambda e, v: views[e](request, **v), + catch_http_exceptions=True) + + Keep in mind that this method might return exception objects, too, so + use :class:`Response.force_type` to get a response object. + + :param view_func: a function that is called with the endpoint as + first argument and the value dict as second. Has + to dispatch to the actual view function with this + information. (see above) + :param path_info: the path info to use for matching. Overrides the + path info specified on binding. + :param method: the HTTP method used for matching. Overrides the + method specified on binding. + :param catch_http_exceptions: set to `True` to catch any of the + werkzeug :class:`HTTPException`\\s. + """ + try: + try: + endpoint, args = self.match(path_info, method) + except RequestRedirect as e: + return e + return view_func(endpoint, args) + except HTTPException as e: + if catch_http_exceptions: + return e + raise + + @typing.overload + def match( # type: ignore + self, + path_info: t.Optional[str] = None, + method: t.Optional[str] = None, + return_rule: "te.Literal[False]" = False, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + websocket: t.Optional[bool] = None, + ) -> t.Tuple[str, t.Mapping[str, t.Any]]: + ... + + @typing.overload + def match( + self, + path_info: t.Optional[str] = None, + method: t.Optional[str] = None, + return_rule: "te.Literal[True]" = True, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + websocket: t.Optional[bool] = None, + ) -> t.Tuple[Rule, t.Mapping[str, t.Any]]: + ... + + def match( + self, + path_info: t.Optional[str] = None, + method: t.Optional[str] = None, + return_rule: bool = False, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + websocket: t.Optional[bool] = None, + ) -> t.Tuple[t.Union[str, Rule], t.Mapping[str, t.Any]]: + """The usage is simple: you just pass the match method the current + path info as well as the method (which defaults to `GET`). The + following things can then happen: + + - you receive a `NotFound` exception that indicates that no URL is + matching. A `NotFound` exception is also a WSGI application you + can call to get a default page not found page (happens to be the + same object as `werkzeug.exceptions.NotFound`) + + - you receive a `MethodNotAllowed` exception that indicates that there + is a match for this URL but not for the current request method. + This is useful for RESTful applications. + + - you receive a `RequestRedirect` exception with a `new_url` + attribute. This exception is used to notify you about a request + Werkzeug requests from your WSGI application. This is for example the + case if you request ``/foo`` although the correct URL is ``/foo/`` + You can use the `RequestRedirect` instance as response-like object + similar to all other subclasses of `HTTPException`. + + - you receive a ``WebsocketMismatch`` exception if the only + match is a WebSocket rule but the bind is an HTTP request, or + if the match is an HTTP rule but the bind is a WebSocket + request. + + - you get a tuple in the form ``(endpoint, arguments)`` if there is + a match (unless `return_rule` is True, in which case you get a tuple + in the form ``(rule, arguments)``) + + If the path info is not passed to the match method the default path + info of the map is used (defaults to the root URL if not defined + explicitly). + + All of the exceptions raised are subclasses of `HTTPException` so they + can be used as WSGI responses. They will all render generic error or + redirect pages. + + Here is a small example for matching: + + >>> m = Map([ + ... Rule('/', endpoint='index'), + ... Rule('/downloads/', endpoint='downloads/index'), + ... Rule('/downloads/', endpoint='downloads/show') + ... ]) + >>> urls = m.bind("example.com", "/") + >>> urls.match("/", "GET") + ('index', {}) + >>> urls.match("/downloads/42") + ('downloads/show', {'id': 42}) + + And here is what happens on redirect and missing URLs: + + >>> urls.match("/downloads") + Traceback (most recent call last): + ... + RequestRedirect: http://example.com/downloads/ + >>> urls.match("/missing") + Traceback (most recent call last): + ... + NotFound: 404 Not Found + + :param path_info: the path info to use for matching. Overrides the + path info specified on binding. + :param method: the HTTP method used for matching. Overrides the + method specified on binding. + :param return_rule: return the rule that matched instead of just the + endpoint (defaults to `False`). + :param query_args: optional query arguments that are used for + automatic redirects as string or dictionary. It's + currently not possible to use the query arguments + for URL matching. + :param websocket: Match WebSocket instead of HTTP requests. A + websocket request has a ``ws`` or ``wss`` + :attr:`url_scheme`. This overrides that detection. + + .. versionadded:: 1.0 + Added ``websocket``. + + .. versionchanged:: 0.8 + ``query_args`` can be a string. + + .. versionadded:: 0.7 + Added ``query_args``. + + .. versionadded:: 0.6 + Added ``return_rule``. + """ + self.map.update() + if path_info is None: + path_info = self.path_info + else: + path_info = _to_str(path_info, self.map.charset) + if query_args is None: + query_args = self.query_args or {} + method = (method or self.default_method).upper() + + if websocket is None: + websocket = self.websocket + + require_redirect = False + + domain_part = self.server_name if self.map.host_matching else self.subdomain + path_part = f"/{path_info.lstrip('/')}" if path_info else "" + path = f"{domain_part}|{path_part}" + + have_match_for = set() + websocket_mismatch = False + + for rule in self.map._rules: + try: + rv = rule.match(path, method) + except RequestPath as e: + raise RequestRedirect( + self.make_redirect_url( + url_quote(e.path_info, self.map.charset, safe="/:|+"), + query_args, + ) + ) from None + except RequestAliasRedirect as e: + raise RequestRedirect( + self.make_alias_redirect_url( + path, rule.endpoint, e.matched_values, method, query_args + ) + ) from None + if rv is None: + continue + if rule.methods is not None and method not in rule.methods: + have_match_for.update(rule.methods) + continue + + if rule.websocket != websocket: + websocket_mismatch = True + continue + + if self.map.redirect_defaults: + redirect_url = self.get_default_redirect(rule, method, rv, query_args) + if redirect_url is not None: + raise RequestRedirect(redirect_url) + + if rule.redirect_to is not None: + if isinstance(rule.redirect_to, str): + + def _handle_match(match: t.Match[str]) -> str: + value = rv[match.group(1)] # type: ignore + return rule._converters[match.group(1)].to_url(value) + + redirect_url = _simple_rule_re.sub(_handle_match, rule.redirect_to) + else: + redirect_url = rule.redirect_to(self, **rv) + + if self.subdomain: + netloc = f"{self.subdomain}.{self.server_name}" + else: + netloc = self.server_name + + raise RequestRedirect( + url_join( + f"{self.url_scheme or 'http'}://{netloc}{self.script_name}", + redirect_url, + ) + ) + + if require_redirect: + raise RequestRedirect( + self.make_redirect_url( + url_quote(path_info, self.map.charset, safe="/:|+"), query_args + ) + ) + + if return_rule: + return rule, rv + else: + return rule.endpoint, rv + + if have_match_for: + raise MethodNotAllowed(valid_methods=list(have_match_for)) + + if websocket_mismatch: + raise WebsocketMismatch() + + raise NotFound() + + def test( + self, path_info: t.Optional[str] = None, method: t.Optional[str] = None + ) -> bool: + """Test if a rule would match. Works like `match` but returns `True` + if the URL matches, or `False` if it does not exist. + + :param path_info: the path info to use for matching. Overrides the + path info specified on binding. + :param method: the HTTP method used for matching. Overrides the + method specified on binding. + """ + try: + self.match(path_info, method) + except RequestRedirect: + pass + except HTTPException: + return False + return True + + def allowed_methods(self, path_info: t.Optional[str] = None) -> t.Iterable[str]: + """Returns the valid methods that match for a given path. + + .. versionadded:: 0.7 + """ + try: + self.match(path_info, method="--") + except MethodNotAllowed as e: + return e.valid_methods # type: ignore + except HTTPException: + pass + return [] + + def get_host(self, domain_part: t.Optional[str]) -> str: + """Figures out the full host name for the given domain part. The + domain part is a subdomain in case host matching is disabled or + a full host name. + """ + if self.map.host_matching: + if domain_part is None: + return self.server_name + return _to_str(domain_part, "ascii") + subdomain = domain_part + if subdomain is None: + subdomain = self.subdomain + else: + subdomain = _to_str(subdomain, "ascii") + + if subdomain: + return f"{subdomain}.{self.server_name}" + else: + return self.server_name + + def get_default_redirect( + self, + rule: Rule, + method: str, + values: t.MutableMapping[str, t.Any], + query_args: t.Union[t.Mapping[str, t.Any], str], + ) -> t.Optional[str]: + """A helper that returns the URL to redirect to if it finds one. + This is used for default redirecting only. + + :internal: + """ + assert self.map.redirect_defaults + for r in self.map._rules_by_endpoint[rule.endpoint]: + # every rule that comes after this one, including ourself + # has a lower priority for the defaults. We order the ones + # with the highest priority up for building. + if r is rule: + break + if r.provides_defaults_for(rule) and r.suitable_for(values, method): + values.update(r.defaults) # type: ignore + domain_part, path = r.build(values) # type: ignore + return self.make_redirect_url(path, query_args, domain_part=domain_part) + return None + + def encode_query_args(self, query_args: t.Union[t.Mapping[str, t.Any], str]) -> str: + if not isinstance(query_args, str): + return url_encode(query_args, self.map.charset) + return query_args + + def make_redirect_url( + self, + path_info: str, + query_args: t.Optional[t.Union[t.Mapping[str, t.Any], str]] = None, + domain_part: t.Optional[str] = None, + ) -> str: + """Creates a redirect URL. + + :internal: + """ + if query_args: + suffix = f"?{self.encode_query_args(query_args)}" + else: + suffix = "" + + scheme = self.url_scheme or "http" + host = self.get_host(domain_part) + path = posixpath.join(self.script_name.strip("/"), path_info.lstrip("/")) + return f"{scheme}://{host}/{path}{suffix}" + + def make_alias_redirect_url( + self, + path: str, + endpoint: str, + values: t.Mapping[str, t.Any], + method: str, + query_args: t.Union[t.Mapping[str, t.Any], str], + ) -> str: + """Internally called to make an alias redirect URL.""" + url = self.build( + endpoint, values, method, append_unknown=False, force_external=True + ) + if query_args: + url += f"?{self.encode_query_args(query_args)}" + assert url != path, "detected invalid alias setting. No canonical URL found" + return url + + def _partial_build( + self, + endpoint: str, + values: t.Mapping[str, t.Any], + method: t.Optional[str], + append_unknown: bool, + ) -> t.Optional[t.Tuple[str, str, bool]]: + """Helper for :meth:`build`. Returns subdomain and path for the + rule that accepts this endpoint, values and method. + + :internal: + """ + # in case the method is none, try with the default method first + if method is None: + rv = self._partial_build( + endpoint, values, self.default_method, append_unknown + ) + if rv is not None: + return rv + + # Default method did not match or a specific method is passed. + # Check all for first match with matching host. If no matching + # host is found, go with first result. + first_match = None + + for rule in self.map._rules_by_endpoint.get(endpoint, ()): + if rule.suitable_for(values, method): + build_rv = rule.build(values, append_unknown) + + if build_rv is not None: + rv = (build_rv[0], build_rv[1], rule.websocket) + if self.map.host_matching: + if rv[0] == self.server_name: + return rv + elif first_match is None: + first_match = rv + else: + return rv + + return first_match + + def build( + self, + endpoint: str, + values: t.Optional[t.Mapping[str, t.Any]] = None, + method: t.Optional[str] = None, + force_external: bool = False, + append_unknown: bool = True, + url_scheme: t.Optional[str] = None, + ) -> str: + """Building URLs works pretty much the other way round. Instead of + `match` you call `build` and pass it the endpoint and a dict of + arguments for the placeholders. + + The `build` function also accepts an argument called `force_external` + which, if you set it to `True` will force external URLs. Per default + external URLs (include the server name) will only be used if the + target URL is on a different subdomain. + + >>> m = Map([ + ... Rule('/', endpoint='index'), + ... Rule('/downloads/', endpoint='downloads/index'), + ... Rule('/downloads/', endpoint='downloads/show') + ... ]) + >>> urls = m.bind("example.com", "/") + >>> urls.build("index", {}) + '/' + >>> urls.build("downloads/show", {'id': 42}) + '/downloads/42' + >>> urls.build("downloads/show", {'id': 42}, force_external=True) + 'http://example.com/downloads/42' + + Because URLs cannot contain non ASCII data you will always get + bytes back. Non ASCII characters are urlencoded with the + charset defined on the map instance. + + Additional values are converted to strings and appended to the URL as + URL querystring parameters: + + >>> urls.build("index", {'q': 'My Searchstring'}) + '/?q=My+Searchstring' + + When processing those additional values, lists are furthermore + interpreted as multiple values (as per + :py:class:`werkzeug.datastructures.MultiDict`): + + >>> urls.build("index", {'q': ['a', 'b', 'c']}) + '/?q=a&q=b&q=c' + + Passing a ``MultiDict`` will also add multiple values: + + >>> urls.build("index", MultiDict((('p', 'z'), ('q', 'a'), ('q', 'b')))) + '/?p=z&q=a&q=b' + + If a rule does not exist when building a `BuildError` exception is + raised. + + The build method accepts an argument called `method` which allows you + to specify the method you want to have an URL built for if you have + different methods for the same endpoint specified. + + :param endpoint: the endpoint of the URL to build. + :param values: the values for the URL to build. Unhandled values are + appended to the URL as query parameters. + :param method: the HTTP method for the rule if there are different + URLs for different methods on the same endpoint. + :param force_external: enforce full canonical external URLs. If the URL + scheme is not provided, this will generate + a protocol-relative URL. + :param append_unknown: unknown parameters are appended to the generated + URL as query string argument. Disable this + if you want the builder to ignore those. + :param url_scheme: Scheme to use in place of the bound + :attr:`url_scheme`. + + .. versionchanged:: 2.0 + Added the ``url_scheme`` parameter. + + .. versionadded:: 0.6 + Added the ``append_unknown`` parameter. + """ + self.map.update() + + if values: + temp_values: t.Dict[str, t.Union[t.List[t.Any], t.Any]] = {} + always_list = isinstance(values, MultiDict) + key: str + value: t.Optional[t.Union[t.List[t.Any], t.Any]] + + # For MultiDict, dict.items(values) is like values.lists() + # without the call or list coercion overhead. + for key, value in dict.items(values): # type: ignore + if value is None: + continue + + if always_list or isinstance(value, (list, tuple)): + value = [v for v in value if v is not None] + + if not value: + continue + + if len(value) == 1: + value = value[0] + + temp_values[key] = value + + values = temp_values + else: + values = {} + + rv = self._partial_build(endpoint, values, method, append_unknown) + if rv is None: + raise BuildError(endpoint, values, method, self) + + domain_part, path, websocket = rv + host = self.get_host(domain_part) + + if url_scheme is None: + url_scheme = self.url_scheme + + # Always build WebSocket routes with the scheme (browsers + # require full URLs). If bound to a WebSocket, ensure that HTTP + # routes are built with an HTTP scheme. + secure = url_scheme in {"https", "wss"} + + if websocket: + force_external = True + url_scheme = "wss" if secure else "ws" + elif url_scheme: + url_scheme = "https" if secure else "http" + + # shortcut this. + if not force_external and ( + (self.map.host_matching and host == self.server_name) + or (not self.map.host_matching and domain_part == self.subdomain) + ): + return f"{self.script_name.rstrip('/')}/{path.lstrip('/')}" + + scheme = f"{url_scheme}:" if url_scheme else "" + return f"{scheme}//{host}{self.script_name[:-1]}/{path.lstrip('/')}" diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/sansio/__init__.py b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49036bf7d3ebf5788221a96adfbb3c32b94d3bfe GIT binary patch literal 195 zcmd1j<>g`kf&-TflR)%i5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;x_oenx(7s(w~! zaY<%gs(x@{UP@Aag?>O~Nk)F2esE!FreA(ZYO#K9Wm#%onSM@Yl70bL&REaTK)*P% zBvrQ{F*!Ri9jK-}wJ5tPwKQG7I5Dp{GhaVGJ~J<~BtBlRpz;=nO>TZlX-=vg$o^s` IAi=@_0HX0T#Q*>R literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3817a016adb37e5a72328a126503b6947acd06ac GIT binary patch literal 6469 zcmai2&2t>bb)V_^*xA_+f*`oyH;14gY8`>F>^QDal1++SS+vL{O+s<9qB0(AH-KI4 z&Mv!WNdj3^WI?&4ELRT6$>l2G$Wspf2Ykyt<%2!fR8pnfTs+vN3a$KJ&n~b4$Yi&s z=k@H#eEJI$*kgPK*cmai zWo?;_;}ewQyaGyPM5#iU5f`@dN(7L(EC

    XfVAPmO8b^B2Mbhtkes` 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 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/request.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/request.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f80e666960334e575a155edabf6c5322d0d5eefc GIT binary patch literal 17119 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 T4PCCH$2mUz!vBw@4;=l!79Y=5 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/utils.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f93dbb135e6c9c5bbbe57b10da6b25944e86e33 GIT binary patch literal 3934 zcmcIn&2Jn@74NF<8TX9GcGk&~4;EB}%_ak8CcBA57%d5_EGsOKiQ+^EOK7G&RpaTn zr+ZRWZO7IaAsmkU1*?@3i3|S)2mS(%eOifA&TvYy8-A~P#vfTEPPEl;Ue|l|s_K3G z>N#6jsC#(+_Ky!YKRx4l|D=zT&jLPvhSz)vB0b3xZ-YrL{e+MF4WD`7gCrPLHY$#< zB-PQ}#+>7;;KPj&{G6)EP_ajKRlDfPnwlR}WnIoc4K^AzR%7ylY(QfH8VgQi(Q)k2 zqN+J=>7qB_7d^~fno67{XR9`-%QNzwr<;dt-au&O;$9zD(z=dbz_-E_+D67{anX(@8?=)8UuBFb#2y-b&RiC zu9NQLJhQ3@hncZyG*U&w>fBf*yGd+pQPs-kI_=W}vu6%W4ewpN=IrI$t zhg>%%Y>OSh?UqU3@w#zey(`S4zuzKM>)tLbeCqw~ z#|ObAcoxX&bEa=i0y!tcKf#>`fvg==QZCs+#aRkqsR~Q;&eAVumJqY^2(ckZN(N+Urp&H?t@0dwX`6rS0{{dAx?SH0{yej!Jjh zNxaz}JK2rahu7OCw(3XYsJ|U;L1$O#?R}MRwM~@bi0$0QiD`}Zih4KA^ay_4R~N9T zse**;3=8@G_bI^L*mS4iXnmYy{U|ZFTgP(>*3$Esvi9I#bf8wVTGV3$r`jm(tHL){ z7nRLDY_n)QOk*lMS7$}Nj1h&X2vsT#am~5X(;-ahC6JB9S0n}zY4@=e zBkzQ(Y-Le7(^`KpjExw?8k(-MjZ^ORPI#=>b5$GLt45@#&aqZTrFMlhQ&)1mUg^_b zPrzDklx#W7a2s%yoTbU0=x3=#3A+`>nMR!tsMKTQjL3K}P#T6Ik4h!VOBrb?oc8P_ zW1ZQopC!{1{CaE&1(IZMg&-jw?2!SoGW7tqG())@$-d=Q>kRU=-zkrITr@p_1%-}c zqeN8dsV7kT{j4He_*8B|(>iq(;pEf9kdICSX_a~WPQH*gHb-7wo32rY<9~;sLwC!`s3Q-vkY-n@BJ3Mwupg}-aee$csj^TvzUknLA!_hSDHWj$5KY>}p~&%>Sr zAx%KitmsQL@NFXR5%~@X*24U^aWLFcwo8IrP%+;H2`+_zwGe*|z6^+4WXlA3ybk0C z)O|qqu8_T&&NADjz!ia`4dj>J>tYe)eV<@$_R!5iuYt1B&L;Awil_$rkFr@M*x#vEi(btw-cjXSRve@ zQb%85s0e{Nq>9c+qt&4nB<&|TG{MZ4Izmh(Xko-0FtvJrb;V7HFbLLUWO@`Gquul( z1-zoG>X*=q(h(1VF;f&e&TaIsriEUXT3U3rJ=bw%DR2#2LV{)Dll8l+tN$N-DS?Gj z?-V-iMg|9xaCz1e>*{FNvphWwR7WY;5%+M{12K-!I2xmQ>PH0ECT@;`mFOS}L8 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/sansio/multipart.py b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/multipart.py new file mode 100644 index 0000000..2d54422 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/multipart.py @@ -0,0 +1,260 @@ +import re +from dataclasses import dataclass +from enum import auto +from enum import Enum +from typing import cast +from typing import List +from typing import Optional +from typing import Tuple + +from .._internal import _to_bytes +from .._internal import _to_str +from ..datastructures import Headers +from ..exceptions import RequestEntityTooLarge +from ..http import parse_options_header + + +class Event: + pass + + +@dataclass(frozen=True) +class Preamble(Event): + data: bytes + + +@dataclass(frozen=True) +class Field(Event): + name: str + headers: Headers + + +@dataclass(frozen=True) +class File(Event): + name: str + filename: str + headers: Headers + + +@dataclass(frozen=True) +class Data(Event): + data: bytes + more_data: bool + + +@dataclass(frozen=True) +class Epilogue(Event): + data: bytes + + +class NeedData(Event): + pass + + +NEED_DATA = NeedData() + + +class State(Enum): + PREAMBLE = auto() + PART = auto() + DATA = auto() + EPILOGUE = auto() + COMPLETE = auto() + + +# Multipart line breaks MUST be CRLF (\r\n) by RFC-7578, except that +# many implementations break this and either use CR or LF alone. +LINE_BREAK = b"(?:\r\n|\n|\r)" +BLANK_LINE_RE = re.compile(b"(?:\r\n\r\n|\r\r|\n\n)", re.MULTILINE) +LINE_BREAK_RE = re.compile(LINE_BREAK, re.MULTILINE) +# Header values can be continued via a space or tab after the linebreak, as +# per RFC2231 +HEADER_CONTINUATION_RE = re.compile(b"%s[ \t]" % LINE_BREAK, re.MULTILINE) + + +class MultipartDecoder: + """Decodes a multipart message as bytes into Python events. + + The part data is returned as available to allow the caller to save + the data from memory to disk, if desired. + """ + + def __init__( + self, + boundary: bytes, + max_form_memory_size: Optional[int] = None, + ) -> None: + self.buffer = bytearray() + self.complete = False + self.max_form_memory_size = max_form_memory_size + self.state = State.PREAMBLE + self.boundary = boundary + + # Note in the below \h i.e. horizontal whitespace is used + # as [^\S\n\r] as \h isn't supported in python. + + # The preamble must end with a boundary where the boundary is + # prefixed by a line break, RFC2046. Except that many + # implementations including Werkzeug's tests omit the line + # break prefix. In addition the first boundary could be the + # epilogue boundary (for empty form-data) hence the matching + # group to understand if it is an epilogue boundary. + self.preamble_re = re.compile( + rb"%s?--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)" + % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK), + re.MULTILINE, + ) + # A boundary must include a line break prefix and suffix, and + # may include trailing whitespace. In addition the boundary + # could be the epilogue boundary hence the matching group to + # understand if it is an epilogue boundary. + self.boundary_re = re.compile( + rb"%s--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)" + % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK), + re.MULTILINE, + ) + + def last_newline(self) -> int: + try: + last_nl = self.buffer.rindex(b"\n") + except ValueError: + last_nl = len(self.buffer) + try: + last_cr = self.buffer.rindex(b"\r") + except ValueError: + last_cr = len(self.buffer) + + return min(last_nl, last_cr) + + def receive_data(self, data: Optional[bytes]) -> None: + if data is None: + self.complete = True + elif ( + self.max_form_memory_size is not None + and len(self.buffer) + len(data) > self.max_form_memory_size + ): + raise RequestEntityTooLarge() + else: + self.buffer.extend(data) + + def next_event(self) -> Event: + event: Event = NEED_DATA + + if self.state == State.PREAMBLE: + match = self.preamble_re.search(self.buffer) + if match is not None: + if match.group(1).startswith(b"--"): + self.state = State.EPILOGUE + else: + self.state = State.PART + data = bytes(self.buffer[: match.start()]) + del self.buffer[: match.end()] + event = Preamble(data=data) + + elif self.state == State.PART: + match = BLANK_LINE_RE.search(self.buffer) + if match is not None: + headers = self._parse_headers(self.buffer[: match.start()]) + del self.buffer[: match.end()] + + if "content-disposition" not in headers: + raise ValueError("Missing Content-Disposition header") + + disposition, extra = parse_options_header( + headers["content-disposition"] + ) + name = cast(str, extra.get("name")) + filename = extra.get("filename") + if filename is not None: + event = File( + filename=filename, + headers=headers, + name=name, + ) + else: + event = Field( + headers=headers, + name=name, + ) + self.state = State.DATA + + elif self.state == State.DATA: + if self.buffer.find(b"--" + self.boundary) == -1: + # No complete boundary in the buffer, but there may be + # a partial boundary at the end. As the boundary + # starts with either a nl or cr find the earliest and + # return up to that as data. + data_length = del_index = self.last_newline() + more_data = True + else: + match = self.boundary_re.search(self.buffer) + if match is not None: + if match.group(1).startswith(b"--"): + self.state = State.EPILOGUE + else: + self.state = State.PART + data_length = match.start() + del_index = match.end() + else: + data_length = del_index = self.last_newline() + more_data = match is None + + data = bytes(self.buffer[:data_length]) + del self.buffer[:del_index] + if data or not more_data: + event = Data(data=data, more_data=more_data) + + elif self.state == State.EPILOGUE and self.complete: + event = Epilogue(data=bytes(self.buffer)) + del self.buffer[:] + self.state = State.COMPLETE + + if self.complete and isinstance(event, NeedData): + raise ValueError(f"Invalid form-data cannot parse beyond {self.state}") + + return event + + def _parse_headers(self, data: bytes) -> Headers: + headers: List[Tuple[str, str]] = [] + # Merge the continued headers into one line + data = HEADER_CONTINUATION_RE.sub(b" ", data) + # Now there is one header per line + for line in data.splitlines(): + if line.strip() != b"": + name, value = _to_str(line).strip().split(":", 1) + headers.append((name.strip(), value.strip())) + return Headers(headers) + + +class MultipartEncoder: + def __init__(self, boundary: bytes) -> None: + self.boundary = boundary + self.state = State.PREAMBLE + + def send_event(self, event: Event) -> bytes: + if isinstance(event, Preamble) and self.state == State.PREAMBLE: + self.state = State.PART + return event.data + elif isinstance(event, (Field, File)) and self.state in { + State.PREAMBLE, + State.PART, + State.DATA, + }: + self.state = State.DATA + data = b"\r\n--" + self.boundary + b"\r\n" + data += b'Content-Disposition: form-data; name="%s"' % _to_bytes(event.name) + if isinstance(event, File): + data += b'; filename="%s"' % _to_bytes(event.filename) + data += b"\r\n" + for name, value in cast(Field, event).headers: + if name.lower() != "content-disposition": + data += _to_bytes(f"{name}: {value}\r\n") + data += b"\r\n" + return data + elif isinstance(event, Data) and self.state == State.DATA: + return event.data + elif isinstance(event, Epilogue): + self.state = State.COMPLETE + return b"\r\n--" + self.boundary + b"--\r\n" + event.data + else: + raise ValueError(f"Cannot generate {event} in state: {self.state}") diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/sansio/request.py b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/request.py new file mode 100644 index 0000000..adafc26 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/request.py @@ -0,0 +1,548 @@ +import typing as t +from datetime import datetime + +from .._internal import _to_str +from ..datastructures import Accept +from ..datastructures import Authorization +from ..datastructures import CharsetAccept +from ..datastructures import ETags +from ..datastructures import Headers +from ..datastructures import HeaderSet +from ..datastructures import IfRange +from ..datastructures import ImmutableList +from ..datastructures import ImmutableMultiDict +from ..datastructures import LanguageAccept +from ..datastructures import MIMEAccept +from ..datastructures import MultiDict +from ..datastructures import Range +from ..datastructures import RequestCacheControl +from ..http import parse_accept_header +from ..http import parse_authorization_header +from ..http import parse_cache_control_header +from ..http import parse_cookie +from ..http import parse_date +from ..http import parse_etags +from ..http import parse_if_range_header +from ..http import parse_list_header +from ..http import parse_options_header +from ..http import parse_range_header +from ..http import parse_set_header +from ..urls import url_decode +from ..user_agent import UserAgent +from ..useragents import _UserAgent as _DeprecatedUserAgent +from ..utils import cached_property +from ..utils import header_property +from .utils import get_current_url +from .utils import get_host + + +class Request: + """Represents the non-IO parts of a HTTP request, including the + method, URL info, and headers. + + This class is not meant for general use. It should only be used when + implementing WSGI, ASGI, or another HTTP application spec. Werkzeug + provides a WSGI implementation at :cls:`werkzeug.wrappers.Request`. + + :param method: The method the request was made with, such as + ``GET``. + :param scheme: The URL scheme of the protocol the request used, such + as ``https`` or ``wss``. + :param server: The address of the server. ``(host, port)``, + ``(path, None)`` for unix sockets, or ``None`` if not known. + :param root_path: The prefix that the application is mounted under. + This is prepended to generated URLs, but is not part of route + matching. + :param path: The path part of the URL after ``root_path``. + :param query_string: The part of the URL after the "?". + :param headers: The headers received with the request. + :param remote_addr: The address of the client sending the request. + + .. versionadded:: 2.0 + """ + + #: The charset used to decode most data in the request. + charset = "utf-8" + + #: the error handling procedure for errors, defaults to 'replace' + encoding_errors = "replace" + + #: the class to use for `args` and `form`. The default is an + #: :class:`~werkzeug.datastructures.ImmutableMultiDict` which supports + #: multiple values per key. alternatively it makes sense to use an + #: :class:`~werkzeug.datastructures.ImmutableOrderedMultiDict` which + #: preserves order or a :class:`~werkzeug.datastructures.ImmutableDict` + #: which is the fastest but only remembers the last key. It is also + #: possible to use mutable structures, but this is not recommended. + #: + #: .. versionadded:: 0.6 + parameter_storage_class: t.Type[MultiDict] = ImmutableMultiDict + + #: The type to be used for dict values from the incoming WSGI + #: environment. (For example for :attr:`cookies`.) By default an + #: :class:`~werkzeug.datastructures.ImmutableMultiDict` is used. + #: + #: .. versionchanged:: 1.0.0 + #: Changed to ``ImmutableMultiDict`` to support multiple values. + #: + #: .. versionadded:: 0.6 + dict_storage_class: t.Type[MultiDict] = ImmutableMultiDict + + #: the type to be used for list values from the incoming WSGI environment. + #: By default an :class:`~werkzeug.datastructures.ImmutableList` is used + #: (for example for :attr:`access_list`). + #: + #: .. versionadded:: 0.6 + list_storage_class: t.Type[t.List] = ImmutableList + + user_agent_class: t.Type[UserAgent] = _DeprecatedUserAgent + """The class used and returned by the :attr:`user_agent` property to + parse the header. Defaults to + :class:`~werkzeug.user_agent.UserAgent`, which does no parsing. An + extension can provide a subclass that uses a parser to provide other + data. + + .. versionadded:: 2.0 + """ + + #: Valid host names when handling requests. By default all hosts are + #: trusted, which means that whatever the client says the host is + #: will be accepted. + #: + #: Because ``Host`` and ``X-Forwarded-Host`` headers can be set to + #: any value by a malicious client, it is recommended to either set + #: this property or implement similar validation in the proxy (if + #: the application is being run behind one). + #: + #: .. versionadded:: 0.9 + trusted_hosts: t.Optional[t.List[str]] = None + + def __init__( + self, + method: str, + scheme: str, + server: t.Optional[t.Tuple[str, t.Optional[int]]], + root_path: str, + path: str, + query_string: bytes, + headers: Headers, + remote_addr: t.Optional[str], + ) -> None: + #: The method the request was made with, such as ``GET``. + self.method = method.upper() + #: The URL scheme of the protocol the request used, such as + #: ``https`` or ``wss``. + self.scheme = scheme + #: The address of the server. ``(host, port)``, ``(path, None)`` + #: for unix sockets, or ``None`` if not known. + self.server = server + #: The prefix that the application is mounted under, without a + #: trailing slash. :attr:`path` comes after this. + self.root_path = root_path.rstrip("/") + #: The path part of the URL after :attr:`root_path`. This is the + #: path used for routing within the application. + self.path = "/" + path.lstrip("/") + #: The part of the URL after the "?". This is the raw value, use + #: :attr:`args` for the parsed values. + self.query_string = query_string + #: The headers received with the request. + self.headers = headers + #: The address of the client sending the request. + self.remote_addr = remote_addr + + def __repr__(self) -> str: + try: + url = self.url + except Exception as e: + url = f"(invalid URL: {e})" + + return f"<{type(self).__name__} {url!r} [{self.method}]>" + + @property + def url_charset(self) -> str: + """The charset that is assumed for URLs. Defaults to the value + of :attr:`charset`. + + .. versionadded:: 0.6 + """ + return self.charset + + @cached_property + def args(self) -> "MultiDict[str, str]": + """The parsed URL parameters (the part in the URL after the question + mark). + + By default an + :class:`~werkzeug.datastructures.ImmutableMultiDict` + is returned from this function. This can be changed by setting + :attr:`parameter_storage_class` to a different type. This might + be necessary if the order of the form data is important. + """ + return url_decode( + self.query_string, + self.url_charset, + errors=self.encoding_errors, + cls=self.parameter_storage_class, + ) + + @cached_property + def access_route(self) -> t.List[str]: + """If a forwarded header exists this is a list of all ip addresses + from the client ip to the last proxy server. + """ + if "X-Forwarded-For" in self.headers: + return self.list_storage_class( + parse_list_header(self.headers["X-Forwarded-For"]) + ) + elif self.remote_addr is not None: + return self.list_storage_class([self.remote_addr]) + return self.list_storage_class() + + @cached_property + def full_path(self) -> str: + """Requested path, including the query string.""" + return f"{self.path}?{_to_str(self.query_string, self.url_charset)}" + + @property + def is_secure(self) -> bool: + """``True`` if the request was made with a secure protocol + (HTTPS or WSS). + """ + return self.scheme in {"https", "wss"} + + @cached_property + def url(self) -> str: + """The full request URL with the scheme, host, root path, path, + and query string.""" + return get_current_url( + self.scheme, self.host, self.root_path, self.path, self.query_string + ) + + @cached_property + def base_url(self) -> str: + """Like :attr:`url` but without the query string.""" + return get_current_url(self.scheme, self.host, self.root_path, self.path) + + @cached_property + def root_url(self) -> str: + """The request URL scheme, host, and root path. This is the root + that the application is accessed from. + """ + return get_current_url(self.scheme, self.host, self.root_path) + + @cached_property + def host_url(self) -> str: + """The request URL scheme and host only.""" + return get_current_url(self.scheme, self.host) + + @cached_property + def host(self) -> str: + """The host name the request was made to, including the port if + it's non-standard. Validated with :attr:`trusted_hosts`. + """ + return get_host( + self.scheme, self.headers.get("host"), self.server, self.trusted_hosts + ) + + @cached_property + def cookies(self) -> "ImmutableMultiDict[str, str]": + """A :class:`dict` with the contents of all cookies transmitted with + the request.""" + wsgi_combined_cookie = ";".join(self.headers.getlist("Cookie")) + return parse_cookie( # type: ignore + wsgi_combined_cookie, + self.charset, + self.encoding_errors, + cls=self.dict_storage_class, + ) + + # Common Descriptors + + content_type = header_property[str]( + "Content-Type", + doc="""The Content-Type entity-header field indicates the media + type of the entity-body sent to the recipient or, in the case of + the HEAD method, the media type that would have been sent had + the request been a GET.""", + read_only=True, + ) + + @cached_property + def content_length(self) -> t.Optional[int]: + """The Content-Length entity-header field indicates the size of the + entity-body in bytes or, in the case of the HEAD method, the size of + the entity-body that would have been sent had the request been a + GET. + """ + if self.headers.get("Transfer-Encoding", "") == "chunked": + return None + + content_length = self.headers.get("Content-Length") + if content_length is not None: + try: + return max(0, int(content_length)) + except (ValueError, TypeError): + pass + + return None + + content_encoding = header_property[str]( + "Content-Encoding", + doc="""The Content-Encoding entity-header field is used as a + modifier to the media-type. When present, its value indicates + what additional content codings have been applied to the + entity-body, and thus what decoding mechanisms must be applied + in order to obtain the media-type referenced by the Content-Type + header field. + + .. versionadded:: 0.9""", + read_only=True, + ) + content_md5 = header_property[str]( + "Content-MD5", + doc="""The Content-MD5 entity-header field, as defined in + RFC 1864, is an MD5 digest of the entity-body for the purpose of + providing an end-to-end message integrity check (MIC) of the + entity-body. (Note: a MIC is good for detecting accidental + modification of the entity-body in transit, but is not proof + against malicious attacks.) + + .. versionadded:: 0.9""", + read_only=True, + ) + referrer = header_property[str]( + "Referer", + doc="""The Referer[sic] request-header field allows the client + to specify, for the server's benefit, the address (URI) of the + resource from which the Request-URI was obtained (the + "referrer", although the header field is misspelled).""", + read_only=True, + ) + date = header_property( + "Date", + None, + parse_date, + doc="""The Date general-header field represents the date and + time at which the message was originated, having the same + semantics as orig-date in RFC 822. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """, + read_only=True, + ) + max_forwards = header_property( + "Max-Forwards", + None, + int, + doc="""The Max-Forwards request-header field provides a + mechanism with the TRACE and OPTIONS methods to limit the number + of proxies or gateways that can forward the request to the next + inbound server.""", + read_only=True, + ) + + def _parse_content_type(self) -> None: + if not hasattr(self, "_parsed_content_type"): + self._parsed_content_type = parse_options_header( + self.headers.get("Content-Type", "") + ) + + @property + def mimetype(self) -> str: + """Like :attr:`content_type`, but without parameters (eg, without + charset, type etc.) and always lowercase. For example if the content + type is ``text/HTML; charset=utf-8`` the mimetype would be + ``'text/html'``. + """ + self._parse_content_type() + return self._parsed_content_type[0].lower() + + @property + def mimetype_params(self) -> t.Dict[str, str]: + """The mimetype parameters as dict. For example if the content + type is ``text/html; charset=utf-8`` the params would be + ``{'charset': 'utf-8'}``. + """ + self._parse_content_type() + return self._parsed_content_type[1] + + @cached_property + def pragma(self) -> HeaderSet: + """The Pragma general-header field is used to include + implementation-specific directives that might apply to any recipient + along the request/response chain. All pragma directives specify + optional behavior from the viewpoint of the protocol; however, some + systems MAY require that behavior be consistent with the directives. + """ + return parse_set_header(self.headers.get("Pragma", "")) + + # Accept + + @cached_property + def accept_mimetypes(self) -> MIMEAccept: + """List of mimetypes this client supports as + :class:`~werkzeug.datastructures.MIMEAccept` object. + """ + return parse_accept_header(self.headers.get("Accept"), MIMEAccept) + + @cached_property + def accept_charsets(self) -> CharsetAccept: + """List of charsets this client supports as + :class:`~werkzeug.datastructures.CharsetAccept` object. + """ + return parse_accept_header(self.headers.get("Accept-Charset"), CharsetAccept) + + @cached_property + def accept_encodings(self) -> Accept: + """List of encodings this client accepts. Encodings in a HTTP term + are compression encodings such as gzip. For charsets have a look at + :attr:`accept_charset`. + """ + return parse_accept_header(self.headers.get("Accept-Encoding")) + + @cached_property + def accept_languages(self) -> LanguageAccept: + """List of languages this client accepts as + :class:`~werkzeug.datastructures.LanguageAccept` object. + + .. versionchanged 0.5 + In previous versions this was a regular + :class:`~werkzeug.datastructures.Accept` object. + """ + return parse_accept_header(self.headers.get("Accept-Language"), LanguageAccept) + + # ETag + + @cached_property + def cache_control(self) -> RequestCacheControl: + """A :class:`~werkzeug.datastructures.RequestCacheControl` object + for the incoming cache control headers. + """ + cache_control = self.headers.get("Cache-Control") + return parse_cache_control_header(cache_control, None, RequestCacheControl) + + @cached_property + def if_match(self) -> ETags: + """An object containing all the etags in the `If-Match` header. + + :rtype: :class:`~werkzeug.datastructures.ETags` + """ + return parse_etags(self.headers.get("If-Match")) + + @cached_property + def if_none_match(self) -> ETags: + """An object containing all the etags in the `If-None-Match` header. + + :rtype: :class:`~werkzeug.datastructures.ETags` + """ + return parse_etags(self.headers.get("If-None-Match")) + + @cached_property + def if_modified_since(self) -> t.Optional[datetime]: + """The parsed `If-Modified-Since` header as a datetime object. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """ + return parse_date(self.headers.get("If-Modified-Since")) + + @cached_property + def if_unmodified_since(self) -> t.Optional[datetime]: + """The parsed `If-Unmodified-Since` header as a datetime object. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """ + return parse_date(self.headers.get("If-Unmodified-Since")) + + @cached_property + def if_range(self) -> IfRange: + """The parsed ``If-Range`` header. + + .. versionchanged:: 2.0 + ``IfRange.date`` is timezone-aware. + + .. versionadded:: 0.7 + """ + return parse_if_range_header(self.headers.get("If-Range")) + + @cached_property + def range(self) -> t.Optional[Range]: + """The parsed `Range` header. + + .. versionadded:: 0.7 + + :rtype: :class:`~werkzeug.datastructures.Range` + """ + return parse_range_header(self.headers.get("Range")) + + # User Agent + + @cached_property + def user_agent(self) -> UserAgent: + """The user agent. Use ``user_agent.string`` to get the header + value. Set :attr:`user_agent_class` to a subclass of + :class:`~werkzeug.user_agent.UserAgent` to provide parsing for + the other properties or other extended data. + + .. versionchanged:: 2.0 + The built in parser is deprecated and will be removed in + Werkzeug 2.1. A ``UserAgent`` subclass must be set to parse + data from the string. + """ + return self.user_agent_class(self.headers.get("User-Agent", "")) + + # Authorization + + @cached_property + def authorization(self) -> t.Optional[Authorization]: + """The `Authorization` object in parsed form.""" + return parse_authorization_header(self.headers.get("Authorization")) + + # CORS + + origin = header_property[str]( + "Origin", + doc=( + "The host that the request originated from. Set" + " :attr:`~CORSResponseMixin.access_control_allow_origin` on" + " the response to indicate which origins are allowed." + ), + read_only=True, + ) + + access_control_request_headers = header_property( + "Access-Control-Request-Headers", + load_func=parse_set_header, + doc=( + "Sent with a preflight request to indicate which headers" + " will be sent with the cross origin request. Set" + " :attr:`~CORSResponseMixin.access_control_allow_headers`" + " on the response to indicate which headers are allowed." + ), + read_only=True, + ) + + access_control_request_method = header_property[str]( + "Access-Control-Request-Method", + doc=( + "Sent with a preflight request to indicate which method" + " will be used for the cross origin request. Set" + " :attr:`~CORSResponseMixin.access_control_allow_methods`" + " on the response to indicate which methods are allowed." + ), + read_only=True, + ) + + @property + def is_json(self) -> bool: + """Check if the mimetype indicates JSON data, either + :mimetype:`application/json` or :mimetype:`application/*+json`. + """ + mt = self.mimetype + return ( + mt == "application/json" + or mt.startswith("application/") + and mt.endswith("+json") + ) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/sansio/response.py b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/response.py new file mode 100644 index 0000000..82817e8 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/response.py @@ -0,0 +1,704 @@ +import typing as t +from datetime import datetime +from datetime import timedelta +from datetime import timezone +from http import HTTPStatus + +from .._internal import _to_str +from ..datastructures import Headers +from ..datastructures import HeaderSet +from ..http import dump_cookie +from ..http import HTTP_STATUS_CODES +from ..utils import get_content_type +from werkzeug.datastructures import CallbackDict +from werkzeug.datastructures import ContentRange +from werkzeug.datastructures import ContentSecurityPolicy +from werkzeug.datastructures import ResponseCacheControl +from werkzeug.datastructures import WWWAuthenticate +from werkzeug.http import COEP +from werkzeug.http import COOP +from werkzeug.http import dump_age +from werkzeug.http import dump_header +from werkzeug.http import dump_options_header +from werkzeug.http import http_date +from werkzeug.http import parse_age +from werkzeug.http import parse_cache_control_header +from werkzeug.http import parse_content_range_header +from werkzeug.http import parse_csp_header +from werkzeug.http import parse_date +from werkzeug.http import parse_options_header +from werkzeug.http import parse_set_header +from werkzeug.http import parse_www_authenticate_header +from werkzeug.http import quote_etag +from werkzeug.http import unquote_etag +from werkzeug.utils import header_property + + +def _set_property(name: str, doc: t.Optional[str] = None) -> property: + def fget(self: "Response") -> HeaderSet: + def on_update(header_set: HeaderSet) -> None: + if not header_set and name in self.headers: + del self.headers[name] + elif header_set: + self.headers[name] = header_set.to_header() + + return parse_set_header(self.headers.get(name), on_update) + + def fset( + self: "Response", + value: t.Optional[ + t.Union[str, t.Dict[str, t.Union[str, int]], t.Iterable[str]] + ], + ) -> None: + if not value: + del self.headers[name] + elif isinstance(value, str): + self.headers[name] = value + else: + self.headers[name] = dump_header(value) + + return property(fget, fset, doc=doc) + + +class Response: + """Represents the non-IO parts of an HTTP response, specifically the + status and headers but not the body. + + This class is not meant for general use. It should only be used when + implementing WSGI, ASGI, or another HTTP application spec. Werkzeug + provides a WSGI implementation at :cls:`werkzeug.wrappers.Response`. + + :param status: The status code for the response. Either an int, in + which case the default status message is added, or a string in + the form ``{code} {message}``, like ``404 Not Found``. Defaults + to 200. + :param headers: A :class:`~werkzeug.datastructures.Headers` object, + or a list of ``(key, value)`` tuples that will be converted to a + ``Headers`` object. + :param mimetype: The mime type (content type without charset or + other parameters) of the response. If the value starts with + ``text/`` (or matches some other special cases), the charset + will be added to create the ``content_type``. + :param content_type: The full content type of the response. + Overrides building the value from ``mimetype``. + + .. versionadded:: 2.0 + """ + + #: the charset of the response. + charset = "utf-8" + + #: the default status if none is provided. + default_status = 200 + + #: the default mimetype if none is provided. + default_mimetype = "text/plain" + + #: Warn if a cookie header exceeds this size. The default, 4093, should be + #: safely `supported by most browsers `_. A cookie larger than + #: this size will still be sent, but it may be ignored or handled + #: incorrectly by some browsers. Set to 0 to disable this check. + #: + #: .. versionadded:: 0.13 + #: + #: .. _`cookie`: http://browsercookielimits.squawky.net/ + max_cookie_size = 4093 + + # A :class:`Headers` object representing the response headers. + headers: Headers + + def __init__( + self, + status: t.Optional[t.Union[int, str, HTTPStatus]] = None, + headers: t.Optional[ + t.Union[ + t.Mapping[str, t.Union[str, int, t.Iterable[t.Union[str, int]]]], + t.Iterable[t.Tuple[str, t.Union[str, int]]], + ] + ] = None, + mimetype: t.Optional[str] = None, + content_type: t.Optional[str] = None, + ) -> None: + if isinstance(headers, Headers): + self.headers = headers + elif not headers: + self.headers = Headers() + else: + self.headers = Headers(headers) + + if content_type is None: + if mimetype is None and "content-type" not in self.headers: + mimetype = self.default_mimetype + if mimetype is not None: + mimetype = get_content_type(mimetype, self.charset) + content_type = mimetype + if content_type is not None: + self.headers["Content-Type"] = content_type + if status is None: + status = self.default_status + self.status = status # type: ignore + + def __repr__(self) -> str: + return f"<{type(self).__name__} [{self.status}]>" + + @property + def status_code(self) -> int: + """The HTTP status code as a number.""" + return self._status_code + + @status_code.setter + def status_code(self, code: int) -> None: + self.status = code # type: ignore + + @property + def status(self) -> str: + """The HTTP status code as a string.""" + return self._status + + @status.setter + def status(self, value: t.Union[str, int, HTTPStatus]) -> None: + if not isinstance(value, (str, bytes, int, HTTPStatus)): + raise TypeError("Invalid status argument") + + self._status, self._status_code = self._clean_status(value) + + def _clean_status(self, value: t.Union[str, int, HTTPStatus]) -> t.Tuple[str, int]: + if isinstance(value, HTTPStatus): + value = int(value) + status = _to_str(value, self.charset) + split_status = status.split(None, 1) + + if len(split_status) == 0: + raise ValueError("Empty status argument") + + if len(split_status) > 1: + if split_status[0].isdigit(): + # code and message + return status, int(split_status[0]) + + # multi-word message + return f"0 {status}", 0 + + if split_status[0].isdigit(): + # code only + status_code = int(split_status[0]) + + try: + status = f"{status_code} {HTTP_STATUS_CODES[status_code].upper()}" + except KeyError: + status = f"{status_code} UNKNOWN" + + return status, status_code + + # one-word message + return f"0 {status}", 0 + + def set_cookie( + self, + key: str, + value: str = "", + max_age: t.Optional[t.Union[timedelta, int]] = None, + expires: t.Optional[t.Union[str, datetime, int, float]] = None, + path: t.Optional[str] = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: t.Optional[str] = None, + ) -> None: + """Sets a cookie. + + A warning is raised if the size of the cookie header exceeds + :attr:`max_cookie_size`, but the header will still be set. + + :param key: the key (name) of the cookie to be set. + :param value: the value of the cookie. + :param max_age: should be a number of seconds, or `None` (default) if + the cookie should last only as long as the client's + browser session. + :param expires: should be a `datetime` object or UNIX timestamp. + :param path: limits the cookie to a given path, per default it will + span the whole domain. + :param domain: if you want to set a cross-domain cookie. For example, + ``domain=".example.com"`` will set a cookie that is + readable by the domain ``www.example.com``, + ``foo.example.com`` etc. Otherwise, a cookie will only + be readable by the domain that set it. + :param secure: If ``True``, the cookie will only be available + via HTTPS. + :param httponly: Disallow JavaScript access to the cookie. + :param samesite: Limit the scope of the cookie to only be + attached to requests that are "same-site". + """ + self.headers.add( + "Set-Cookie", + dump_cookie( + key, + value=value, + max_age=max_age, + expires=expires, + path=path, + domain=domain, + secure=secure, + httponly=httponly, + charset=self.charset, + max_size=self.max_cookie_size, + samesite=samesite, + ), + ) + + def delete_cookie( + self, + key: str, + path: str = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: t.Optional[str] = None, + ) -> None: + """Delete a cookie. Fails silently if key doesn't exist. + + :param key: the key (name) of the cookie to be deleted. + :param path: if the cookie that should be deleted was limited to a + path, the path has to be defined here. + :param domain: if the cookie that should be deleted was limited to a + domain, that domain has to be defined here. + :param secure: If ``True``, the cookie will only be available + via HTTPS. + :param httponly: Disallow JavaScript access to the cookie. + :param samesite: Limit the scope of the cookie to only be + attached to requests that are "same-site". + """ + self.set_cookie( + key, + expires=0, + max_age=0, + path=path, + domain=domain, + secure=secure, + httponly=httponly, + samesite=samesite, + ) + + @property + def is_json(self) -> bool: + """Check if the mimetype indicates JSON data, either + :mimetype:`application/json` or :mimetype:`application/*+json`. + """ + mt = self.mimetype + return mt is not None and ( + mt == "application/json" + or mt.startswith("application/") + and mt.endswith("+json") + ) + + # Common Descriptors + + @property + def mimetype(self) -> t.Optional[str]: + """The mimetype (content type without charset etc.)""" + ct = self.headers.get("content-type") + + if ct: + return ct.split(";")[0].strip() + else: + return None + + @mimetype.setter + def mimetype(self, value: str) -> None: + self.headers["Content-Type"] = get_content_type(value, self.charset) + + @property + def mimetype_params(self) -> t.Dict[str, str]: + """The mimetype parameters as dict. For example if the + content type is ``text/html; charset=utf-8`` the params would be + ``{'charset': 'utf-8'}``. + + .. versionadded:: 0.5 + """ + + def on_update(d: CallbackDict) -> None: + self.headers["Content-Type"] = dump_options_header(self.mimetype, d) + + d = parse_options_header(self.headers.get("content-type", ""))[1] + return CallbackDict(d, on_update) + + location = header_property[str]( + "Location", + doc="""The Location response-header field is used to redirect + the recipient to a location other than the Request-URI for + completion of the request or identification of a new + resource.""", + ) + age = header_property( + "Age", + None, + parse_age, + dump_age, # type: ignore + doc="""The Age response-header field conveys the sender's + estimate of the amount of time since the response (or its + revalidation) was generated at the origin server. + + Age values are non-negative decimal integers, representing time + in seconds.""", + ) + content_type = header_property[str]( + "Content-Type", + doc="""The Content-Type entity-header field indicates the media + type of the entity-body sent to the recipient or, in the case of + the HEAD method, the media type that would have been sent had + the request been a GET.""", + ) + content_length = header_property( + "Content-Length", + None, + int, + str, + doc="""The Content-Length entity-header field indicates the size + of the entity-body, in decimal number of OCTETs, sent to the + recipient or, in the case of the HEAD method, the size of the + entity-body that would have been sent had the request been a + GET.""", + ) + content_location = header_property[str]( + "Content-Location", + doc="""The Content-Location entity-header field MAY be used to + supply the resource location for the entity enclosed in the + message when that entity is accessible from a location separate + from the requested resource's URI.""", + ) + content_encoding = header_property[str]( + "Content-Encoding", + doc="""The Content-Encoding entity-header field is used as a + modifier to the media-type. When present, its value indicates + what additional content codings have been applied to the + entity-body, and thus what decoding mechanisms must be applied + in order to obtain the media-type referenced by the Content-Type + header field.""", + ) + content_md5 = header_property[str]( + "Content-MD5", + doc="""The Content-MD5 entity-header field, as defined in + RFC 1864, is an MD5 digest of the entity-body for the purpose of + providing an end-to-end message integrity check (MIC) of the + entity-body. (Note: a MIC is good for detecting accidental + modification of the entity-body in transit, but is not proof + against malicious attacks.)""", + ) + date = header_property( + "Date", + None, + parse_date, + http_date, + doc="""The Date general-header field represents the date and + time at which the message was originated, having the same + semantics as orig-date in RFC 822. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """, + ) + expires = header_property( + "Expires", + None, + parse_date, + http_date, + doc="""The Expires entity-header field gives the date/time after + which the response is considered stale. A stale cache entry may + not normally be returned by a cache. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """, + ) + last_modified = header_property( + "Last-Modified", + None, + parse_date, + http_date, + doc="""The Last-Modified entity-header field indicates the date + and time at which the origin server believes the variant was + last modified. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """, + ) + + @property + def retry_after(self) -> t.Optional[datetime]: + """The Retry-After response-header field can be used with a + 503 (Service Unavailable) response to indicate how long the + service is expected to be unavailable to the requesting client. + + Time in seconds until expiration or date. + + .. versionchanged:: 2.0 + The datetime object is timezone-aware. + """ + value = self.headers.get("retry-after") + if value is None: + return None + elif value.isdigit(): + return datetime.now(timezone.utc) + timedelta(seconds=int(value)) + return parse_date(value) + + @retry_after.setter + def retry_after(self, value: t.Optional[t.Union[datetime, int, str]]) -> None: + if value is None: + if "retry-after" in self.headers: + del self.headers["retry-after"] + return + elif isinstance(value, datetime): + value = http_date(value) + else: + value = str(value) + self.headers["Retry-After"] = value + + vary = _set_property( + "Vary", + doc="""The Vary field value indicates the set of request-header + fields that fully determines, while the response is fresh, + whether a cache is permitted to use the response to reply to a + subsequent request without revalidation.""", + ) + content_language = _set_property( + "Content-Language", + doc="""The Content-Language entity-header field describes the + natural language(s) of the intended audience for the enclosed + entity. Note that this might not be equivalent to all the + languages used within the entity-body.""", + ) + allow = _set_property( + "Allow", + doc="""The Allow entity-header field lists the set of methods + supported by the resource identified by the Request-URI. The + purpose of this field is strictly to inform the recipient of + valid methods associated with the resource. An Allow header + field MUST be present in a 405 (Method Not Allowed) + response.""", + ) + + # ETag + + @property + def cache_control(self) -> ResponseCacheControl: + """The Cache-Control general-header field is used to specify + directives that MUST be obeyed by all caching mechanisms along the + request/response chain. + """ + + def on_update(cache_control: ResponseCacheControl) -> None: + if not cache_control and "cache-control" in self.headers: + del self.headers["cache-control"] + elif cache_control: + self.headers["Cache-Control"] = cache_control.to_header() + + return parse_cache_control_header( + self.headers.get("cache-control"), on_update, ResponseCacheControl + ) + + def set_etag(self, etag: str, weak: bool = False) -> None: + """Set the etag, and override the old one if there was one.""" + self.headers["ETag"] = quote_etag(etag, weak) + + def get_etag(self) -> t.Union[t.Tuple[str, bool], t.Tuple[None, None]]: + """Return a tuple in the form ``(etag, is_weak)``. If there is no + ETag the return value is ``(None, None)``. + """ + return unquote_etag(self.headers.get("ETag")) + + accept_ranges = header_property[str]( + "Accept-Ranges", + doc="""The `Accept-Ranges` header. Even though the name would + indicate that multiple values are supported, it must be one + string token only. + + The values ``'bytes'`` and ``'none'`` are common. + + .. versionadded:: 0.7""", + ) + + @property + def content_range(self) -> ContentRange: + """The ``Content-Range`` header as a + :class:`~werkzeug.datastructures.ContentRange` object. Available + even if the header is not set. + + .. versionadded:: 0.7 + """ + + def on_update(rng: ContentRange) -> None: + if not rng: + del self.headers["content-range"] + else: + self.headers["Content-Range"] = rng.to_header() + + rv = parse_content_range_header(self.headers.get("content-range"), on_update) + # always provide a content range object to make the descriptor + # more user friendly. It provides an unset() method that can be + # used to remove the header quickly. + if rv is None: + rv = ContentRange(None, None, None, on_update=on_update) + return rv + + @content_range.setter + def content_range(self, value: t.Optional[t.Union[ContentRange, str]]) -> None: + if not value: + del self.headers["content-range"] + elif isinstance(value, str): + self.headers["Content-Range"] = value + else: + self.headers["Content-Range"] = value.to_header() + + # Authorization + + @property + def www_authenticate(self) -> WWWAuthenticate: + """The ``WWW-Authenticate`` header in a parsed form.""" + + def on_update(www_auth: WWWAuthenticate) -> None: + if not www_auth and "www-authenticate" in self.headers: + del self.headers["www-authenticate"] + elif www_auth: + self.headers["WWW-Authenticate"] = www_auth.to_header() + + header = self.headers.get("www-authenticate") + return parse_www_authenticate_header(header, on_update) + + # CSP + + @property + def content_security_policy(self) -> ContentSecurityPolicy: + """The ``Content-Security-Policy`` header as a + :class:`~werkzeug.datastructures.ContentSecurityPolicy` object. Available + even if the header is not set. + + The Content-Security-Policy header adds an additional layer of + security to help detect and mitigate certain types of attacks. + """ + + def on_update(csp: ContentSecurityPolicy) -> None: + if not csp: + del self.headers["content-security-policy"] + else: + self.headers["Content-Security-Policy"] = csp.to_header() + + rv = parse_csp_header(self.headers.get("content-security-policy"), on_update) + if rv is None: + rv = ContentSecurityPolicy(None, on_update=on_update) + return rv + + @content_security_policy.setter + def content_security_policy( + self, value: t.Optional[t.Union[ContentSecurityPolicy, str]] + ) -> None: + if not value: + del self.headers["content-security-policy"] + elif isinstance(value, str): + self.headers["Content-Security-Policy"] = value + else: + self.headers["Content-Security-Policy"] = value.to_header() + + @property + def content_security_policy_report_only(self) -> ContentSecurityPolicy: + """The ``Content-Security-policy-report-only`` header as a + :class:`~werkzeug.datastructures.ContentSecurityPolicy` object. Available + even if the header is not set. + + The Content-Security-Policy-Report-Only header adds a csp policy + that is not enforced but is reported thereby helping detect + certain types of attacks. + """ + + def on_update(csp: ContentSecurityPolicy) -> None: + if not csp: + del self.headers["content-security-policy-report-only"] + else: + self.headers["Content-Security-policy-report-only"] = csp.to_header() + + rv = parse_csp_header( + self.headers.get("content-security-policy-report-only"), on_update + ) + if rv is None: + rv = ContentSecurityPolicy(None, on_update=on_update) + return rv + + @content_security_policy_report_only.setter + def content_security_policy_report_only( + self, value: t.Optional[t.Union[ContentSecurityPolicy, str]] + ) -> None: + if not value: + del self.headers["content-security-policy-report-only"] + elif isinstance(value, str): + self.headers["Content-Security-policy-report-only"] = value + else: + self.headers["Content-Security-policy-report-only"] = value.to_header() + + # CORS + + @property + def access_control_allow_credentials(self) -> bool: + """Whether credentials can be shared by the browser to + JavaScript code. As part of the preflight request it indicates + whether credentials can be used on the cross origin request. + """ + return "Access-Control-Allow-Credentials" in self.headers + + @access_control_allow_credentials.setter + def access_control_allow_credentials(self, value: t.Optional[bool]) -> None: + if value is True: + self.headers["Access-Control-Allow-Credentials"] = "true" + else: + self.headers.pop("Access-Control-Allow-Credentials", None) + + access_control_allow_headers = header_property( + "Access-Control-Allow-Headers", + load_func=parse_set_header, + dump_func=dump_header, + doc="Which headers can be sent with the cross origin request.", + ) + + access_control_allow_methods = header_property( + "Access-Control-Allow-Methods", + load_func=parse_set_header, + dump_func=dump_header, + doc="Which methods can be used for the cross origin request.", + ) + + access_control_allow_origin = header_property[str]( + "Access-Control-Allow-Origin", + doc="The origin or '*' for any origin that may make cross origin requests.", + ) + + access_control_expose_headers = header_property( + "Access-Control-Expose-Headers", + load_func=parse_set_header, + dump_func=dump_header, + doc="Which headers can be shared by the browser to JavaScript code.", + ) + + access_control_max_age = header_property( + "Access-Control-Max-Age", + load_func=int, + dump_func=str, + doc="The maximum age in seconds the access control settings can be cached for.", + ) + + cross_origin_opener_policy = header_property[COOP]( + "Cross-Origin-Opener-Policy", + load_func=lambda value: COOP(value), + dump_func=lambda value: value.value, + default=COOP.UNSAFE_NONE, + doc="""Allows control over sharing of browsing context group with cross-origin + documents. Values must be a member of the :class:`werkzeug.http.COOP` enum.""", + ) + + cross_origin_embedder_policy = header_property[COEP]( + "Cross-Origin-Embedder-Policy", + load_func=lambda value: COEP(value), + dump_func=lambda value: value.value, + default=COEP.UNSAFE_NONE, + doc="""Prevents a document from loading any cross-origin resources that do not + explicitly grant the document permission. Values must be a member of the + :class:`werkzeug.http.COEP` enum.""", + ) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/sansio/utils.py b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/utils.py new file mode 100644 index 0000000..1b4d892 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/sansio/utils.py @@ -0,0 +1,142 @@ +import typing as t + +from .._internal import _encode_idna +from ..exceptions import SecurityError +from ..urls import uri_to_iri +from ..urls import url_quote + + +def host_is_trusted(hostname: str, trusted_list: t.Iterable[str]) -> bool: + """Check if a host matches a list of trusted names. + + :param hostname: The name to check. + :param trusted_list: A list of valid names to match. If a name + starts with a dot it will match all subdomains. + + .. versionadded:: 0.9 + """ + if not hostname: + return False + + if isinstance(trusted_list, str): + trusted_list = [trusted_list] + + def _normalize(hostname: str) -> bytes: + if ":" in hostname: + hostname = hostname.rsplit(":", 1)[0] + + return _encode_idna(hostname) + + try: + hostname_bytes = _normalize(hostname) + except UnicodeError: + return False + + for ref in trusted_list: + if ref.startswith("."): + ref = ref[1:] + suffix_match = True + else: + suffix_match = False + + try: + ref_bytes = _normalize(ref) + except UnicodeError: + return False + + if ref_bytes == hostname_bytes: + return True + + if suffix_match and hostname_bytes.endswith(b"." + ref_bytes): + return True + + return False + + +def get_host( + scheme: str, + host_header: t.Optional[str], + server: t.Optional[t.Tuple[str, t.Optional[int]]] = None, + trusted_hosts: t.Optional[t.Iterable[str]] = None, +) -> str: + """Return the host for the given parameters. + + This first checks the ``host_header``. If it's not present, then + ``server`` is used. The host will only contain the port if it is + different than the standard port for the protocol. + + Optionally, verify that the host is trusted using + :func:`host_is_trusted` and raise a + :exc:`~werkzeug.exceptions.SecurityError` if it is not. + + :param scheme: The protocol the request used, like ``"https"``. + :param host_header: The ``Host`` header value. + :param server: Address of the server. ``(host, port)``, or + ``(path, None)`` for unix sockets. + :param trusted_hosts: A list of trusted host names. + + :return: Host, with port if necessary. + :raise ~werkzeug.exceptions.SecurityError: If the host is not + trusted. + """ + host = "" + + if host_header is not None: + host = host_header + elif server is not None: + host = server[0] + + if server[1] is not None: + host = f"{host}:{server[1]}" + + if scheme in {"http", "ws"} and host.endswith(":80"): + host = host[:-3] + elif scheme in {"https", "wss"} and host.endswith(":443"): + host = host[:-4] + + if trusted_hosts is not None: + if not host_is_trusted(host, trusted_hosts): + raise SecurityError(f"Host {host!r} is not trusted.") + + return host + + +def get_current_url( + scheme: str, + host: str, + root_path: t.Optional[str] = None, + path: t.Optional[str] = None, + query_string: t.Optional[bytes] = None, +) -> str: + """Recreate the URL for a request. If an optional part isn't + provided, it and subsequent parts are not included in the URL. + + The URL is an IRI, not a URI, so it may contain Unicode characters. + Use :func:`~werkzeug.urls.iri_to_uri` to convert it to ASCII. + + :param scheme: The protocol the request used, like ``"https"``. + :param host: The host the request was made to. See :func:`get_host`. + :param root_path: Prefix that the application is mounted under. This + is prepended to ``path``. + :param path: The path part of the URL after ``root_path``. + :param query_string: The portion of the URL after the "?". + """ + url = [scheme, "://", host] + + if root_path is None: + url.append("/") + return uri_to_iri("".join(url)) + + url.append(url_quote(root_path.rstrip("/"))) + url.append("/") + + if path is None: + return uri_to_iri("".join(url)) + + url.append(url_quote(path.lstrip("/"))) + + if query_string: + url.append("?") + url.append(url_quote(query_string, safe=":&%=+$!*'(),")) + + return uri_to_iri("".join(url)) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/security.py b/myvenv/lib/python3.10/site-packages/werkzeug/security.py new file mode 100644 index 0000000..e23040a --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/security.py @@ -0,0 +1,247 @@ +import hashlib +import hmac +import os +import posixpath +import secrets +import typing as t +import warnings + +if t.TYPE_CHECKING: + pass + +SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" +DEFAULT_PBKDF2_ITERATIONS = 260000 + +_os_alt_seps: t.List[str] = list( + sep for sep in [os.path.sep, os.path.altsep] if sep is not None and sep != "/" +) + + +def pbkdf2_hex( + data: t.Union[str, bytes], + salt: t.Union[str, bytes], + iterations: int = DEFAULT_PBKDF2_ITERATIONS, + keylen: t.Optional[int] = None, + hashfunc: t.Optional[t.Union[str, t.Callable]] = None, +) -> str: + """Like :func:`pbkdf2_bin`, but returns a hex-encoded string. + + :param data: the data to derive. + :param salt: the salt for the derivation. + :param iterations: the number of iterations. + :param keylen: the length of the resulting key. If not provided, + the digest size will be used. + :param hashfunc: the hash function to use. This can either be the + string name of a known hash function, or a function + from the hashlib module. Defaults to sha256. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use :func:`hashlib.pbkdf2_hmac` + instead. + + .. versionadded:: 0.9 + """ + warnings.warn( + "'pbkdf2_hex' is deprecated and will be removed in Werkzeug" + " 2.1. Use 'hashlib.pbkdf2_hmac().hex()' instead.", + DeprecationWarning, + stacklevel=2, + ) + return pbkdf2_bin(data, salt, iterations, keylen, hashfunc).hex() + + +def pbkdf2_bin( + data: t.Union[str, bytes], + salt: t.Union[str, bytes], + iterations: int = DEFAULT_PBKDF2_ITERATIONS, + keylen: t.Optional[int] = None, + hashfunc: t.Optional[t.Union[str, t.Callable]] = None, +) -> bytes: + """Returns a binary digest for the PBKDF2 hash algorithm of `data` + with the given `salt`. It iterates `iterations` times and produces a + key of `keylen` bytes. By default, SHA-256 is used as hash function; + a different hashlib `hashfunc` can be provided. + + :param data: the data to derive. + :param salt: the salt for the derivation. + :param iterations: the number of iterations. + :param keylen: the length of the resulting key. If not provided + the digest size will be used. + :param hashfunc: the hash function to use. This can either be the + string name of a known hash function or a function + from the hashlib module. Defaults to sha256. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use :func:`hashlib.pbkdf2_hmac` + instead. + + .. versionadded:: 0.9 + """ + warnings.warn( + "'pbkdf2_bin' is deprecated and will be removed in Werkzeug" + " 2.1. Use 'hashlib.pbkdf2_hmac()' instead.", + DeprecationWarning, + stacklevel=2, + ) + + if isinstance(data, str): + data = data.encode("utf8") + + if isinstance(salt, str): + salt = salt.encode("utf8") + + if not hashfunc: + hash_name = "sha256" + elif callable(hashfunc): + hash_name = hashfunc().name + else: + hash_name = hashfunc + + return hashlib.pbkdf2_hmac(hash_name, data, salt, iterations, keylen) + + +def safe_str_cmp(a: str, b: str) -> bool: + """This function compares strings in somewhat constant time. This + requires that the length of at least one string is known in advance. + + Returns `True` if the two strings are equal, or `False` if they are not. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use + :func:`hmac.compare_digest` instead. + + .. versionadded:: 0.7 + """ + warnings.warn( + "'safe_str_cmp' is deprecated and will be removed in Werkzeug" + " 2.1. Use 'hmac.compare_digest' instead.", + DeprecationWarning, + stacklevel=2, + ) + + if isinstance(a, str): + a = a.encode("utf-8") # type: ignore + + if isinstance(b, str): + b = b.encode("utf-8") # type: ignore + + return hmac.compare_digest(a, b) + + +def gen_salt(length: int) -> str: + """Generate a random string of SALT_CHARS with specified ``length``.""" + if length <= 0: + raise ValueError("Salt length must be positive") + + return "".join(secrets.choice(SALT_CHARS) for _ in range(length)) + + +def _hash_internal(method: str, salt: str, password: str) -> t.Tuple[str, str]: + """Internal password hash helper. Supports plaintext without salt, + unsalted and salted passwords. In case salted passwords are used + hmac is used. + """ + if method == "plain": + return password, method + + salt = salt.encode("utf-8") + password = password.encode("utf-8") + + if method.startswith("pbkdf2:"): + if not salt: + raise ValueError("Salt is required for PBKDF2") + + args = method[7:].split(":") + + if len(args) not in (1, 2): + raise ValueError("Invalid number of arguments for PBKDF2") + + method = args.pop(0) + iterations = int(args[0] or 0) if args else DEFAULT_PBKDF2_ITERATIONS + return ( + hashlib.pbkdf2_hmac(method, password, salt, iterations).hex(), + f"pbkdf2:{method}:{iterations}", + ) + + if salt: + return hmac.new(salt, password, method).hexdigest(), method + + return hashlib.new(method, password).hexdigest(), method + + +def generate_password_hash( + password: str, method: str = "pbkdf2:sha256", salt_length: int = 16 +) -> str: + """Hash a password with the given method and salt with a string of + the given length. The format of the string returned includes the method + that was used so that :func:`check_password_hash` can check the hash. + + The format for the hashed string looks like this:: + + method$salt$hash + + This method can **not** generate unsalted passwords but it is possible + to set param method='plain' in order to enforce plaintext passwords. + If a salt is used, hmac is used internally to salt the password. + + If PBKDF2 is wanted it can be enabled by setting the method to + ``pbkdf2:method:iterations`` where iterations is optional:: + + pbkdf2:sha256:80000$salt$hash + pbkdf2:sha256$salt$hash + + :param password: the password to hash. + :param method: the hash method to use (one that hashlib supports). Can + optionally be in the format ``pbkdf2:method:iterations`` + to enable PBKDF2. + :param salt_length: the length of the salt in letters. + """ + salt = gen_salt(salt_length) if method != "plain" else "" + h, actual_method = _hash_internal(method, salt, password) + return f"{actual_method}${salt}${h}" + + +def check_password_hash(pwhash: str, password: str) -> bool: + """Check a password against a given salted and hashed password value. + In order to support unsalted legacy passwords this method supports + plain text passwords, md5 and sha1 hashes (both salted and unsalted). + + Returns `True` if the password matched, `False` otherwise. + + :param pwhash: a hashed string like returned by + :func:`generate_password_hash`. + :param password: the plaintext password to compare against the hash. + """ + if pwhash.count("$") < 2: + return False + + method, salt, hashval = pwhash.split("$", 2) + return hmac.compare_digest(_hash_internal(method, salt, password)[0], hashval) + + +def safe_join(directory: str, *pathnames: str) -> t.Optional[str]: + """Safely join zero or more untrusted path components to a base + directory to avoid escaping the base directory. + + :param directory: The trusted base directory. + :param pathnames: The untrusted path components relative to the + base directory. + :return: A safe path, otherwise ``None``. + """ + parts = [directory] + + for filename in pathnames: + if filename != "": + filename = posixpath.normpath(filename) + + if ( + any(sep in filename for sep in _os_alt_seps) + or os.path.isabs(filename) + or filename == ".." + or filename.startswith("../") + ): + return None + + parts.append(filename) + + return posixpath.join(*parts) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/serving.py b/myvenv/lib/python3.10/site-packages/werkzeug/serving.py new file mode 100644 index 0000000..80e4192 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/serving.py @@ -0,0 +1,1088 @@ +"""A WSGI and HTTP server for use **during development only**. This +server is convenient to use, but is not designed to be particularly +stable, secure, or efficient. Use a dedicate WSGI server and HTTP +server when deploying to production. + +It provides features like interactive debugging and code reloading. Use +``run_simple`` to start the server. Put this in a ``run.py`` script: + +.. code-block:: python + + from myapp import create_app + from werkzeug import run_simple +""" +import io +import os +import platform +import signal +import socket +import socketserver +import sys +import typing as t +import warnings +from datetime import datetime as dt +from datetime import timedelta +from datetime import timezone +from http.server import BaseHTTPRequestHandler +from http.server import HTTPServer + +from ._internal import _log +from ._internal import _wsgi_encoding_dance +from .exceptions import InternalServerError +from .urls import uri_to_iri +from .urls import url_parse +from .urls import url_unquote + +try: + import ssl +except ImportError: + + class _SslDummy: + def __getattr__(self, name: str) -> t.Any: + raise RuntimeError( # noqa: B904 + "SSL is unavailable because this Python runtime was not" + " compiled with SSL/TLS support." + ) + + ssl = _SslDummy() # type: ignore + +_log_add_style = True + +if os.name == "nt": + try: + __import__("colorama") + except ImportError: + _log_add_style = False + +can_fork = hasattr(os, "fork") + +if can_fork: + ForkingMixIn = socketserver.ForkingMixIn +else: + + class ForkingMixIn: # type: ignore + pass + + +try: + af_unix = socket.AF_UNIX +except AttributeError: + af_unix = None # type: ignore + +LISTEN_QUEUE = 128 +can_open_by_fd = not platform.system() == "Windows" and hasattr(socket, "fromfd") + +_TSSLContextArg = t.Optional[ + t.Union["ssl.SSLContext", t.Tuple[str, t.Optional[str]], "te.Literal['adhoc']"] +] + +if t.TYPE_CHECKING: + import typing_extensions as te # noqa: F401 + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + from cryptography.hazmat.primitives.asymmetric.rsa import ( + RSAPrivateKeyWithSerialization, + ) + from cryptography.x509 import Certificate + + +class DechunkedInput(io.RawIOBase): + """An input stream that handles Transfer-Encoding 'chunked'""" + + def __init__(self, rfile: t.IO[bytes]) -> None: + self._rfile = rfile + self._done = False + self._len = 0 + + def readable(self) -> bool: + return True + + def read_chunk_len(self) -> int: + try: + line = self._rfile.readline().decode("latin1") + _len = int(line.strip(), 16) + except ValueError as e: + raise OSError("Invalid chunk header") from e + if _len < 0: + raise OSError("Negative chunk length not allowed") + return _len + + def readinto(self, buf: bytearray) -> int: # type: ignore + read = 0 + while not self._done and read < len(buf): + if self._len == 0: + # This is the first chunk or we fully consumed the previous + # one. Read the next length of the next chunk + self._len = self.read_chunk_len() + + if self._len == 0: + # Found the final chunk of size 0. The stream is now exhausted, + # but there is still a final newline that should be consumed + self._done = True + + if self._len > 0: + # There is data (left) in this chunk, so append it to the + # buffer. If this operation fully consumes the chunk, this will + # reset self._len to 0. + n = min(len(buf), self._len) + + # If (read + chunk size) becomes more than len(buf), buf will + # grow beyond the original size and read more data than + # required. So only read as much data as can fit in buf. + if read + n > len(buf): + buf[read:] = self._rfile.read(len(buf) - read) + self._len -= len(buf) - read + read = len(buf) + else: + buf[read : read + n] = self._rfile.read(n) + self._len -= n + read += n + + if self._len == 0: + # Skip the terminating newline of a chunk that has been fully + # consumed. This also applies to the 0-sized final chunk + terminator = self._rfile.readline() + if terminator not in (b"\n", b"\r\n", b"\r"): + raise OSError("Missing chunk terminating newline") + + return read + + +class WSGIRequestHandler(BaseHTTPRequestHandler): + """A request handler that implements WSGI dispatching.""" + + server: "BaseWSGIServer" + + @property + def server_version(self) -> str: # type: ignore + from . import __version__ + + return f"Werkzeug/{__version__}" + + def make_environ(self) -> "WSGIEnvironment": + request_url = url_parse(self.path) + + def shutdown_server() -> None: + warnings.warn( + "The 'environ['werkzeug.server.shutdown']' function is" + " deprecated and will be removed in Werkzeug 2.1.", + stacklevel=2, + ) + self.server.shutdown_signal = True + + url_scheme = "http" if self.server.ssl_context is None else "https" + + if not self.client_address: + self.client_address = ("", 0) + elif isinstance(self.client_address, str): + self.client_address = (self.client_address, 0) + + # If there was no scheme but the path started with two slashes, + # the first segment may have been incorrectly parsed as the + # netloc, prepend it to the path again. + if not request_url.scheme and request_url.netloc: + path_info = f"/{request_url.netloc}{request_url.path}" + else: + path_info = request_url.path + + path_info = url_unquote(path_info) + + environ: "WSGIEnvironment" = { + "wsgi.version": (1, 0), + "wsgi.url_scheme": url_scheme, + "wsgi.input": self.rfile, + "wsgi.errors": sys.stderr, + "wsgi.multithread": self.server.multithread, + "wsgi.multiprocess": self.server.multiprocess, + "wsgi.run_once": False, + "werkzeug.server.shutdown": shutdown_server, + "werkzeug.socket": self.connection, + "SERVER_SOFTWARE": self.server_version, + "REQUEST_METHOD": self.command, + "SCRIPT_NAME": "", + "PATH_INFO": _wsgi_encoding_dance(path_info), + "QUERY_STRING": _wsgi_encoding_dance(request_url.query), + # Non-standard, added by mod_wsgi, uWSGI + "REQUEST_URI": _wsgi_encoding_dance(self.path), + # Non-standard, added by gunicorn + "RAW_URI": _wsgi_encoding_dance(self.path), + "REMOTE_ADDR": self.address_string(), + "REMOTE_PORT": self.port_integer(), + "SERVER_NAME": self.server.server_address[0], + "SERVER_PORT": str(self.server.server_address[1]), + "SERVER_PROTOCOL": self.request_version, + } + + for key, value in self.headers.items(): + key = key.upper().replace("-", "_") + value = value.replace("\r\n", "") + if key not in ("CONTENT_TYPE", "CONTENT_LENGTH"): + key = f"HTTP_{key}" + if key in environ: + value = f"{environ[key]},{value}" + environ[key] = value + + if environ.get("HTTP_TRANSFER_ENCODING", "").strip().lower() == "chunked": + environ["wsgi.input_terminated"] = True + environ["wsgi.input"] = DechunkedInput(environ["wsgi.input"]) + + # Per RFC 2616, if the URL is absolute, use that as the host. + # We're using "has a scheme" to indicate an absolute URL. + if request_url.scheme and request_url.netloc: + environ["HTTP_HOST"] = request_url.netloc + + try: + # binary_form=False gives nicer information, but wouldn't be compatible with + # what Nginx or Apache could return. + peer_cert = self.connection.getpeercert( # type: ignore[attr-defined] + binary_form=True + ) + if peer_cert is not None: + # Nginx and Apache use PEM format. + environ["SSL_CLIENT_CERT"] = ssl.DER_cert_to_PEM_cert(peer_cert) + except ValueError: + # SSL handshake hasn't finished. + self.server.log("error", "Cannot fetch SSL peer certificate info") + except AttributeError: + # Not using TLS, the socket will not have getpeercert(). + pass + + return environ + + def run_wsgi(self) -> None: + if self.headers.get("Expect", "").lower().strip() == "100-continue": + self.wfile.write(b"HTTP/1.1 100 Continue\r\n\r\n") + + self.environ = environ = self.make_environ() + status_set: t.Optional[str] = None + headers_set: t.Optional[t.List[t.Tuple[str, str]]] = None + status_sent: t.Optional[str] = None + headers_sent: t.Optional[t.List[t.Tuple[str, str]]] = None + + def write(data: bytes) -> None: + nonlocal status_sent, headers_sent + assert status_set is not None, "write() before start_response" + assert headers_set is not None, "write() before start_response" + if status_sent is None: + status_sent = status_set + headers_sent = headers_set + try: + code_str, msg = status_sent.split(None, 1) + except ValueError: + code_str, msg = status_sent, "" + code = int(code_str) + self.send_response(code, msg) + header_keys = set() + for key, value in headers_sent: + self.send_header(key, value) + key = key.lower() + header_keys.add(key) + if not ( + "content-length" in header_keys + or environ["REQUEST_METHOD"] == "HEAD" + or code < 200 + or code in (204, 304) + ): + self.close_connection = True + self.send_header("Connection", "close") + if "server" not in header_keys: + self.send_header("Server", self.version_string()) + if "date" not in header_keys: + self.send_header("Date", self.date_time_string()) + self.end_headers() + + assert isinstance(data, bytes), "applications must write bytes" + self.wfile.write(data) + self.wfile.flush() + + def start_response(status, headers, exc_info=None): # type: ignore + nonlocal status_set, headers_set + if exc_info: + try: + if headers_sent: + raise exc_info[1].with_traceback(exc_info[2]) + finally: + exc_info = None + elif headers_set: + raise AssertionError("Headers already set") + status_set = status + headers_set = headers + return write + + def execute(app: "WSGIApplication") -> None: + application_iter = app(environ, start_response) + try: + for data in application_iter: + write(data) + if not headers_sent: + write(b"") + finally: + if hasattr(application_iter, "close"): + application_iter.close() # type: ignore + + try: + execute(self.server.app) + except (ConnectionError, socket.timeout) as e: + self.connection_dropped(e, environ) + except Exception: + if self.server.passthrough_errors: + raise + from .debug.tbtools import get_current_traceback + + traceback = get_current_traceback(ignore_system_exceptions=True) + try: + # if we haven't yet sent the headers but they are set + # we roll back to be able to set them again. + if status_sent is None: + status_set = None + headers_set = None + execute(InternalServerError()) + except Exception: + pass + self.server.log("error", "Error on request:\n%s", traceback.plaintext) + + def handle(self) -> None: + """Handles a request ignoring dropped connections.""" + try: + BaseHTTPRequestHandler.handle(self) + except (ConnectionError, socket.timeout) as e: + self.connection_dropped(e) + except Exception as e: + if self.server.ssl_context is not None and is_ssl_error(e): + self.log_error("SSL error occurred: %s", e) + else: + raise + if self.server.shutdown_signal: + self.initiate_shutdown() + + def initiate_shutdown(self) -> None: + if is_running_from_reloader(): + # Windows does not provide SIGKILL, go with SIGTERM then. + sig = getattr(signal, "SIGKILL", signal.SIGTERM) + os.kill(os.getpid(), sig) + + self.server._BaseServer__shutdown_request = True # type: ignore + + def connection_dropped( + self, error: BaseException, environ: t.Optional["WSGIEnvironment"] = None + ) -> None: + """Called if the connection was closed by the client. By default + nothing happens. + """ + + def handle_one_request(self) -> None: + """Handle a single HTTP request.""" + self.raw_requestline = self.rfile.readline() + if not self.raw_requestline: + self.close_connection = True + elif self.parse_request(): + self.run_wsgi() + + def send_response(self, code: int, message: t.Optional[str] = None) -> None: + """Send the response header and log the response code.""" + self.log_request(code) + if message is None: + message = self.responses[code][0] if code in self.responses else "" + if self.request_version != "HTTP/0.9": + hdr = f"{self.protocol_version} {code} {message}\r\n" + self.wfile.write(hdr.encode("ascii")) + + def version_string(self) -> str: + return super().version_string().strip() + + def address_string(self) -> str: + if getattr(self, "environ", None): + return self.environ["REMOTE_ADDR"] # type: ignore + + if not self.client_address: + return "" + + return self.client_address[0] + + def port_integer(self) -> int: + return self.client_address[1] + + def log_request( + self, code: t.Union[int, str] = "-", size: t.Union[int, str] = "-" + ) -> None: + try: + path = uri_to_iri(self.path) + msg = f"{self.command} {path} {self.request_version}" + except AttributeError: + # path isn't set if the requestline was bad + msg = self.requestline + + code = str(code) + + if _log_add_style: + if code[0] == "1": # 1xx - Informational + msg = _ansi_style(msg, "bold") + elif code == "200": # 2xx - Success + pass + elif code == "304": # 304 - Resource Not Modified + msg = _ansi_style(msg, "cyan") + elif code[0] == "3": # 3xx - Redirection + msg = _ansi_style(msg, "green") + elif code == "404": # 404 - Resource Not Found + msg = _ansi_style(msg, "yellow") + elif code[0] == "4": # 4xx - Client Error + msg = _ansi_style(msg, "bold", "red") + else: # 5xx, or any other response + msg = _ansi_style(msg, "bold", "magenta") + + self.log("info", '"%s" %s %s', msg, code, size) + + def log_error(self, format: str, *args: t.Any) -> None: + self.log("error", format, *args) + + def log_message(self, format: str, *args: t.Any) -> None: + self.log("info", format, *args) + + def log(self, type: str, message: str, *args: t.Any) -> None: + _log( + type, + f"{self.address_string()} - - [{self.log_date_time_string()}] {message}\n", + *args, + ) + + +def _ansi_style(value: str, *styles: str) -> str: + codes = { + "bold": 1, + "red": 31, + "green": 32, + "yellow": 33, + "magenta": 35, + "cyan": 36, + } + + for style in styles: + value = f"\x1b[{codes[style]}m{value}" + + return f"{value}\x1b[0m" + + +def generate_adhoc_ssl_pair( + cn: t.Optional[str] = None, +) -> t.Tuple["Certificate", "RSAPrivateKeyWithSerialization"]: + try: + from cryptography import x509 + from cryptography.x509.oid import NameOID + from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives import hashes + from cryptography.hazmat.primitives.asymmetric import rsa + except ImportError: + raise TypeError( + "Using ad-hoc certificates requires the cryptography library." + ) from None + + backend = default_backend() + pkey = rsa.generate_private_key( + public_exponent=65537, key_size=2048, backend=backend + ) + + # pretty damn sure that this is not actually accepted by anyone + if cn is None: + cn = "*" + + subject = x509.Name( + [ + x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Dummy Certificate"), + x509.NameAttribute(NameOID.COMMON_NAME, cn), + ] + ) + + backend = default_backend() + cert = ( + x509.CertificateBuilder() + .subject_name(subject) + .issuer_name(subject) + .public_key(pkey.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(dt.now(timezone.utc)) + .not_valid_after(dt.now(timezone.utc) + timedelta(days=365)) + .add_extension(x509.ExtendedKeyUsage([x509.OID_SERVER_AUTH]), critical=False) + .add_extension(x509.SubjectAlternativeName([x509.DNSName(cn)]), critical=False) + .sign(pkey, hashes.SHA256(), backend) + ) + return cert, pkey + + +def make_ssl_devcert( + base_path: str, host: t.Optional[str] = None, cn: t.Optional[str] = None +) -> t.Tuple[str, str]: + """Creates an SSL key for development. This should be used instead of + the ``'adhoc'`` key which generates a new cert on each server start. + It accepts a path for where it should store the key and cert and + either a host or CN. If a host is given it will use the CN + ``*.host/CN=host``. + + For more information see :func:`run_simple`. + + .. versionadded:: 0.9 + + :param base_path: the path to the certificate and key. The extension + ``.crt`` is added for the certificate, ``.key`` is + added for the key. + :param host: the name of the host. This can be used as an alternative + for the `cn`. + :param cn: the `CN` to use. + """ + + if host is not None: + cn = f"*.{host}/CN={host}" + cert, pkey = generate_adhoc_ssl_pair(cn=cn) + + from cryptography.hazmat.primitives import serialization + + cert_file = f"{base_path}.crt" + pkey_file = f"{base_path}.key" + + with open(cert_file, "wb") as f: + f.write(cert.public_bytes(serialization.Encoding.PEM)) + with open(pkey_file, "wb") as f: + f.write( + pkey.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + ) + + return cert_file, pkey_file + + +def generate_adhoc_ssl_context() -> "ssl.SSLContext": + """Generates an adhoc SSL context for the development server.""" + import tempfile + import atexit + + cert, pkey = generate_adhoc_ssl_pair() + + from cryptography.hazmat.primitives import serialization + + cert_handle, cert_file = tempfile.mkstemp() + pkey_handle, pkey_file = tempfile.mkstemp() + atexit.register(os.remove, pkey_file) + atexit.register(os.remove, cert_file) + + os.write(cert_handle, cert.public_bytes(serialization.Encoding.PEM)) + os.write( + pkey_handle, + pkey.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ), + ) + + os.close(cert_handle) + os.close(pkey_handle) + ctx = load_ssl_context(cert_file, pkey_file) + return ctx + + +def load_ssl_context( + cert_file: str, pkey_file: t.Optional[str] = None, protocol: t.Optional[int] = None +) -> "ssl.SSLContext": + """Loads SSL context from cert/private key files and optional protocol. + Many parameters are directly taken from the API of + :py:class:`ssl.SSLContext`. + + :param cert_file: Path of the certificate to use. + :param pkey_file: Path of the private key to use. If not given, the key + will be obtained from the certificate file. + :param protocol: A ``PROTOCOL`` constant from the :mod:`ssl` module. + Defaults to :data:`ssl.PROTOCOL_TLS_SERVER`. + """ + if protocol is None: + protocol = ssl.PROTOCOL_TLS_SERVER + + ctx = ssl.SSLContext(protocol) + ctx.load_cert_chain(cert_file, pkey_file) + return ctx + + +def is_ssl_error(error: t.Optional[Exception] = None) -> bool: + """Checks if the given error (or the current one) is an SSL error.""" + if error is None: + error = t.cast(Exception, sys.exc_info()[1]) + return isinstance(error, ssl.SSLError) + + +def select_address_family(host: str, port: int) -> socket.AddressFamily: + """Return ``AF_INET4``, ``AF_INET6``, or ``AF_UNIX`` depending on + the host and port.""" + if host.startswith("unix://"): + return socket.AF_UNIX + elif ":" in host and hasattr(socket, "AF_INET6"): + return socket.AF_INET6 + return socket.AF_INET + + +def get_sockaddr( + host: str, port: int, family: socket.AddressFamily +) -> t.Union[t.Tuple[str, int], str]: + """Return a fully qualified socket address that can be passed to + :func:`socket.bind`.""" + if family == af_unix: + return host.split("://", 1)[1] + try: + res = socket.getaddrinfo( + host, port, family, socket.SOCK_STREAM, socket.IPPROTO_TCP + ) + except socket.gaierror: + return host, port + return res[0][4] # type: ignore + + +def get_interface_ip(family: socket.AddressFamily) -> str: + """Get the IP address of an external interface. Used when binding to + 0.0.0.0 or ::1 to show a more useful URL. + + :meta private: + """ + # arbitrary private address + host = "fd31:f903:5ab5:1::1" if family == socket.AF_INET6 else "10.253.155.219" + + with socket.socket(family, socket.SOCK_DGRAM) as s: + try: + s.connect((host, 58162)) + except OSError: + return "::1" if family == socket.AF_INET6 else "127.0.0.1" + + return s.getsockname()[0] # type: ignore + + +class BaseWSGIServer(HTTPServer): + + """Simple single-threaded, single-process WSGI server.""" + + multithread = False + multiprocess = False + request_queue_size = LISTEN_QUEUE + + def __init__( + self, + host: str, + port: int, + app: "WSGIApplication", + handler: t.Optional[t.Type[WSGIRequestHandler]] = None, + passthrough_errors: bool = False, + ssl_context: t.Optional[_TSSLContextArg] = None, + fd: t.Optional[int] = None, + ) -> None: + if handler is None: + handler = WSGIRequestHandler + + self.address_family = select_address_family(host, port) + + if fd is not None: + real_sock = socket.fromfd(fd, self.address_family, socket.SOCK_STREAM) + port = 0 + + server_address = get_sockaddr(host, int(port), self.address_family) + + # remove socket file if it already exists + if self.address_family == af_unix: + server_address = t.cast(str, server_address) + + if os.path.exists(server_address): + os.unlink(server_address) + + super().__init__(server_address, handler) # type: ignore + + self.app = app + self.passthrough_errors = passthrough_errors + self.shutdown_signal = False + self.host = host + self.port = self.socket.getsockname()[1] + + # Patch in the original socket. + if fd is not None: + self.socket.close() + self.socket = real_sock + self.server_address = self.socket.getsockname() + + if ssl_context is not None: + if isinstance(ssl_context, tuple): + ssl_context = load_ssl_context(*ssl_context) + if ssl_context == "adhoc": + ssl_context = generate_adhoc_ssl_context() + + self.socket = ssl_context.wrap_socket(self.socket, server_side=True) + self.ssl_context: t.Optional["ssl.SSLContext"] = ssl_context + else: + self.ssl_context = None + + def log(self, type: str, message: str, *args: t.Any) -> None: + _log(type, message, *args) + + def serve_forever(self, poll_interval: float = 0.5) -> None: + self.shutdown_signal = False + try: + super().serve_forever(poll_interval=poll_interval) + except KeyboardInterrupt: + pass + finally: + self.server_close() + + def handle_error( + self, request: t.Any, client_address: t.Union[t.Tuple[str, int], str] + ) -> None: + if self.passthrough_errors: + raise + + return super().handle_error(request, client_address) + + +class ThreadedWSGIServer(socketserver.ThreadingMixIn, BaseWSGIServer): + + """A WSGI server that does threading.""" + + multithread = True + daemon_threads = True + + +class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer): + + """A WSGI server that does forking.""" + + multiprocess = True + + def __init__( + self, + host: str, + port: int, + app: "WSGIApplication", + processes: int = 40, + handler: t.Optional[t.Type[WSGIRequestHandler]] = None, + passthrough_errors: bool = False, + ssl_context: t.Optional[_TSSLContextArg] = None, + fd: t.Optional[int] = None, + ) -> None: + if not can_fork: + raise ValueError("Your platform does not support forking.") + BaseWSGIServer.__init__( + self, host, port, app, handler, passthrough_errors, ssl_context, fd + ) + self.max_children = processes + + +def make_server( + host: str, + port: int, + app: "WSGIApplication", + threaded: bool = False, + processes: int = 1, + request_handler: t.Optional[t.Type[WSGIRequestHandler]] = None, + passthrough_errors: bool = False, + ssl_context: t.Optional[_TSSLContextArg] = None, + fd: t.Optional[int] = None, +) -> BaseWSGIServer: + """Create a new server instance that is either threaded, or forks + or just processes one request after another. + """ + if threaded and processes > 1: + raise ValueError("cannot have a multithreaded and multi process server.") + elif threaded: + return ThreadedWSGIServer( + host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd + ) + elif processes > 1: + return ForkingWSGIServer( + host, + port, + app, + processes, + request_handler, + passthrough_errors, + ssl_context, + fd=fd, + ) + else: + return BaseWSGIServer( + host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd + ) + + +def is_running_from_reloader() -> bool: + """Checks if the application is running from within the Werkzeug + reloader subprocess. + + .. versionadded:: 0.10 + """ + return os.environ.get("WERKZEUG_RUN_MAIN") == "true" + + +def run_simple( + hostname: str, + port: int, + application: "WSGIApplication", + use_reloader: bool = False, + use_debugger: bool = False, + use_evalex: bool = True, + extra_files: t.Optional[t.Iterable[str]] = None, + exclude_patterns: t.Optional[t.Iterable[str]] = None, + reloader_interval: int = 1, + reloader_type: str = "auto", + threaded: bool = False, + processes: int = 1, + request_handler: t.Optional[t.Type[WSGIRequestHandler]] = None, + static_files: t.Optional[t.Dict[str, t.Union[str, t.Tuple[str, str]]]] = None, + passthrough_errors: bool = False, + ssl_context: t.Optional[_TSSLContextArg] = None, +) -> None: + """Start a WSGI application. Optional features include a reloader, + multithreading and fork support. + + This function has a command-line interface too:: + + python -m werkzeug.serving --help + + .. versionchanged:: 2.0 + Added ``exclude_patterns`` parameter. + + .. versionadded:: 0.5 + `static_files` was added to simplify serving of static files as well + as `passthrough_errors`. + + .. versionadded:: 0.6 + support for SSL was added. + + .. versionadded:: 0.8 + Added support for automatically loading a SSL context from certificate + file and private key. + + .. versionadded:: 0.9 + Added command-line interface. + + .. versionadded:: 0.10 + Improved the reloader and added support for changing the backend + through the `reloader_type` parameter. See :ref:`reloader` + for more information. + + .. versionchanged:: 0.15 + Bind to a Unix socket by passing a path that starts with + ``unix://`` as the ``hostname``. + + :param hostname: The host to bind to, for example ``'localhost'``. + If the value is a path that starts with ``unix://`` it will bind + to a Unix socket instead of a TCP socket.. + :param port: The port for the server. eg: ``8080`` + :param application: the WSGI application to execute + :param use_reloader: should the server automatically restart the python + process if modules were changed? + :param use_debugger: should the werkzeug debugging system be used? + :param use_evalex: should the exception evaluation feature be enabled? + :param extra_files: a list of files the reloader should watch + additionally to the modules. For example configuration + files. + :param exclude_patterns: List of :mod:`fnmatch` patterns to ignore + when running the reloader. For example, ignore cache files that + shouldn't reload when updated. + :param reloader_interval: the interval for the reloader in seconds. + :param reloader_type: the type of reloader to use. The default is + auto detection. Valid values are ``'stat'`` and + ``'watchdog'``. See :ref:`reloader` for more + information. + :param threaded: should the process handle each request in a separate + thread? + :param processes: if greater than 1 then handle each request in a new process + up to this maximum number of concurrent processes. + :param request_handler: optional parameter that can be used to replace + the default one. You can use this to replace it + with a different + :class:`~BaseHTTPServer.BaseHTTPRequestHandler` + subclass. + :param static_files: a list or dict of paths for static files. This works + exactly like :class:`SharedDataMiddleware`, it's actually + just wrapping the application in that middleware before + serving. + :param passthrough_errors: set this to `True` to disable the error catching. + This means that the server will die on errors but + it can be useful to hook debuggers in (pdb etc.) + :param ssl_context: an SSL context for the connection. Either an + :class:`ssl.SSLContext`, a tuple in the form + ``(cert_file, pkey_file)``, the string ``'adhoc'`` if + the server should automatically create one, or ``None`` + to disable SSL (which is the default). + """ + if not isinstance(port, int): + raise TypeError("port must be an integer") + if use_debugger: + from .debug import DebuggedApplication + + application = DebuggedApplication(application, use_evalex) + if static_files: + from .middleware.shared_data import SharedDataMiddleware + + application = SharedDataMiddleware(application, static_files) + + def log_startup(sock: socket.socket) -> None: + all_addresses_message = ( + " * Running on all addresses.\n" + " WARNING: This is a development server. Do not use it in" + " a production deployment." + ) + + if sock.family == af_unix: + _log("info", " * Running on %s (Press CTRL+C to quit)", hostname) + else: + if hostname == "0.0.0.0": + _log("warning", all_addresses_message) + display_hostname = get_interface_ip(socket.AF_INET) + elif hostname == "::": + _log("warning", all_addresses_message) + display_hostname = get_interface_ip(socket.AF_INET6) + else: + display_hostname = hostname + + if ":" in display_hostname: + display_hostname = f"[{display_hostname}]" + + _log( + "info", + " * Running on %s://%s:%d/ (Press CTRL+C to quit)", + "http" if ssl_context is None else "https", + display_hostname, + sock.getsockname()[1], + ) + + def inner() -> None: + try: + fd: t.Optional[int] = int(os.environ["WERKZEUG_SERVER_FD"]) + except (LookupError, ValueError): + fd = None + srv = make_server( + hostname, + port, + application, + threaded, + processes, + request_handler, + passthrough_errors, + ssl_context, + fd=fd, + ) + if fd is None: + log_startup(srv.socket) + srv.serve_forever() + + if use_reloader: + # If we're not running already in the subprocess that is the + # reloader we want to open up a socket early to make sure the + # port is actually available. + if not is_running_from_reloader(): + if port == 0 and not can_open_by_fd: + raise ValueError( + "Cannot bind to a random port with enabled " + "reloader if the Python interpreter does " + "not support socket opening by fd." + ) + + # Create and destroy a socket so that any exceptions are + # raised before we spawn a separate Python interpreter and + # lose this ability. + address_family = select_address_family(hostname, port) + server_address = get_sockaddr(hostname, port, address_family) + s = socket.socket(address_family, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(server_address) + s.set_inheritable(True) + + # If we can open the socket by file descriptor, then we can just + # reuse this one and our socket will survive the restarts. + if can_open_by_fd: + os.environ["WERKZEUG_SERVER_FD"] = str(s.fileno()) + s.listen(LISTEN_QUEUE) + log_startup(s) + else: + s.close() + if address_family == af_unix: + server_address = t.cast(str, server_address) + _log("info", "Unlinking %s", server_address) + os.unlink(server_address) + + from ._reloader import run_with_reloader as _rwr + + _rwr( + inner, + extra_files=extra_files, + exclude_patterns=exclude_patterns, + interval=reloader_interval, + reloader_type=reloader_type, + ) + else: + inner() + + +def run_with_reloader(*args: t.Any, **kwargs: t.Any) -> None: + """Run a process with the reloader. This is not a public API, do + not use this function. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. + """ + from ._reloader import run_with_reloader as _rwr + + warnings.warn( + ( + "'run_with_reloader' is a private API, it will no longer be" + " accessible in Werkzeug 2.1. Use 'run_simple' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + _rwr(*args, **kwargs) + + +def main() -> None: + """A simple command-line interface for :py:func:`run_simple`.""" + import argparse + from .utils import import_string + + _log("warning", "This CLI is deprecated and will be removed in version 2.1.") + + parser = argparse.ArgumentParser( + description="Run the given WSGI application with the development server.", + allow_abbrev=False, + ) + parser.add_argument( + "-b", + "--bind", + dest="address", + help="The hostname:port the app should listen on.", + ) + parser.add_argument( + "-d", + "--debug", + action="store_true", + help="Show the interactive debugger for unhandled exceptions.", + ) + parser.add_argument( + "-r", + "--reload", + action="store_true", + help="Reload the process if modules change.", + ) + parser.add_argument( + "application", help="Application to import and serve, in the form module:app." + ) + args = parser.parse_args() + hostname, port = None, None + + if args.address: + hostname, _, port = args.address.partition(":") + + run_simple( + hostname=hostname or "127.0.0.1", + port=int(port or 5000), + application=import_string(args.application), + use_reloader=args.reload, + use_debugger=args.debug, + ) + + +if __name__ == "__main__": + main() diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/test.py b/myvenv/lib/python3.10/site-packages/werkzeug/test.py new file mode 100644 index 0000000..448dbbd --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/test.py @@ -0,0 +1,1331 @@ +import mimetypes +import sys +import typing as t +import warnings +from collections import defaultdict +from datetime import datetime +from datetime import timedelta +from http.cookiejar import CookieJar +from io import BytesIO +from itertools import chain +from random import random +from tempfile import TemporaryFile +from time import time +from urllib.request import Request as _UrllibRequest + +from ._internal import _get_environ +from ._internal import _make_encode_wrapper +from ._internal import _wsgi_decoding_dance +from ._internal import _wsgi_encoding_dance +from .datastructures import Authorization +from .datastructures import CallbackDict +from .datastructures import CombinedMultiDict +from .datastructures import EnvironHeaders +from .datastructures import FileMultiDict +from .datastructures import Headers +from .datastructures import MultiDict +from .http import dump_cookie +from .http import dump_options_header +from .http import parse_options_header +from .sansio.multipart import Data +from .sansio.multipart import Epilogue +from .sansio.multipart import Field +from .sansio.multipart import File +from .sansio.multipart import MultipartEncoder +from .sansio.multipart import Preamble +from .urls import iri_to_uri +from .urls import url_encode +from .urls import url_fix +from .urls import url_parse +from .urls import url_unparse +from .urls import url_unquote +from .utils import get_content_type +from .wrappers.request import Request +from .wrappers.response import Response +from .wsgi import ClosingIterator +from .wsgi import get_current_url + +if t.TYPE_CHECKING: + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +def stream_encode_multipart( + data: t.Mapping[str, t.Any], + use_tempfile: bool = True, + threshold: int = 1024 * 500, + boundary: t.Optional[str] = None, + charset: str = "utf-8", +) -> t.Tuple[t.IO[bytes], int, str]: + """Encode a dict of values (either strings or file descriptors or + :class:`FileStorage` objects.) into a multipart encoded string stored + in a file descriptor. + """ + if boundary is None: + boundary = f"---------------WerkzeugFormPart_{time()}{random()}" + + stream: t.IO[bytes] = BytesIO() + total_length = 0 + on_disk = False + + if use_tempfile: + + def write_binary(s: bytes) -> int: + nonlocal stream, total_length, on_disk + + if on_disk: + return stream.write(s) + else: + length = len(s) + + if length + total_length <= threshold: + stream.write(s) + else: + new_stream = t.cast(t.IO[bytes], TemporaryFile("wb+")) + new_stream.write(stream.getvalue()) # type: ignore + new_stream.write(s) + stream = new_stream + on_disk = True + + total_length += length + return length + + else: + write_binary = stream.write + + encoder = MultipartEncoder(boundary.encode()) + write_binary(encoder.send_event(Preamble(data=b""))) + for key, value in _iter_data(data): + reader = getattr(value, "read", None) + if reader is not None: + filename = getattr(value, "filename", getattr(value, "name", None)) + content_type = getattr(value, "content_type", None) + if content_type is None: + content_type = ( + filename + and mimetypes.guess_type(filename)[0] + or "application/octet-stream" + ) + headers = Headers([("Content-Type", content_type)]) + if filename is None: + write_binary(encoder.send_event(Field(name=key, headers=headers))) + else: + write_binary( + encoder.send_event( + File(name=key, filename=filename, headers=headers) + ) + ) + while True: + chunk = reader(16384) + + if not chunk: + break + + write_binary(encoder.send_event(Data(data=chunk, more_data=True))) + else: + if not isinstance(value, str): + value = str(value) + write_binary(encoder.send_event(Field(name=key, headers=Headers()))) + write_binary( + encoder.send_event(Data(data=value.encode(charset), more_data=False)) + ) + + write_binary(encoder.send_event(Epilogue(data=b""))) + + length = stream.tell() + stream.seek(0) + return stream, length, boundary + + +def encode_multipart( + values: t.Mapping[str, t.Any], + boundary: t.Optional[str] = None, + charset: str = "utf-8", +) -> t.Tuple[str, bytes]: + """Like `stream_encode_multipart` but returns a tuple in the form + (``boundary``, ``data``) where data is bytes. + """ + stream, length, boundary = stream_encode_multipart( + values, use_tempfile=False, boundary=boundary, charset=charset + ) + return boundary, stream.read() + + +class _TestCookieHeaders: + """A headers adapter for cookielib""" + + def __init__(self, headers: t.Union[Headers, t.List[t.Tuple[str, str]]]) -> None: + self.headers = headers + + def getheaders(self, name: str) -> t.Iterable[str]: + headers = [] + name = name.lower() + for k, v in self.headers: + if k.lower() == name: + headers.append(v) + return headers + + def get_all( + self, name: str, default: t.Optional[t.Iterable[str]] = None + ) -> t.Iterable[str]: + headers = self.getheaders(name) + + if not headers: + return default # type: ignore + + return headers + + +class _TestCookieResponse: + """Something that looks like a httplib.HTTPResponse, but is actually just an + adapter for our test responses to make them available for cookielib. + """ + + def __init__(self, headers: t.Union[Headers, t.List[t.Tuple[str, str]]]) -> None: + self.headers = _TestCookieHeaders(headers) + + def info(self) -> _TestCookieHeaders: + return self.headers + + +class _TestCookieJar(CookieJar): + """A cookielib.CookieJar modified to inject and read cookie headers from + and to wsgi environments, and wsgi application responses. + """ + + def inject_wsgi(self, environ: "WSGIEnvironment") -> None: + """Inject the cookies as client headers into the server's wsgi + environment. + """ + cvals = [f"{c.name}={c.value}" for c in self] + + if cvals: + environ["HTTP_COOKIE"] = "; ".join(cvals) + else: + environ.pop("HTTP_COOKIE", None) + + def extract_wsgi( + self, + environ: "WSGIEnvironment", + headers: t.Union[Headers, t.List[t.Tuple[str, str]]], + ) -> None: + """Extract the server's set-cookie headers as cookies into the + cookie jar. + """ + self.extract_cookies( + _TestCookieResponse(headers), # type: ignore + _UrllibRequest(get_current_url(environ)), + ) + + +def _iter_data(data: t.Mapping[str, t.Any]) -> t.Iterator[t.Tuple[str, t.Any]]: + """Iterate over a mapping that might have a list of values, yielding + all key, value pairs. Almost like iter_multi_items but only allows + lists, not tuples, of values so tuples can be used for files. + """ + if isinstance(data, MultiDict): + yield from data.items(multi=True) + else: + for key, value in data.items(): + if isinstance(value, list): + for v in value: + yield key, v + else: + yield key, value + + +_TAnyMultiDict = t.TypeVar("_TAnyMultiDict", bound=MultiDict) + + +class EnvironBuilder: + """This class can be used to conveniently create a WSGI environment + for testing purposes. It can be used to quickly create WSGI environments + or request objects from arbitrary data. + + The signature of this class is also used in some other places as of + Werkzeug 0.5 (:func:`create_environ`, :meth:`Response.from_values`, + :meth:`Client.open`). Because of this most of the functionality is + available through the constructor alone. + + Files and regular form data can be manipulated independently of each + other with the :attr:`form` and :attr:`files` attributes, but are + passed with the same argument to the constructor: `data`. + + `data` can be any of these values: + + - a `str` or `bytes` object: The object is converted into an + :attr:`input_stream`, the :attr:`content_length` is set and you have to + provide a :attr:`content_type`. + - a `dict` or :class:`MultiDict`: The keys have to be strings. The values + have to be either any of the following objects, or a list of any of the + following objects: + + - a :class:`file`-like object: These are converted into + :class:`FileStorage` objects automatically. + - a `tuple`: The :meth:`~FileMultiDict.add_file` method is called + with the key and the unpacked `tuple` items as positional + arguments. + - a `str`: The string is set as form data for the associated key. + - a file-like object: The object content is loaded in memory and then + handled like a regular `str` or a `bytes`. + + :param path: the path of the request. In the WSGI environment this will + end up as `PATH_INFO`. If the `query_string` is not defined + and there is a question mark in the `path` everything after + it is used as query string. + :param base_url: the base URL is a URL that is used to extract the WSGI + URL scheme, host (server name + server port) and the + script root (`SCRIPT_NAME`). + :param query_string: an optional string or dict with URL parameters. + :param method: the HTTP method to use, defaults to `GET`. + :param input_stream: an optional input stream. Do not specify this and + `data`. As soon as an input stream is set you can't + modify :attr:`args` and :attr:`files` unless you + set the :attr:`input_stream` to `None` again. + :param content_type: The content type for the request. As of 0.5 you + don't have to provide this when specifying files + and form data via `data`. + :param content_length: The content length for the request. You don't + have to specify this when providing data via + `data`. + :param errors_stream: an optional error stream that is used for + `wsgi.errors`. Defaults to :data:`stderr`. + :param multithread: controls `wsgi.multithread`. Defaults to `False`. + :param multiprocess: controls `wsgi.multiprocess`. Defaults to `False`. + :param run_once: controls `wsgi.run_once`. Defaults to `False`. + :param headers: an optional list or :class:`Headers` object of headers. + :param data: a string or dict of form data or a file-object. + See explanation above. + :param json: An object to be serialized and assigned to ``data``. + Defaults the content type to ``"application/json"``. + Serialized with the function assigned to :attr:`json_dumps`. + :param environ_base: an optional dict of environment defaults. + :param environ_overrides: an optional dict of environment overrides. + :param charset: the charset used to encode string data. + :param auth: An authorization object to use for the + ``Authorization`` header value. A ``(username, password)`` tuple + is a shortcut for ``Basic`` authorization. + + .. versionchanged:: 2.0 + ``REQUEST_URI`` and ``RAW_URI`` is the full raw URI including + the query string, not only the path. + + .. versionchanged:: 2.0 + The default :attr:`request_class` is ``Request`` instead of + ``BaseRequest``. + + .. versionadded:: 2.0 + Added the ``auth`` parameter. + + .. versionadded:: 0.15 + The ``json`` param and :meth:`json_dumps` method. + + .. versionadded:: 0.15 + The environ has keys ``REQUEST_URI`` and ``RAW_URI`` containing + the path before perecent-decoding. This is not part of the WSGI + PEP, but many WSGI servers include it. + + .. versionchanged:: 0.6 + ``path`` and ``base_url`` can now be unicode strings that are + encoded with :func:`iri_to_uri`. + """ + + #: the server protocol to use. defaults to HTTP/1.1 + server_protocol = "HTTP/1.1" + + #: the wsgi version to use. defaults to (1, 0) + wsgi_version = (1, 0) + + #: The default request class used by :meth:`get_request`. + request_class = Request + + import json + + #: The serialization function used when ``json`` is passed. + json_dumps = staticmethod(json.dumps) + del json + + _args: t.Optional[MultiDict] + _query_string: t.Optional[str] + _input_stream: t.Optional[t.IO[bytes]] + _form: t.Optional[MultiDict] + _files: t.Optional[FileMultiDict] + + def __init__( + self, + path: str = "/", + base_url: t.Optional[str] = None, + query_string: t.Optional[t.Union[t.Mapping[str, str], str]] = None, + method: str = "GET", + input_stream: t.Optional[t.IO[bytes]] = None, + content_type: t.Optional[str] = None, + content_length: t.Optional[int] = None, + errors_stream: t.Optional[t.IO[str]] = None, + multithread: bool = False, + multiprocess: bool = False, + run_once: bool = False, + headers: t.Optional[t.Union[Headers, t.Iterable[t.Tuple[str, str]]]] = None, + data: t.Optional[ + t.Union[t.IO[bytes], str, bytes, t.Mapping[str, t.Any]] + ] = None, + environ_base: t.Optional[t.Mapping[str, t.Any]] = None, + environ_overrides: t.Optional[t.Mapping[str, t.Any]] = None, + charset: str = "utf-8", + mimetype: t.Optional[str] = None, + json: t.Optional[t.Mapping[str, t.Any]] = None, + auth: t.Optional[t.Union[Authorization, t.Tuple[str, str]]] = None, + ) -> None: + path_s = _make_encode_wrapper(path) + if query_string is not None and path_s("?") in path: + raise ValueError("Query string is defined in the path and as an argument") + request_uri = url_parse(path) + if query_string is None and path_s("?") in path: + query_string = request_uri.query + self.charset = charset + self.path = iri_to_uri(request_uri.path) + self.request_uri = path + if base_url is not None: + base_url = url_fix(iri_to_uri(base_url, charset), charset) + self.base_url = base_url # type: ignore + if isinstance(query_string, (bytes, str)): + self.query_string = query_string + else: + if query_string is None: + query_string = MultiDict() + elif not isinstance(query_string, MultiDict): + query_string = MultiDict(query_string) + self.args = query_string + self.method = method + if headers is None: + headers = Headers() + elif not isinstance(headers, Headers): + headers = Headers(headers) + self.headers = headers + if content_type is not None: + self.content_type = content_type + if errors_stream is None: + errors_stream = sys.stderr + self.errors_stream = errors_stream + self.multithread = multithread + self.multiprocess = multiprocess + self.run_once = run_once + self.environ_base = environ_base + self.environ_overrides = environ_overrides + self.input_stream = input_stream + self.content_length = content_length + self.closed = False + + if auth is not None: + if isinstance(auth, tuple): + auth = Authorization( + "basic", {"username": auth[0], "password": auth[1]} + ) + + self.headers.set("Authorization", auth.to_header()) + + if json is not None: + if data is not None: + raise TypeError("can't provide both json and data") + + data = self.json_dumps(json) + + if self.content_type is None: + self.content_type = "application/json" + + if data: + if input_stream is not None: + raise TypeError("can't provide input stream and data") + if hasattr(data, "read"): + data = data.read() # type: ignore + if isinstance(data, str): + data = data.encode(self.charset) + if isinstance(data, bytes): + self.input_stream = BytesIO(data) + if self.content_length is None: + self.content_length = len(data) + else: + for key, value in _iter_data(data): # type: ignore + if isinstance(value, (tuple, dict)) or hasattr(value, "read"): + self._add_file_from_data(key, value) + else: + self.form.setlistdefault(key).append(value) + + if mimetype is not None: + self.mimetype = mimetype + + @classmethod + def from_environ( + cls, environ: "WSGIEnvironment", **kwargs: t.Any + ) -> "EnvironBuilder": + """Turn an environ dict back into a builder. Any extra kwargs + override the args extracted from the environ. + + .. versionchanged:: 2.0 + Path and query values are passed through the WSGI decoding + dance to avoid double encoding. + + .. versionadded:: 0.15 + """ + headers = Headers(EnvironHeaders(environ)) + out = { + "path": _wsgi_decoding_dance(environ["PATH_INFO"]), + "base_url": cls._make_base_url( + environ["wsgi.url_scheme"], + headers.pop("Host"), + _wsgi_decoding_dance(environ["SCRIPT_NAME"]), + ), + "query_string": _wsgi_decoding_dance(environ["QUERY_STRING"]), + "method": environ["REQUEST_METHOD"], + "input_stream": environ["wsgi.input"], + "content_type": headers.pop("Content-Type", None), + "content_length": headers.pop("Content-Length", None), + "errors_stream": environ["wsgi.errors"], + "multithread": environ["wsgi.multithread"], + "multiprocess": environ["wsgi.multiprocess"], + "run_once": environ["wsgi.run_once"], + "headers": headers, + } + out.update(kwargs) + return cls(**out) + + def _add_file_from_data( + self, + key: str, + value: t.Union[ + t.IO[bytes], t.Tuple[t.IO[bytes], str], t.Tuple[t.IO[bytes], str, str] + ], + ) -> None: + """Called in the EnvironBuilder to add files from the data dict.""" + if isinstance(value, tuple): + self.files.add_file(key, *value) + else: + self.files.add_file(key, value) + + @staticmethod + def _make_base_url(scheme: str, host: str, script_root: str) -> str: + return url_unparse((scheme, host, script_root, "", "")).rstrip("/") + "/" + + @property + def base_url(self) -> str: + """The base URL is used to extract the URL scheme, host name, + port, and root path. + """ + return self._make_base_url(self.url_scheme, self.host, self.script_root) + + @base_url.setter + def base_url(self, value: t.Optional[str]) -> None: + if value is None: + scheme = "http" + netloc = "localhost" + script_root = "" + else: + scheme, netloc, script_root, qs, anchor = url_parse(value) + if qs or anchor: + raise ValueError("base url must not contain a query string or fragment") + self.script_root = script_root.rstrip("/") + self.host = netloc + self.url_scheme = scheme + + @property + def content_type(self) -> t.Optional[str]: + """The content type for the request. Reflected from and to + the :attr:`headers`. Do not set if you set :attr:`files` or + :attr:`form` for auto detection. + """ + ct = self.headers.get("Content-Type") + if ct is None and not self._input_stream: + if self._files: + return "multipart/form-data" + if self._form: + return "application/x-www-form-urlencoded" + return None + return ct + + @content_type.setter + def content_type(self, value: t.Optional[str]) -> None: + if value is None: + self.headers.pop("Content-Type", None) + else: + self.headers["Content-Type"] = value + + @property + def mimetype(self) -> t.Optional[str]: + """The mimetype (content type without charset etc.) + + .. versionadded:: 0.14 + """ + ct = self.content_type + return ct.split(";")[0].strip() if ct else None + + @mimetype.setter + def mimetype(self, value: str) -> None: + self.content_type = get_content_type(value, self.charset) + + @property + def mimetype_params(self) -> t.Mapping[str, str]: + """The mimetype parameters as dict. For example if the + content type is ``text/html; charset=utf-8`` the params would be + ``{'charset': 'utf-8'}``. + + .. versionadded:: 0.14 + """ + + def on_update(d: CallbackDict) -> None: + self.headers["Content-Type"] = dump_options_header(self.mimetype, d) + + d = parse_options_header(self.headers.get("content-type", ""))[1] + return CallbackDict(d, on_update) + + @property + def content_length(self) -> t.Optional[int]: + """The content length as integer. Reflected from and to the + :attr:`headers`. Do not set if you set :attr:`files` or + :attr:`form` for auto detection. + """ + return self.headers.get("Content-Length", type=int) + + @content_length.setter + def content_length(self, value: t.Optional[int]) -> None: + if value is None: + self.headers.pop("Content-Length", None) + else: + self.headers["Content-Length"] = str(value) + + def _get_form(self, name: str, storage: t.Type[_TAnyMultiDict]) -> _TAnyMultiDict: + """Common behavior for getting the :attr:`form` and + :attr:`files` properties. + + :param name: Name of the internal cached attribute. + :param storage: Storage class used for the data. + """ + if self.input_stream is not None: + raise AttributeError("an input stream is defined") + + rv = getattr(self, name) + + if rv is None: + rv = storage() + setattr(self, name, rv) + + return rv # type: ignore + + def _set_form(self, name: str, value: MultiDict) -> None: + """Common behavior for setting the :attr:`form` and + :attr:`files` properties. + + :param name: Name of the internal cached attribute. + :param value: Value to assign to the attribute. + """ + self._input_stream = None + setattr(self, name, value) + + @property + def form(self) -> MultiDict: + """A :class:`MultiDict` of form values.""" + return self._get_form("_form", MultiDict) + + @form.setter + def form(self, value: MultiDict) -> None: + self._set_form("_form", value) + + @property + def files(self) -> FileMultiDict: + """A :class:`FileMultiDict` of uploaded files. Use + :meth:`~FileMultiDict.add_file` to add new files. + """ + return self._get_form("_files", FileMultiDict) + + @files.setter + def files(self, value: FileMultiDict) -> None: + self._set_form("_files", value) + + @property + def input_stream(self) -> t.Optional[t.IO[bytes]]: + """An optional input stream. This is mutually exclusive with + setting :attr:`form` and :attr:`files`, setting it will clear + those. Do not provide this if the method is not ``POST`` or + another method that has a body. + """ + return self._input_stream + + @input_stream.setter + def input_stream(self, value: t.Optional[t.IO[bytes]]) -> None: + self._input_stream = value + self._form = None + self._files = None + + @property + def query_string(self) -> str: + """The query string. If you set this to a string + :attr:`args` will no longer be available. + """ + if self._query_string is None: + if self._args is not None: + return url_encode(self._args, charset=self.charset) + return "" + return self._query_string + + @query_string.setter + def query_string(self, value: t.Optional[str]) -> None: + self._query_string = value + self._args = None + + @property + def args(self) -> MultiDict: + """The URL arguments as :class:`MultiDict`.""" + if self._query_string is not None: + raise AttributeError("a query string is defined") + if self._args is None: + self._args = MultiDict() + return self._args + + @args.setter + def args(self, value: t.Optional[MultiDict]) -> None: + self._query_string = None + self._args = value + + @property + def server_name(self) -> str: + """The server name (read-only, use :attr:`host` to set)""" + return self.host.split(":", 1)[0] + + @property + def server_port(self) -> int: + """The server port as integer (read-only, use :attr:`host` to set)""" + pieces = self.host.split(":", 1) + if len(pieces) == 2 and pieces[1].isdigit(): + return int(pieces[1]) + if self.url_scheme == "https": + return 443 + return 80 + + def __del__(self) -> None: + try: + self.close() + except Exception: + pass + + def close(self) -> None: + """Closes all files. If you put real :class:`file` objects into the + :attr:`files` dict you can call this method to automatically close + them all in one go. + """ + if self.closed: + return + try: + files = self.files.values() + except AttributeError: + files = () # type: ignore + for f in files: + try: + f.close() + except Exception: + pass + self.closed = True + + def get_environ(self) -> "WSGIEnvironment": + """Return the built environ. + + .. versionchanged:: 0.15 + The content type and length headers are set based on + input stream detection. Previously this only set the WSGI + keys. + """ + input_stream = self.input_stream + content_length = self.content_length + + mimetype = self.mimetype + content_type = self.content_type + + if input_stream is not None: + start_pos = input_stream.tell() + input_stream.seek(0, 2) + end_pos = input_stream.tell() + input_stream.seek(start_pos) + content_length = end_pos - start_pos + elif mimetype == "multipart/form-data": + input_stream, content_length, boundary = stream_encode_multipart( + CombinedMultiDict([self.form, self.files]), charset=self.charset + ) + content_type = f'{mimetype}; boundary="{boundary}"' + elif mimetype == "application/x-www-form-urlencoded": + form_encoded = url_encode(self.form, charset=self.charset).encode("ascii") + content_length = len(form_encoded) + input_stream = BytesIO(form_encoded) + else: + input_stream = BytesIO() + + result: "WSGIEnvironment" = {} + if self.environ_base: + result.update(self.environ_base) + + def _path_encode(x: str) -> str: + return _wsgi_encoding_dance(url_unquote(x, self.charset), self.charset) + + raw_uri = _wsgi_encoding_dance(self.request_uri, self.charset) + result.update( + { + "REQUEST_METHOD": self.method, + "SCRIPT_NAME": _path_encode(self.script_root), + "PATH_INFO": _path_encode(self.path), + "QUERY_STRING": _wsgi_encoding_dance(self.query_string, self.charset), + # Non-standard, added by mod_wsgi, uWSGI + "REQUEST_URI": raw_uri, + # Non-standard, added by gunicorn + "RAW_URI": raw_uri, + "SERVER_NAME": self.server_name, + "SERVER_PORT": str(self.server_port), + "HTTP_HOST": self.host, + "SERVER_PROTOCOL": self.server_protocol, + "wsgi.version": self.wsgi_version, + "wsgi.url_scheme": self.url_scheme, + "wsgi.input": input_stream, + "wsgi.errors": self.errors_stream, + "wsgi.multithread": self.multithread, + "wsgi.multiprocess": self.multiprocess, + "wsgi.run_once": self.run_once, + } + ) + + headers = self.headers.copy() + + if content_type is not None: + result["CONTENT_TYPE"] = content_type + headers.set("Content-Type", content_type) + + if content_length is not None: + result["CONTENT_LENGTH"] = str(content_length) + headers.set("Content-Length", content_length) + + combined_headers = defaultdict(list) + + for key, value in headers.to_wsgi_list(): + combined_headers[f"HTTP_{key.upper().replace('-', '_')}"].append(value) + + for key, values in combined_headers.items(): + result[key] = ", ".join(values) + + if self.environ_overrides: + result.update(self.environ_overrides) + + return result + + def get_request(self, cls: t.Optional[t.Type[Request]] = None) -> Request: + """Returns a request with the data. If the request class is not + specified :attr:`request_class` is used. + + :param cls: The request wrapper to use. + """ + if cls is None: + cls = self.request_class + + return cls(self.get_environ()) + + +class ClientRedirectError(Exception): + """If a redirect loop is detected when using follow_redirects=True with + the :cls:`Client`, then this exception is raised. + """ + + +class Client: + """This class allows you to send requests to a wrapped application. + + The use_cookies parameter indicates whether cookies should be stored and + sent for subsequent requests. This is True by default, but passing False + will disable this behaviour. + + If you want to request some subdomain of your application you may set + `allow_subdomain_redirects` to `True` as if not no external redirects + are allowed. + + .. versionchanged:: 2.0 + ``response_wrapper`` is always a subclass of + :class:``TestResponse``. + + .. versionchanged:: 0.5 + Added the ``use_cookies`` parameter. + """ + + def __init__( + self, + application: "WSGIApplication", + response_wrapper: t.Optional[t.Type["Response"]] = None, + use_cookies: bool = True, + allow_subdomain_redirects: bool = False, + ) -> None: + self.application = application + + if response_wrapper in {None, Response}: + response_wrapper = TestResponse + elif not isinstance(response_wrapper, TestResponse): + response_wrapper = type( + "WrapperTestResponse", + (TestResponse, response_wrapper), # type: ignore + {}, + ) + + self.response_wrapper = t.cast(t.Type["TestResponse"], response_wrapper) + + if use_cookies: + self.cookie_jar: t.Optional[_TestCookieJar] = _TestCookieJar() + else: + self.cookie_jar = None + + self.allow_subdomain_redirects = allow_subdomain_redirects + + def set_cookie( + self, + server_name: str, + key: str, + value: str = "", + max_age: t.Optional[t.Union[timedelta, int]] = None, + expires: t.Optional[t.Union[str, datetime, int, float]] = None, + path: str = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: t.Optional[str] = None, + charset: str = "utf-8", + ) -> None: + """Sets a cookie in the client's cookie jar. The server name + is required and has to match the one that is also passed to + the open call. + """ + assert self.cookie_jar is not None, "cookies disabled" + header = dump_cookie( + key, + value, + max_age, + expires, + path, + domain, + secure, + httponly, + charset, + samesite=samesite, + ) + environ = create_environ(path, base_url=f"http://{server_name}") + headers = [("Set-Cookie", header)] + self.cookie_jar.extract_wsgi(environ, headers) + + def delete_cookie( + self, + server_name: str, + key: str, + path: str = "/", + domain: t.Optional[str] = None, + secure: bool = False, + httponly: bool = False, + samesite: t.Optional[str] = None, + ) -> None: + """Deletes a cookie in the test client.""" + self.set_cookie( + server_name, + key, + expires=0, + max_age=0, + path=path, + domain=domain, + secure=secure, + httponly=httponly, + samesite=samesite, + ) + + def run_wsgi_app( + self, environ: "WSGIEnvironment", buffered: bool = False + ) -> t.Tuple[t.Iterable[bytes], str, Headers]: + """Runs the wrapped WSGI app with the given environment. + + :meta private: + """ + if self.cookie_jar is not None: + self.cookie_jar.inject_wsgi(environ) + + rv = run_wsgi_app(self.application, environ, buffered=buffered) + + if self.cookie_jar is not None: + self.cookie_jar.extract_wsgi(environ, rv[2]) + + return rv + + def resolve_redirect( + self, response: "TestResponse", buffered: bool = False + ) -> "TestResponse": + """Perform a new request to the location given by the redirect + response to the previous request. + + :meta private: + """ + scheme, netloc, path, qs, anchor = url_parse(response.location) + builder = EnvironBuilder.from_environ( + response.request.environ, path=path, query_string=qs + ) + + to_name_parts = netloc.split(":", 1)[0].split(".") + from_name_parts = builder.server_name.split(".") + + if to_name_parts != [""]: + # The new location has a host, use it for the base URL. + builder.url_scheme = scheme + builder.host = netloc + else: + # A local redirect with autocorrect_location_header=False + # doesn't have a host, so use the request's host. + to_name_parts = from_name_parts + + # Explain why a redirect to a different server name won't be followed. + if to_name_parts != from_name_parts: + if to_name_parts[-len(from_name_parts) :] == from_name_parts: + if not self.allow_subdomain_redirects: + raise RuntimeError("Following subdomain redirects is not enabled.") + else: + raise RuntimeError("Following external redirects is not supported.") + + path_parts = path.split("/") + root_parts = builder.script_root.split("/") + + if path_parts[: len(root_parts)] == root_parts: + # Strip the script root from the path. + builder.path = path[len(builder.script_root) :] + else: + # The new location is not under the script root, so use the + # whole path and clear the previous root. + builder.path = path + builder.script_root = "" + + # Only 307 and 308 preserve all of the original request. + if response.status_code not in {307, 308}: + # HEAD is preserved, everything else becomes GET. + if builder.method != "HEAD": + builder.method = "GET" + + # Clear the body and the headers that describe it. + + if builder.input_stream is not None: + builder.input_stream.close() + builder.input_stream = None + + builder.content_type = None + builder.content_length = None + builder.headers.pop("Transfer-Encoding", None) + + return self.open(builder, buffered=buffered) + + def open( + self, + *args: t.Any, + as_tuple: bool = False, + buffered: bool = False, + follow_redirects: bool = False, + **kwargs: t.Any, + ) -> "TestResponse": + """Generate an environ dict from the given arguments, make a + request to the application using it, and return the response. + + :param args: Passed to :class:`EnvironBuilder` to create the + environ for the request. If a single arg is passed, it can + be an existing :class:`EnvironBuilder` or an environ dict. + :param buffered: Convert the iterator returned by the app into + a list. If the iterator has a ``close()`` method, it is + called automatically. + :param follow_redirects: Make additional requests to follow HTTP + redirects until a non-redirect status is returned. + :attr:`TestResponse.history` lists the intermediate + responses. + + .. versionchanged:: 2.0 + ``as_tuple`` is deprecated and will be removed in Werkzeug + 2.1. Use :attr:`TestResponse.request` and + ``request.environ`` instead. + + .. versionchanged:: 2.0 + The request input stream is closed when calling + ``response.close()``. Input streams for redirects are + automatically closed. + + .. versionchanged:: 0.5 + If a dict is provided as file in the dict for the ``data`` + parameter the content type has to be called ``content_type`` + instead of ``mimetype``. This change was made for + consistency with :class:`werkzeug.FileWrapper`. + + .. versionchanged:: 0.5 + Added the ``follow_redirects`` parameter. + """ + request: t.Optional["Request"] = None + + if not kwargs and len(args) == 1: + arg = args[0] + + if isinstance(arg, EnvironBuilder): + request = arg.get_request() + elif isinstance(arg, dict): + request = EnvironBuilder.from_environ(arg).get_request() + elif isinstance(arg, Request): + request = arg + + if request is None: + builder = EnvironBuilder(*args, **kwargs) + + try: + request = builder.get_request() + finally: + builder.close() + + response = self.run_wsgi_app(request.environ, buffered=buffered) + response = self.response_wrapper(*response, request=request) + + redirects = set() + history: t.List["TestResponse"] = [] + + while follow_redirects and response.status_code in { + 301, + 302, + 303, + 305, + 307, + 308, + }: + # Exhaust intermediate response bodies to ensure middleware + # that returns an iterator runs any cleanup code. + if not buffered: + response.make_sequence() + response.close() + + new_redirect_entry = (response.location, response.status_code) + + if new_redirect_entry in redirects: + raise ClientRedirectError( + f"Loop detected: A {response.status_code} redirect" + f" to {response.location} was already made." + ) + + redirects.add(new_redirect_entry) + response.history = tuple(history) + history.append(response) + response = self.resolve_redirect(response, buffered=buffered) + else: + # This is the final request after redirects, or not + # following redirects. + response.history = tuple(history) + # Close the input stream when closing the response, in case + # the input is an open temporary file. + response.call_on_close(request.input_stream.close) + + if as_tuple: + warnings.warn( + "'as_tuple' is deprecated and will be removed in" + " Werkzeug 2.1. Access 'response.request.environ'" + " instead.", + DeprecationWarning, + stacklevel=2, + ) + return request.environ, response # type: ignore + + return response + + def get(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``GET``.""" + kw["method"] = "GET" + return self.open(*args, **kw) + + def post(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``POST``.""" + kw["method"] = "POST" + return self.open(*args, **kw) + + def put(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``PUT``.""" + kw["method"] = "PUT" + return self.open(*args, **kw) + + def delete(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``DELETE``.""" + kw["method"] = "DELETE" + return self.open(*args, **kw) + + def patch(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``PATCH``.""" + kw["method"] = "PATCH" + return self.open(*args, **kw) + + def options(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``OPTIONS``.""" + kw["method"] = "OPTIONS" + return self.open(*args, **kw) + + def head(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``HEAD``.""" + kw["method"] = "HEAD" + return self.open(*args, **kw) + + def trace(self, *args: t.Any, **kw: t.Any) -> "TestResponse": + """Call :meth:`open` with ``method`` set to ``TRACE``.""" + kw["method"] = "TRACE" + return self.open(*args, **kw) + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.application!r}>" + + +def create_environ(*args: t.Any, **kwargs: t.Any) -> "WSGIEnvironment": + """Create a new WSGI environ dict based on the values passed. The first + parameter should be the path of the request which defaults to '/'. The + second one can either be an absolute path (in that case the host is + localhost:80) or a full path to the request with scheme, netloc port and + the path to the script. + + This accepts the same arguments as the :class:`EnvironBuilder` + constructor. + + .. versionchanged:: 0.5 + This function is now a thin wrapper over :class:`EnvironBuilder` which + was added in 0.5. The `headers`, `environ_base`, `environ_overrides` + and `charset` parameters were added. + """ + builder = EnvironBuilder(*args, **kwargs) + + try: + return builder.get_environ() + finally: + builder.close() + + +def run_wsgi_app( + app: "WSGIApplication", environ: "WSGIEnvironment", buffered: bool = False +) -> t.Tuple[t.Iterable[bytes], str, Headers]: + """Return a tuple in the form (app_iter, status, headers) of the + application output. This works best if you pass it an application that + returns an iterator all the time. + + Sometimes applications may use the `write()` callable returned + by the `start_response` function. This tries to resolve such edge + cases automatically. But if you don't get the expected output you + should set `buffered` to `True` which enforces buffering. + + If passed an invalid WSGI application the behavior of this function is + undefined. Never pass non-conforming WSGI applications to this function. + + :param app: the application to execute. + :param buffered: set to `True` to enforce buffering. + :return: tuple in the form ``(app_iter, status, headers)`` + """ + # Copy environ to ensure any mutations by the app (ProxyFix, for + # example) don't affect subsequent requests (such as redirects). + environ = _get_environ(environ).copy() + status: str + response: t.Optional[t.Tuple[str, t.List[t.Tuple[str, str]]]] = None + buffer: t.List[bytes] = [] + + def start_response(status, headers, exc_info=None): # type: ignore + nonlocal response + + if exc_info: + try: + raise exc_info[1].with_traceback(exc_info[2]) + finally: + exc_info = None + + response = (status, headers) + return buffer.append + + app_rv = app(environ, start_response) + close_func = getattr(app_rv, "close", None) + app_iter: t.Iterable[bytes] = iter(app_rv) + + # when buffering we emit the close call early and convert the + # application iterator into a regular list + if buffered: + try: + app_iter = list(app_iter) + finally: + if close_func is not None: + close_func() + + # otherwise we iterate the application iter until we have a response, chain + # the already received data with the already collected data and wrap it in + # a new `ClosingIterator` if we need to restore a `close` callable from the + # original return value. + else: + for item in app_iter: + buffer.append(item) + + if response is not None: + break + + if buffer: + app_iter = chain(buffer, app_iter) + + if close_func is not None and app_iter is not app_rv: + app_iter = ClosingIterator(app_iter, close_func) + + status, headers = response # type: ignore + return app_iter, status, Headers(headers) + + +class TestResponse(Response): + """:class:`~werkzeug.wrappers.Response` subclass that provides extra + information about requests made with the test :class:`Client`. + + Test client requests will always return an instance of this class. + If a custom response class is passed to the client, it is + subclassed along with this to support test information. + + If the test request included large files, or if the application is + serving a file, call :meth:`close` to close any open files and + prevent Python showing a ``ResourceWarning``. + """ + + request: Request + """A request object with the environ used to make the request that + resulted in this response. + """ + + history: t.Tuple["TestResponse", ...] + """A list of intermediate responses. Populated when the test request + is made with ``follow_redirects`` enabled. + """ + + # Tell Pytest to ignore this, it's not a test class. + __test__ = False + + def __init__( + self, + response: t.Iterable[bytes], + status: str, + headers: Headers, + request: Request, + history: t.Tuple["TestResponse"] = (), # type: ignore + **kwargs: t.Any, + ) -> None: + super().__init__(response, status, headers, **kwargs) + self.request = request + self.history = history + self._compat_tuple = response, status, headers + + def __iter__(self) -> t.Iterator: + warnings.warn( + ( + "The test client no longer returns a tuple, it returns" + " a 'TestResponse'. Tuple unpacking is deprecated and" + " will be removed in Werkzeug 2.1. Access the" + " attributes 'data', 'status', and 'headers' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + return iter(self._compat_tuple) + + def __getitem__(self, item: int) -> t.Any: + warnings.warn( + ( + "The test client no longer returns a tuple, it returns" + " a 'TestResponse'. Item indexing is deprecated and" + " will be removed in Werkzeug 2.1. Access the" + " attributes 'data', 'status', and 'headers' instead." + ), + DeprecationWarning, + stacklevel=2, + ) + return self._compat_tuple[item] diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/testapp.py b/myvenv/lib/python3.10/site-packages/werkzeug/testapp.py new file mode 100644 index 0000000..981f887 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/testapp.py @@ -0,0 +1,240 @@ +"""A small application that can be used to test a WSGI server and check +it for WSGI compliance. +""" +import base64 +import os +import sys +import typing as t +from html import escape +from textwrap import wrap + +from . import __version__ as _werkzeug_version +from .wrappers.request import Request +from .wrappers.response import Response + +if t.TYPE_CHECKING: + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIEnvironment + + +logo = Response( + base64.b64decode( + """ +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 +=""" + ), + mimetype="image/png", +) + + +TEMPLATE = """\ + +WSGI Information + +

    + +

    WSGI Information

    +

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

    Python Interpreter

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

    WSGI Environment

    + %(wsgi_env)s
    +

    Installed Eggs

    +

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

      %(python_eggs)s
    +

    System Path

    +

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

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

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

    foo bar

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

    <foo>

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

    Redirecting...

    \n" + "

    You should be redirected automatically to target URL: " + f'{display_location}. If' + " not click the link.", + code, + mimetype="text/html", + ) + response.headers["Location"] = location + return response + + +def append_slash_redirect(environ: "WSGIEnvironment", code: int = 301) -> "Response": + """Redirects to the same URL but with a slash appended. The behavior + of this function is undefined if the path ends with a slash already. + + :param environ: the WSGI environment for the request that triggers + the redirect. + :param code: the status code for the redirect. + """ + new_path = environ["PATH_INFO"].strip("/") + "/" + query_string = environ.get("QUERY_STRING") + if query_string: + new_path += f"?{query_string}" + return redirect(new_path, code) + + +def send_file( + path_or_file: t.Union[os.PathLike, str, t.IO[bytes]], + environ: "WSGIEnvironment", + mimetype: t.Optional[str] = None, + as_attachment: bool = False, + download_name: t.Optional[str] = None, + conditional: bool = True, + etag: t.Union[bool, str] = True, + last_modified: t.Optional[t.Union[datetime, int, float]] = None, + max_age: t.Optional[ + t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]] + ] = None, + use_x_sendfile: bool = False, + response_class: t.Optional[t.Type["Response"]] = None, + _root_path: t.Optional[t.Union[os.PathLike, str]] = None, +) -> "Response": + """Send the contents of a file to the client. + + The first argument can be a file path or a file-like object. Paths + are preferred in most cases because Werkzeug can manage the file and + get extra information from the path. Passing a file-like object + requires that the file is opened in binary mode, and is mostly + useful when building a file in memory with :class:`io.BytesIO`. + + Never pass file paths provided by a user. The path is assumed to be + trusted, so a user could craft a path to access a file you didn't + intend. + + If the WSGI server sets a ``file_wrapper`` in ``environ``, it is + used, otherwise Werkzeug's built-in wrapper is used. Alternatively, + if the HTTP server supports ``X-Sendfile``, ``use_x_sendfile=True`` + will tell the server to send the given path, which is much more + efficient than reading it in Python. + + :param path_or_file: The path to the file to send, relative to the + current working directory if a relative path is given. + Alternatively, a file-like object opened in binary mode. Make + sure the file pointer is seeked to the start of the data. + :param environ: The WSGI environ for the current request. + :param mimetype: The MIME type to send for the file. If not + provided, it will try to detect it from the file name. + :param as_attachment: Indicate to a browser that it should offer to + save the file instead of displaying it. + :param download_name: The default name browsers will use when saving + the file. Defaults to the passed file name. + :param conditional: Enable conditional and range responses based on + request headers. Requires passing a file path and ``environ``. + :param etag: Calculate an ETag for the file, which requires passing + a file path. Can also be a string to use instead. + :param last_modified: The last modified time to send for the file, + in seconds. If not provided, it will try to detect it from the + file path. + :param max_age: How long the client should cache the file, in + seconds. If set, ``Cache-Control`` will be ``public``, otherwise + it will be ``no-cache`` to prefer conditional caching. + :param use_x_sendfile: Set the ``X-Sendfile`` header to let the + server to efficiently send the file. Requires support from the + HTTP server. Requires passing a file path. + :param response_class: Build the response using this class. Defaults + to :class:`~werkzeug.wrappers.Response`. + :param _root_path: Do not use. For internal use only. Use + :func:`send_from_directory` to safely send files under a path. + + .. versionchanged:: 2.0.2 + ``send_file`` only sets a detected ``Content-Encoding`` if + ``as_attachment`` is disabled. + + .. versionadded:: 2.0 + Adapted from Flask's implementation. + + .. versionchanged:: 2.0 + ``download_name`` replaces Flask's ``attachment_filename`` + parameter. If ``as_attachment=False``, it is passed with + ``Content-Disposition: inline`` instead. + + .. versionchanged:: 2.0 + ``max_age`` replaces Flask's ``cache_timeout`` parameter. + ``conditional`` is enabled and ``max_age`` is not set by + default. + + .. versionchanged:: 2.0 + ``etag`` replaces Flask's ``add_etags`` parameter. It can be a + string to use instead of generating one. + + .. versionchanged:: 2.0 + If an encoding is returned when guessing ``mimetype`` from + ``download_name``, set the ``Content-Encoding`` header. + """ + if response_class is None: + from .wrappers import Response + + response_class = Response + + path: t.Optional[str] = None + file: t.Optional[t.IO[bytes]] = None + size: t.Optional[int] = None + mtime: t.Optional[float] = None + headers = Headers() + + if isinstance(path_or_file, (os.PathLike, str)) or hasattr( + path_or_file, "__fspath__" + ): + path_or_file = t.cast(t.Union[os.PathLike, str], path_or_file) + + # Flask will pass app.root_path, allowing its send_file wrapper + # to not have to deal with paths. + if _root_path is not None: + path = os.path.join(_root_path, path_or_file) + else: + path = os.path.abspath(path_or_file) + + stat = os.stat(path) + size = stat.st_size + mtime = stat.st_mtime + else: + file = path_or_file + + if download_name is None and path is not None: + download_name = os.path.basename(path) + + if mimetype is None: + if download_name is None: + raise TypeError( + "Unable to detect the MIME type because a file name is" + " not available. Either set 'download_name', pass a" + " path instead of a file, or set 'mimetype'." + ) + + mimetype, encoding = mimetypes.guess_type(download_name) + + if mimetype is None: + mimetype = "application/octet-stream" + + # Don't send encoding for attachments, it causes browsers to + # save decompress tar.gz files. + if encoding is not None and not as_attachment: + headers.set("Content-Encoding", encoding) + + if download_name is not None: + try: + download_name.encode("ascii") + except UnicodeEncodeError: + simple = unicodedata.normalize("NFKD", download_name) + simple = simple.encode("ascii", "ignore").decode("ascii") + quoted = url_quote(download_name, safe="") + names = {"filename": simple, "filename*": f"UTF-8''{quoted}"} + else: + names = {"filename": download_name} + + value = "attachment" if as_attachment else "inline" + headers.set("Content-Disposition", value, **names) + elif as_attachment: + raise TypeError( + "No name provided for attachment. Either set" + " 'download_name' or pass a path instead of a file." + ) + + if use_x_sendfile and path is not None: + headers["X-Sendfile"] = path + data = None + else: + if file is None: + file = open(path, "rb") # type: ignore + elif isinstance(file, io.BytesIO): + size = file.getbuffer().nbytes + elif isinstance(file, io.TextIOBase): + raise ValueError("Files must be opened in binary mode or use BytesIO.") + + data = wrap_file(environ, file) + + rv = response_class( + data, mimetype=mimetype, headers=headers, direct_passthrough=True + ) + + if size is not None: + rv.content_length = size + + if last_modified is not None: + rv.last_modified = last_modified # type: ignore + elif mtime is not None: + rv.last_modified = mtime # type: ignore + + rv.cache_control.no_cache = True + + # Flask will pass app.get_send_file_max_age, allowing its send_file + # wrapper to not have to deal with paths. + if callable(max_age): + max_age = max_age(path) + + if max_age is not None: + if max_age > 0: + rv.cache_control.no_cache = None + rv.cache_control.public = True + + rv.cache_control.max_age = max_age + rv.expires = int(time() + max_age) # type: ignore + + if isinstance(etag, str): + rv.set_etag(etag) + elif etag and path is not None: + check = adler32(path.encode("utf-8")) & 0xFFFFFFFF + rv.set_etag(f"{mtime}-{size}-{check}") + + if conditional: + try: + rv = rv.make_conditional(environ, accept_ranges=True, complete_length=size) + except RequestedRangeNotSatisfiable: + if file is not None: + file.close() + + raise + + # Some x-sendfile implementations incorrectly ignore the 304 + # status code and send the file anyway. + if rv.status_code == 304: + rv.headers.pop("x-sendfile", None) + + return rv + + +def send_from_directory( + directory: t.Union[os.PathLike, str], + path: t.Union[os.PathLike, str], + environ: "WSGIEnvironment", + **kwargs: t.Any, +) -> "Response": + """Send a file from within a directory using :func:`send_file`. + + This is a secure way to serve files from a folder, such as static + files or uploads. Uses :func:`~werkzeug.security.safe_join` to + ensure the path coming from the client is not maliciously crafted to + point outside the specified directory. + + If the final path does not point to an existing regular file, + returns a 404 :exc:`~werkzeug.exceptions.NotFound` error. + + :param directory: The directory that ``path`` must be located under. + :param path: The path to the file to send, relative to + ``directory``. + :param environ: The WSGI environ for the current request. + :param kwargs: Arguments to pass to :func:`send_file`. + + .. versionadded:: 2.0 + Adapted from Flask's implementation. + """ + path = safe_join(os.fspath(directory), os.fspath(path)) + + if path is None: + raise NotFound() + + # Flask will pass app.root_path, allowing its send_from_directory + # wrapper to not have to deal with paths. + if "_root_path" in kwargs: + path = os.path.join(kwargs["_root_path"], path) + + try: + if not os.path.isfile(path): + raise NotFound() + except ValueError: + # path contains null byte on Python < 3.8 + raise NotFound() from None + + return send_file(path, environ, **kwargs) + + +def import_string(import_name: str, silent: bool = False) -> t.Any: + """Imports an object based on a string. This is useful if you want to + use import paths as endpoints or something similar. An import path can + be specified either in dotted notation (``xml.sax.saxutils.escape``) + or with a colon as object delimiter (``xml.sax.saxutils:escape``). + + If `silent` is True the return value will be `None` if the import fails. + + :param import_name: the dotted name for the object to import. + :param silent: if set to `True` import errors are ignored and + `None` is returned instead. + :return: imported object + """ + import_name = import_name.replace(":", ".") + try: + try: + __import__(import_name) + except ImportError: + if "." not in import_name: + raise + else: + return sys.modules[import_name] + + module_name, obj_name = import_name.rsplit(".", 1) + module = __import__(module_name, globals(), locals(), [obj_name]) + try: + return getattr(module, obj_name) + except AttributeError as e: + raise ImportError(e) from None + + except ImportError as e: + if not silent: + raise ImportStringError(import_name, e).with_traceback( + sys.exc_info()[2] + ) from None + + return None + + +def find_modules( + import_path: str, include_packages: bool = False, recursive: bool = False +) -> t.Iterator[str]: + """Finds all the modules below a package. This can be useful to + automatically import all views / controllers so that their metaclasses / + function decorators have a chance to register themselves on the + application. + + Packages are not returned unless `include_packages` is `True`. This can + also recursively list modules but in that case it will import all the + packages to get the correct load path of that module. + + :param import_path: the dotted name for the package to find child modules. + :param include_packages: set to `True` if packages should be returned, too. + :param recursive: set to `True` if recursion should happen. + :return: generator + """ + module = import_string(import_path) + path = getattr(module, "__path__", None) + if path is None: + raise ValueError(f"{import_path!r} is not a package") + basename = f"{module.__name__}." + for _importer, modname, ispkg in pkgutil.iter_modules(path): + modname = basename + modname + if ispkg: + if include_packages: + yield modname + if recursive: + yield from find_modules(modname, include_packages, True) + else: + yield modname + + +def validate_arguments(func, args, kwargs, drop_extra=True): # type: ignore + """Checks if the function accepts the arguments and keyword arguments. + Returns a new ``(args, kwargs)`` tuple that can safely be passed to + the function without causing a `TypeError` because the function signature + is incompatible. If `drop_extra` is set to `True` (which is the default) + any extra positional or keyword arguments are dropped automatically. + + The exception raised provides three attributes: + + `missing` + A set of argument names that the function expected but where + missing. + + `extra` + A dict of keyword arguments that the function can not handle but + where provided. + + `extra_positional` + A list of values that where given by positional argument but the + function cannot accept. + + This can be useful for decorators that forward user submitted data to + a view function:: + + from werkzeug.utils import ArgumentValidationError, validate_arguments + + def sanitize(f): + def proxy(request): + data = request.values.to_dict() + try: + args, kwargs = validate_arguments(f, (request,), data) + except ArgumentValidationError: + raise BadRequest('The browser failed to transmit all ' + 'the data expected.') + return f(*args, **kwargs) + return proxy + + :param func: the function the validation is performed against. + :param args: a tuple of positional arguments. + :param kwargs: a dict of keyword arguments. + :param drop_extra: set to `False` if you don't want extra arguments + to be silently dropped. + :return: tuple in the form ``(args, kwargs)``. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use :func:`inspect.signature` + instead. + """ + warnings.warn( + "'utils.validate_arguments' is deprecated and will be removed" + " in Werkzeug 2.1. Use 'inspect.signature' instead.", + DeprecationWarning, + stacklevel=2, + ) + parser = _parse_signature(func) + args, kwargs, missing, extra, extra_positional = parser(args, kwargs)[:5] + if missing: + raise ArgumentValidationError(tuple(missing)) + elif (extra or extra_positional) and not drop_extra: + raise ArgumentValidationError(None, extra, extra_positional) + return tuple(args), kwargs + + +def bind_arguments(func, args, kwargs): # type: ignore + """Bind the arguments provided into a dict. When passed a function, + a tuple of arguments and a dict of keyword arguments `bind_arguments` + returns a dict of names as the function would see it. This can be useful + to implement a cache decorator that uses the function arguments to build + the cache key based on the values of the arguments. + + :param func: the function the arguments should be bound for. + :param args: tuple of positional arguments. + :param kwargs: a dict of keyword arguments. + :return: a :class:`dict` of bound keyword arguments. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Use :meth:`Signature.bind` + instead. + """ + warnings.warn( + "'utils.bind_arguments' is deprecated and will be removed in" + " Werkzeug 2.1. Use 'Signature.bind' instead.", + DeprecationWarning, + stacklevel=2, + ) + ( + args, + kwargs, + missing, + extra, + extra_positional, + arg_spec, + vararg_var, + kwarg_var, + ) = _parse_signature(func)(args, kwargs) + values = {} + for (name, _has_default, _default), value in zip(arg_spec, args): + values[name] = value + if vararg_var is not None: + values[vararg_var] = tuple(extra_positional) + elif extra_positional: + raise TypeError("too many positional arguments") + if kwarg_var is not None: + multikw = set(extra) & {x[0] for x in arg_spec} + if multikw: + raise TypeError( + f"got multiple values for keyword argument {next(iter(multikw))!r}" + ) + values[kwarg_var] = extra + elif extra: + raise TypeError(f"got unexpected keyword argument {next(iter(extra))!r}") + return values + + +class ArgumentValidationError(ValueError): + """Raised if :func:`validate_arguments` fails to validate + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1 along with ``utils.bind`` and + ``validate_arguments``. + """ + + def __init__(self, missing=None, extra=None, extra_positional=None): # type: ignore + self.missing = set(missing or ()) + self.extra = extra or {} + self.extra_positional = extra_positional or [] + super().__init__( + "function arguments invalid." + f" ({len(self.missing)} missing," + f" {len(self.extra) + len(self.extra_positional)} additional)" + ) + + +class ImportStringError(ImportError): + """Provides information about a failed :func:`import_string` attempt.""" + + #: String in dotted notation that failed to be imported. + import_name: str + #: Wrapped exception. + exception: BaseException + + def __init__(self, import_name: str, exception: BaseException) -> None: + self.import_name = import_name + self.exception = exception + msg = import_name + name = "" + tracked = [] + for part in import_name.replace(":", ".").split("."): + name = f"{name}.{part}" if name else part + imported = import_string(name, silent=True) + if imported: + tracked.append((name, getattr(imported, "__file__", None))) + else: + track = [f"- {n!r} found in {i!r}." for n, i in tracked] + track.append(f"- {name!r} not found.") + track_str = "\n".join(track) + msg = ( + f"import_string() failed for {import_name!r}. Possible reasons" + f" are:\n\n" + "- missing __init__.py in a package;\n" + "- package or module path not included in sys.path;\n" + "- duplicated package or module name taking precedence in" + " sys.path;\n" + "- missing module, class, function or variable;\n\n" + f"Debugged import:\n\n{track_str}\n\n" + f"Original exception:\n\n{type(exception).__name__}: {exception}" + ) + break + + super().__init__(msg) + + def __repr__(self) -> str: + return f"<{type(self).__name__}({self.import_name!r}, {self.exception!r})>" diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__init__.py b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__init__.py new file mode 100644 index 0000000..eb69a99 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__init__.py @@ -0,0 +1,16 @@ +from .accept import AcceptMixin +from .auth import AuthorizationMixin +from .auth import WWWAuthenticateMixin +from .base_request import BaseRequest +from .base_response import BaseResponse +from .common_descriptors import CommonRequestDescriptorsMixin +from .common_descriptors import CommonResponseDescriptorsMixin +from .etag import ETagRequestMixin +from .etag import ETagResponseMixin +from .request import PlainRequest +from .request import Request as Request +from .request import StreamOnlyMixin +from .response import Response as Response +from .response import ResponseStream +from .response import ResponseStreamMixin +from .user_agent import UserAgentMixin diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d114b94dcea02ca436cf2e4636ff7b79666979ae GIT binary patch literal 918 zcmZ{i&2rN)5XbGrNu2m2PMVf*fZ>`0H65-DL-;sSIt@%_bnz&TCJ~ivSCZSN55arz z794m2j=pl@6*#eyV+u1Mk9U6i*Y0YyqdW)(7OXGdPF9~khBBD3%@n&D3$r{yKozsZ^8N;+W3eN7`wKZHQ zK`?)pv0W4Go|E5Qh7H$YkbCgA(jk zh>ZE}iey(YqpP?udryx~p2U(W^0>h11yq;uH4zuLq};^U0v81lGENejQI#ae#jbLn W@qAq}@?5_=`3Q_}`S$<6$o>WE`2yAe literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/accept.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/accept.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a12cacb6aefe83c33f4d0166fcf264eca896bf9a GIT binary patch literal 828 zcmYjPJ#X7E5G5s9QPZR!LEBmA5W_>Ird^Ao0Sa`gQPe{KFN7?f9Xd26law6?*%BaA z`wzNx$-n4t=*YEG{zAL-C>wzZ@bSnyJid2iwZFehaQymxH2Oga`Qta+BgD-IoaQA0 zC!9($r0p6EgHMD9JiI16yb6XPrvuW9?y!#ZOOM+l-fT>U0kj;$fKv#k5f54nc{Gl1 z$e8jDkFWQJof{I7VT_zR?%{D+LTx{bbD>;R=!tRh*>b&Vu#Hyz64l=iueyWtF-~)c zV9AtnY6GwLTS$owugEmIq=H-qKk0W$(VJJ_zcAgcySk|`DTi7^SycKbG we+1)Ok&r{$p7bIY+f|Ld)z5uZ;=z}0akOln!zJ(5V&3~7X1u3_B=jKs4|F8jbN~PV literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/auth.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/auth.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..568cfb1817b145cfc01559c714c66246379c6b3b GIT binary patch literal 1222 zcmbVLJ#Q015Z%2mJHg?@KtKY-Rk2dwOQ4}Zh)76OScnvs+?>w0>tyZo-eq^sj;&B7 zQY!crH2D|&2Fh!xQoDnOnX?I&l1PY^_TA3TdS>3tbGo*+N??8azTJIk6Y>L%Cp0Pb*y)USa^6ArwL z*s&PpcHd!Ei8gb3-YMx(<}&Z3)^SgXLpmPBtQ7T~-0Y`HOd}&w`Cc3fSvZl}(}gz} z&%cVBT*+2Um$D)i7xpdKvkd@4`jk;)A&Jj6CB{A`edmY@a%6p_Unm8_kkDyOr;W=g z8@|v!;k23 zdKi3|K)YqIdytD(YPb%DlM$DrAQ9alD}67T+gpJahCj=o$Y>AVj2FCvv5GRNLkAH| zt(i^KZODuQnAHFrYEjF!sP}8VGJSNp%gu}GE67AfLmr03Y8VbvmM3_wh2cRSB?}KV zg}o!^Ns6@vxtJ$O7^?e_Zw`0yw2tsc6fA(MpBh)i`Cnz}ER~x7yR=jl%1|o^HxTY1l&vW{RrUvmRmb}W0MjQ| zm3uKF^$;Ik)ny+XW%Q@`0`3i|`ri z2ZA1wPU{jhk=`W**`YT0K^|99n-ovi2UxR2{u~yvMg>GF-7yo6^oC5hf?cq_4~XtK zXwDFm^+ORNlHK9oCKGvB4bnW(`q4z$7Fze%)gT-c8?9SJ#8GEIyWQBsI#p5=Kokdczp@&wMZB6=d# zh8`^3xfZl^nsFdE;I@SIKN9m5v7x zBCeKc?#>qW2InhxQ%nixAsF#$3k0JMbsUHKf3^M7yPMhTZhUG(3{g^~@~wGP0cieT zv$cY}Uk=7^%9cWhR10A}bq&|(oZ1F~`B;74%@Nh<9aG zy{c_MdSYBPc8HMd(!G|#vke~OJr3OkvB6_xGw^umKP5vdJmFt#^}VNrk-iTM3+%Nu zep!|%D0P1VwSJ@(D(|RmKB;TrBUrr;Q)2;yd)U)bvFEBS-@Xn+BN=lg(Rd~&UJTh_ivD1yD$I% literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/base_response.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/base_response.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42883d5aae4a9e97b61b030e27ccf54023e792d0 GIT binary patch literal 1751 zcmb_d&5zqe6rZujjuWz?UAFD=ZK0}ZB#67TM}&Y@aElNNs+7yf@l1A|ICeTS-jLPa zcBP)$KLT;-f5G3taju;9+#7J2#jBTJsAJQ2>A^+ zo5P3A7cliB5P}GrkrAzaoslCP;b!ifjTj~05W$3ZL4+s0b9dy!-WM&{wF_`=}nk$1-oD)A0XXn zt3E?eHVQ<5PxaiOnb|0zSZAY6pnPu`sW_`~F%UI=U zexxnKmEC<*!>2|5cztC%SK5^eO8Sm<0Y#yoFNnGZKflkT<6%GYkw=G5%5+#5siXPoMCK<^mX4!Eb$5Soa4*uSk)JHUtoR7tENlOwrHU5|sdO}k zAaPY^dazj8>zp?ZCzut^1F+&{7X+gYbsUHKe|G%S+uI2oY<+42j8U8?^0lc{Eztb` zCal4{Ta2f#OqfEKR2zXk^#-odJ+%V@Q__52r$oJpmwPBMA!~6R-$JaKw{@)WSiJ?} zHSzh!^C2)H;5?7#l5^YPd|rq$!*!SQCuN+~H`G}6{c0gC8y7{Ub`jkDGGD2ih<9aG zwW@4Dj$&jr_KAS((fzithLW)t-x zIHp1n&=T65R}GHytFloa!QFc>_1~m&fmDCoY@OcN3@6a3*4$FJ;JMmI@iq!(g~sZkGPTeueupwl`^_-dfZX z{0c7p7yJf}d*#$~Zg63|NkfF%g0SRoJ)Yf}`DQ+Cy0Ws2V150z)o!~8eYeKqf;;pI zw%!Dwh+=_SxO_S-=PjZRbx#m=kDZnamPdWCe6T!>y{_Y%`rGo|#nXcl?mc z!i&|OE`r{0`c%y1N;VU`P=ICS!@dQ3vJPNS7gKB;%jc7ek#Ubv*E_--9XVg{XN-Y6 zSrMA&AbV*PXgcU*?MO!dYr-wU5Lwi;o5Mx`Fg zbO`Q5h`P<2(UYK6~4|hriv;dTyjImnOk_8iH`ChqOwhauuFs_BGww*=!c5-(>{y3xi(y~ zXa^{@)5GZf$n2*w+BwL1Gc`;{{n3EQK_qxP%1Yn!#@1$}xnWN;C^FuIcZM_G(NM)1 z)S;uKoFxIi7gKCxBXt|LduC_3 zYVYp@jQ>*je1PhK{p7M4gy1OiJh5+BYr`)BGs-~u>Y=@}qA3ScdKZGsiz=!wriyrJ Qe+$~OsG=&q>8{p(0VQr*#{d8T literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/cors.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/cors.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8eb7ea0da654c319cfab178259dfa4696b40400 GIT binary patch literal 1207 zcmbVLO>Yx15ViL!+mb?CMEMXBdm`$A-4?D0fk5I!K+U02F00MjX`MRWt?f;ksJB+? z34R5a{tJEs$GvjuIXAd4-VG%xR6+t*k5~SYN(w_d*+?Z^l?$aED&P zR$BlB5zJ8+S6{1Zy+y<#_9-IviPg2ia)=9-3zkE02W@&6@GI0#4K6NkZp=bB(2B8Z z5lroYL#zsI;`F^U)W^gn-f6Szo*@TyJ>YDVtFPbhbav=rL6v^Tj+iK&MDD55I~dnr zNp&GbJH-nzjSv_1E!eZi02&Q2!P+tsKH3;*`veV~W6aR8^%;M{8011Yr#(GtT@Ba@ zmHxG$ah&o*DR86jB-+o0G#V62GeM&cNa|%r(T9oN z&qUNYELc0!R7Jzdh>B6fSue^f-?MOgD^g6;CpjdU?7^GyTz52swh>Bs>+xqt(*J10MiG52)^u$yk}nh zt+)X=D(Xk(g|Rl>($kY1=2kv5cV+;pidWtRlFK|w|9Rc&7v_JgOyWNB@lAWZ`4cO3 BGQ0o) literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/etag.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/etag.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c54fe41f284cfd9e86737fa9ee33d46fb44f58df GIT binary patch literal 1207 zcmbVLO>Yx15ViL!+mb?CMEMXBdur4JyDeN10tv*42vMX;xvVy8Cw1z0x3)KFqTX7m zC-@ay`Y-qm9QVqp=iK1Jc(atKPzhnlZ#^F8&AgdOwz9H}V14%ONgUE?5r1yJ*Y1fM20@YH)FRwJ{6fKr6B6fSvSfn-_vksJ5o&3$2laK^x@68)*X#yl0zOUqB`k^`9!Wl zWYhdv6Trb1wrmS~KQ}%6S~iI}mLSx6Nt8z^{RrnUeTu6IVCaj;b`hfZFG%hb1NU3J2L=P#VhXu$z>j;|7_ms=jMN`OyWNB@lAWZ`4b!N BGok!_#}Sstym^2;$f8k1%r46`I7CSfWev6aT;%oCWRu)vUX_sY|7$T+~LCg%g|>n$v|;ijD*| zVp`4No7D6~nDdorPU*|+IHTRKV%7-jx>Q#SD3_|?!cuo8=(y3Pla(%%bW6%5@H(~3 z?g$9%Vc)i{D9@Fc3)O4+6zplK!D$>a-+8|k0t6?Ul5g3j7RINHNi7{?y~xK_sIdwD zO$>00{XS!*Dy(Jf7X9Va literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/request.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/request.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d64524c8493965b59cd096b206f304f86c9f8aca GIT binary patch literal 21400 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 j!4GjM=a`bte^odd_3r@F^uMW#`w#p-Pej{) literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/response.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/response.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a9c9c0b2bab49fe461c11a84ef71eaf260d5b0bf GIT binary patch literal 29815 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 Lmj0Wm&Yk|>{Z%J4 literal 0 HcmV?d00001 diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/user_agent.cpython-310.pyc b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/__pycache__/user_agent.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..184a72d2ab929593332eaca8116858096b44a7e0 GIT binary patch literal 841 zcmY*X&2AGh5Vm(W+ofvxsRUdt4vlhPm%lxlj%?`mFpFuljuk*c}b-%z#3)Xd+uICu8R4c$TcLZ$I>dMJlmrA+?6B6q> zwa9NGWZ9#B)4H-cRxktA>-Y>cYN=6f5!gO_03{9U)8g~O9o4!xIB8|Sc3_L?Vg`Cv zC^;;eW$aD9_o}ec!OI3omSfDBuXPu5Q#Q!M7A?LO$C!3`voKrOKRmw;PC_FZ#Su;a zJ=-{Yet*vSE&q+6MVC|H+;4I|twpPXzs31UTdH-0Iv?-rRRh1hmREsNoSVnkYXuL3 z_as1=Z}<{6Oe7<_ba~TDeCierj@Ud6Sy_OPdZ)&+TBzZ(w<|L5!e6sNRz@ None: + warnings.warn( + "'AcceptMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Request' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/auth.py b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/auth.py new file mode 100644 index 0000000..f7f5815 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/auth.py @@ -0,0 +1,26 @@ +import typing as t +import warnings + + +class AuthorizationMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'AuthorizationMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Request' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) + + +class WWWAuthenticateMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'WWWAuthenticateMixin' is deprecated and will be removed" + " in Werkzeug 2.1. 'Response' now includes the" + " functionality directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/base_request.py b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/base_request.py new file mode 100644 index 0000000..451989f --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/base_request.py @@ -0,0 +1,36 @@ +import typing as t +import warnings + +from .request import Request + + +class _FakeSubclassCheck(type): + def __subclasscheck__(cls, subclass: t.Type) -> bool: + warnings.warn( + "'BaseRequest' is deprecated and will be removed in" + " Werkzeug 2.1. Use 'issubclass(cls, Request)' instead.", + DeprecationWarning, + stacklevel=2, + ) + return issubclass(subclass, Request) + + def __instancecheck__(cls, instance: t.Any) -> bool: + warnings.warn( + "'BaseRequest' is deprecated and will be removed in" + " Werkzeug 2.1. Use 'isinstance(obj, Request)' instead.", + DeprecationWarning, + stacklevel=2, + ) + return isinstance(instance, Request) + + +class BaseRequest(Request, metaclass=_FakeSubclassCheck): + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'BaseRequest' is deprecated and will be removed in" + " Werkzeug 2.1. 'Request' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/base_response.py b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/base_response.py new file mode 100644 index 0000000..3e0dc67 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/base_response.py @@ -0,0 +1,36 @@ +import typing as t +import warnings + +from .response import Response + + +class _FakeSubclassCheck(type): + def __subclasscheck__(cls, subclass: t.Type) -> bool: + warnings.warn( + "'BaseResponse' is deprecated and will be removed in" + " Werkzeug 2.1. Use 'issubclass(cls, Response)' instead.", + DeprecationWarning, + stacklevel=2, + ) + return issubclass(subclass, Response) + + def __instancecheck__(cls, instance: t.Any) -> bool: + warnings.warn( + "'BaseResponse' is deprecated and will be removed in" + " Werkzeug 2.1. Use 'isinstance(obj, Response)' instead.", + DeprecationWarning, + stacklevel=2, + ) + return isinstance(instance, Response) + + +class BaseResponse(Response, metaclass=_FakeSubclassCheck): + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'BaseResponse' is deprecated and will be removed in" + " Werkzeug 2.1. 'Response' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/common_descriptors.py b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/common_descriptors.py new file mode 100644 index 0000000..6436b4c --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/common_descriptors.py @@ -0,0 +1,26 @@ +import typing as t +import warnings + + +class CommonRequestDescriptorsMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'CommonRequestDescriptorsMixin' is deprecated and will be" + " removed in Werkzeug 2.1. 'Request' now includes the" + " functionality directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) + + +class CommonResponseDescriptorsMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'CommonResponseDescriptorsMixin' is deprecated and will be" + " removed in Werkzeug 2.1. 'Response' now includes the" + " functionality directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/cors.py b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/cors.py new file mode 100644 index 0000000..efd8537 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/cors.py @@ -0,0 +1,26 @@ +import typing as t +import warnings + + +class CORSRequestMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'CORSRequestMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Request' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) + + +class CORSResponseMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'CORSResponseMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Response' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/etag.py b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/etag.py new file mode 100644 index 0000000..9131b93 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/etag.py @@ -0,0 +1,26 @@ +import typing as t +import warnings + + +class ETagRequestMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'ETagRequestMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Request' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) + + +class ETagResponseMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'ETagResponseMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Response' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/json.py b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/json.py new file mode 100644 index 0000000..a4dd7c2 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/json.py @@ -0,0 +1,13 @@ +import typing as t +import warnings + + +class JSONMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'JSONMixin' is deprecated and will be removed in Werkzeug" + " 2.1. 'Request' now includes the functionality directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/request.py b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/request.py new file mode 100644 index 0000000..f68dd5a --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/request.py @@ -0,0 +1,660 @@ +import functools +import json +import typing +import typing as t +import warnings +from io import BytesIO + +from .._internal import _wsgi_decoding_dance +from ..datastructures import CombinedMultiDict +from ..datastructures import EnvironHeaders +from ..datastructures import FileStorage +from ..datastructures import ImmutableMultiDict +from ..datastructures import iter_multi_items +from ..datastructures import MultiDict +from ..formparser import default_stream_factory +from ..formparser import FormDataParser +from ..sansio.request import Request as _SansIORequest +from ..utils import cached_property +from ..utils import environ_property +from ..wsgi import _get_server +from ..wsgi import get_input_stream +from werkzeug.exceptions import BadRequest + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +class Request(_SansIORequest): + """Represents an incoming WSGI HTTP request, with headers and body + taken from the WSGI environment. Has properties and methods for + using the functionality defined by various HTTP specs. The data in + requests object is read-only. + + Text data is assumed to use UTF-8 encoding, which should be true for + the vast majority of modern clients. Using an encoding set by the + client is unsafe in Python due to extra encodings it provides, such + as ``zip``. To change the assumed encoding, subclass and replace + :attr:`charset`. + + :param environ: The WSGI environ is generated by the WSGI server and + contains information about the server configuration and client + request. + :param populate_request: Add this request object to the WSGI environ + as ``environ['werkzeug.request']``. Can be useful when + debugging. + :param shallow: Makes reading from :attr:`stream` (and any method + that would read from it) raise a :exc:`RuntimeError`. Useful to + prevent consuming the form data in middleware, which would make + it unavailable to the final application. + + .. versionchanged:: 2.0 + Combine ``BaseRequest`` and mixins into a single ``Request`` + class. Using the old classes is deprecated and will be removed + in Werkzeug 2.1. + + .. versionchanged:: 0.5 + Read-only mode is enforced with immutable classes for all data. + """ + + #: the maximum content length. This is forwarded to the form data + #: parsing function (:func:`parse_form_data`). When set and the + #: :attr:`form` or :attr:`files` attribute is accessed and the + #: parsing fails because more than the specified value is transmitted + #: a :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised. + #: + #: Have a look at :doc:`/request_data` for more details. + #: + #: .. versionadded:: 0.5 + max_content_length: t.Optional[int] = None + + #: the maximum form field size. This is forwarded to the form data + #: parsing function (:func:`parse_form_data`). When set and the + #: :attr:`form` or :attr:`files` attribute is accessed and the + #: data in memory for post data is longer than the specified value a + #: :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised. + #: + #: Have a look at :doc:`/request_data` for more details. + #: + #: .. versionadded:: 0.5 + max_form_memory_size: t.Optional[int] = None + + #: The form data parser that shoud be used. Can be replaced to customize + #: the form date parsing. + form_data_parser_class: t.Type[FormDataParser] = FormDataParser + + #: Disable the :attr:`data` property to avoid reading from the input + #: stream. + #: + #: .. deprecated:: 2.0 + #: Will be removed in Werkzeug 2.1. Create the request with + #: ``shallow=True`` instead. + #: + #: .. versionadded:: 0.9 + disable_data_descriptor: t.Optional[bool] = None + + #: The WSGI environment containing HTTP headers and information from + #: the WSGI server. + environ: "WSGIEnvironment" + + #: Set when creating the request object. If ``True``, reading from + #: the request body will cause a ``RuntimeException``. Useful to + #: prevent modifying the stream from middleware. + shallow: bool + + def __init__( + self, + environ: "WSGIEnvironment", + populate_request: bool = True, + shallow: bool = False, + ) -> None: + super().__init__( + method=environ.get("REQUEST_METHOD", "GET"), + scheme=environ.get("wsgi.url_scheme", "http"), + server=_get_server(environ), + root_path=_wsgi_decoding_dance( + environ.get("SCRIPT_NAME") or "", self.charset, self.encoding_errors + ), + path=_wsgi_decoding_dance( + environ.get("PATH_INFO") or "", self.charset, self.encoding_errors + ), + query_string=environ.get("QUERY_STRING", "").encode("latin1"), + headers=EnvironHeaders(environ), + remote_addr=environ.get("REMOTE_ADDR"), + ) + self.environ = environ + + if self.disable_data_descriptor is not None: + warnings.warn( + "'disable_data_descriptor' is deprecated and will be" + " removed in Werkzeug 2.1. Create the request with" + " 'shallow=True' instead.", + DeprecationWarning, + stacklevel=2, + ) + shallow = shallow or self.disable_data_descriptor + + self.shallow = shallow + + if populate_request and not shallow: + self.environ["werkzeug.request"] = self + + @classmethod + def from_values(cls, *args: t.Any, **kwargs: t.Any) -> "Request": + """Create a new request object based on the values provided. If + environ is given missing values are filled from there. This method is + useful for small scripts when you need to simulate a request from an URL. + Do not use this method for unittesting, there is a full featured client + object (:class:`Client`) that allows to create multipart requests, + support for cookies etc. + + This accepts the same options as the + :class:`~werkzeug.test.EnvironBuilder`. + + .. versionchanged:: 0.5 + This method now accepts the same arguments as + :class:`~werkzeug.test.EnvironBuilder`. Because of this the + `environ` parameter is now called `environ_overrides`. + + :return: request object + """ + from ..test import EnvironBuilder + + charset = kwargs.pop("charset", cls.charset) + kwargs["charset"] = charset + builder = EnvironBuilder(*args, **kwargs) + try: + return builder.get_request(cls) + finally: + builder.close() + + @classmethod + def application( + cls, f: t.Callable[["Request"], "WSGIApplication"] + ) -> "WSGIApplication": + """Decorate a function as responder that accepts the request as + the last argument. This works like the :func:`responder` + decorator but the function is passed the request object as the + last argument and the request object will be closed + automatically:: + + @Request.application + def my_wsgi_app(request): + return Response('Hello World!') + + As of Werkzeug 0.14 HTTP exceptions are automatically caught and + converted to responses instead of failing. + + :param f: the WSGI callable to decorate + :return: a new WSGI callable + """ + #: return a callable that wraps the -2nd argument with the request + #: and calls the function with all the arguments up to that one and + #: the request. The return value is then called with the latest + #: two arguments. This makes it possible to use this decorator for + #: both standalone WSGI functions as well as bound methods and + #: partially applied functions. + from ..exceptions import HTTPException + + @functools.wraps(f) + def application(*args): # type: ignore + request = cls(args[-2]) + with request: + try: + resp = f(*args[:-2] + (request,)) + except HTTPException as e: + resp = e.get_response(args[-2]) + return resp(*args[-2:]) + + return t.cast("WSGIApplication", application) + + def _get_file_stream( + self, + total_content_length: t.Optional[int], + content_type: t.Optional[str], + filename: t.Optional[str] = None, + content_length: t.Optional[int] = None, + ) -> t.IO[bytes]: + """Called to get a stream for the file upload. + + This must provide a file-like class with `read()`, `readline()` + and `seek()` methods that is both writeable and readable. + + The default implementation returns a temporary file if the total + content length is higher than 500KB. Because many browsers do not + provide a content length for the files only the total content + length matters. + + :param total_content_length: the total content length of all the + data in the request combined. This value + is guaranteed to be there. + :param content_type: the mimetype of the uploaded file. + :param filename: the filename of the uploaded file. May be `None`. + :param content_length: the length of this file. This value is usually + not provided because webbrowsers do not provide + this value. + """ + return default_stream_factory( + total_content_length=total_content_length, + filename=filename, + content_type=content_type, + content_length=content_length, + ) + + @property + def want_form_data_parsed(self) -> bool: + """``True`` if the request method carries content. By default + this is true if a ``Content-Type`` is sent. + + .. versionadded:: 0.8 + """ + return bool(self.environ.get("CONTENT_TYPE")) + + def make_form_data_parser(self) -> FormDataParser: + """Creates the form data parser. Instantiates the + :attr:`form_data_parser_class` with some parameters. + + .. versionadded:: 0.8 + """ + return self.form_data_parser_class( + self._get_file_stream, + self.charset, + self.encoding_errors, + self.max_form_memory_size, + self.max_content_length, + self.parameter_storage_class, + ) + + def _load_form_data(self) -> None: + """Method used internally to retrieve submitted data. After calling + this sets `form` and `files` on the request object to multi dicts + filled with the incoming form data. As a matter of fact the input + stream will be empty afterwards. You can also call this method to + force the parsing of the form data. + + .. versionadded:: 0.8 + """ + # abort early if we have already consumed the stream + if "form" in self.__dict__: + return + + if self.want_form_data_parsed: + parser = self.make_form_data_parser() + data = parser.parse( + self._get_stream_for_parsing(), + self.mimetype, + self.content_length, + self.mimetype_params, + ) + else: + data = ( + self.stream, + self.parameter_storage_class(), + self.parameter_storage_class(), + ) + + # inject the values into the instance dict so that we bypass + # our cached_property non-data descriptor. + d = self.__dict__ + d["stream"], d["form"], d["files"] = data + + def _get_stream_for_parsing(self) -> t.IO[bytes]: + """This is the same as accessing :attr:`stream` with the difference + that if it finds cached data from calling :meth:`get_data` first it + will create a new stream out of the cached data. + + .. versionadded:: 0.9.3 + """ + cached_data = getattr(self, "_cached_data", None) + if cached_data is not None: + return BytesIO(cached_data) + return self.stream + + def close(self) -> None: + """Closes associated resources of this request object. This + closes all file handles explicitly. You can also use the request + object in a with statement which will automatically close it. + + .. versionadded:: 0.9 + """ + files = self.__dict__.get("files") + for _key, value in iter_multi_items(files or ()): + value.close() + + def __enter__(self) -> "Request": + return self + + def __exit__(self, exc_type, exc_value, tb) -> None: # type: ignore + self.close() + + @cached_property + def stream(self) -> t.IO[bytes]: + """ + If the incoming form data was not encoded with a known mimetype + the data is stored unmodified in this stream for consumption. Most + of the time it is a better idea to use :attr:`data` which will give + you that data as a string. The stream only returns the data once. + + Unlike :attr:`input_stream` this stream is properly guarded that you + can't accidentally read past the length of the input. Werkzeug will + internally always refer to this stream to read data which makes it + possible to wrap this object with a stream that does filtering. + + .. versionchanged:: 0.9 + This stream is now always available but might be consumed by the + form parser later on. Previously the stream was only set if no + parsing happened. + """ + if self.shallow: + raise RuntimeError( + "This request was created with 'shallow=True', reading" + " from the input stream is disabled." + ) + + return get_input_stream(self.environ) + + input_stream = environ_property[t.IO[bytes]]( + "wsgi.input", + doc="""The WSGI input stream. + + In general it's a bad idea to use this one because you can + easily read past the boundary. Use the :attr:`stream` + instead.""", + ) + + @cached_property + def data(self) -> bytes: + """ + Contains the incoming request data as string in case it came with + a mimetype Werkzeug does not handle. + """ + return self.get_data(parse_form_data=True) + + @typing.overload + def get_data( # type: ignore + self, + cache: bool = True, + as_text: "te.Literal[False]" = False, + parse_form_data: bool = False, + ) -> bytes: + ... + + @typing.overload + def get_data( + self, + cache: bool = True, + as_text: "te.Literal[True]" = ..., + parse_form_data: bool = False, + ) -> str: + ... + + def get_data( + self, cache: bool = True, as_text: bool = False, parse_form_data: bool = False + ) -> t.Union[bytes, str]: + """This reads the buffered incoming data from the client into one + bytes object. By default this is cached but that behavior can be + changed by setting `cache` to `False`. + + Usually it's a bad idea to call this method without checking the + content length first as a client could send dozens of megabytes or more + to cause memory problems on the server. + + Note that if the form data was already parsed this method will not + return anything as form data parsing does not cache the data like + this method does. To implicitly invoke form data parsing function + set `parse_form_data` to `True`. When this is done the return value + of this method will be an empty string if the form parser handles + the data. This generally is not necessary as if the whole data is + cached (which is the default) the form parser will used the cached + data to parse the form data. Please be generally aware of checking + the content length first in any case before calling this method + to avoid exhausting server memory. + + If `as_text` is set to `True` the return value will be a decoded + string. + + .. versionadded:: 0.9 + """ + rv = getattr(self, "_cached_data", None) + if rv is None: + if parse_form_data: + self._load_form_data() + rv = self.stream.read() + if cache: + self._cached_data = rv + if as_text: + rv = rv.decode(self.charset, self.encoding_errors) + return rv + + @cached_property + def form(self) -> "ImmutableMultiDict[str, str]": + """The form parameters. By default an + :class:`~werkzeug.datastructures.ImmutableMultiDict` + is returned from this function. This can be changed by setting + :attr:`parameter_storage_class` to a different type. This might + be necessary if the order of the form data is important. + + Please keep in mind that file uploads will not end up here, but instead + in the :attr:`files` attribute. + + .. versionchanged:: 0.9 + + Previous to Werkzeug 0.9 this would only contain form data for POST + and PUT requests. + """ + self._load_form_data() + return self.form + + @cached_property + def values(self) -> "CombinedMultiDict[str, str]": + """A :class:`werkzeug.datastructures.CombinedMultiDict` that + combines :attr:`args` and :attr:`form`. + + For GET requests, only ``args`` are present, not ``form``. + + .. versionchanged:: 2.0 + For GET requests, only ``args`` are present, not ``form``. + """ + sources = [self.args] + + if self.method != "GET": + # GET requests can have a body, and some caching proxies + # might not treat that differently than a normal GET + # request, allowing form data to "invisibly" affect the + # cache without indication in the query string / URL. + sources.append(self.form) + + args = [] + + for d in sources: + if not isinstance(d, MultiDict): + d = MultiDict(d) + + args.append(d) + + return CombinedMultiDict(args) + + @cached_property + def files(self) -> "ImmutableMultiDict[str, FileStorage]": + """:class:`~werkzeug.datastructures.MultiDict` object containing + all uploaded files. Each key in :attr:`files` is the name from the + ````. Each value in :attr:`files` is a + Werkzeug :class:`~werkzeug.datastructures.FileStorage` object. + + It basically behaves like a standard file object you know from Python, + with the difference that it also has a + :meth:`~werkzeug.datastructures.FileStorage.save` function that can + store the file on the filesystem. + + Note that :attr:`files` will only contain data if the request method was + POST, PUT or PATCH and the ``

    `` that posted to the request had + ``enctype="multipart/form-data"``. It will be empty otherwise. + + See the :class:`~werkzeug.datastructures.MultiDict` / + :class:`~werkzeug.datastructures.FileStorage` documentation for + more details about the used data structure. + """ + self._load_form_data() + return self.files + + @property + def script_root(self) -> str: + """Alias for :attr:`self.root_path`. ``environ["SCRIPT_ROOT"]`` + without a trailing slash. + """ + return self.root_path + + @cached_property + def url_root(self) -> str: + """Alias for :attr:`root_url`. The URL with scheme, host, and + root path. For example, ``https://example.com/app/``. + """ + return self.root_url + + remote_user = environ_property[str]( + "REMOTE_USER", + doc="""If the server supports user authentication, and the + script is protected, this attribute contains the username the + user has authenticated as.""", + ) + is_multithread = environ_property[bool]( + "wsgi.multithread", + doc="""boolean that is `True` if the application is served by a + multithreaded WSGI server.""", + ) + is_multiprocess = environ_property[bool]( + "wsgi.multiprocess", + doc="""boolean that is `True` if the application is served by a + WSGI server that spawns multiple processes.""", + ) + is_run_once = environ_property[bool]( + "wsgi.run_once", + doc="""boolean that is `True` if the application will be + executed only once in a process lifetime. This is the case for + CGI for example, but it's not guaranteed that the execution only + happens one time.""", + ) + + # JSON + + #: A module or other object that has ``dumps`` and ``loads`` + #: functions that match the API of the built-in :mod:`json` module. + json_module = json + + @property + def json(self) -> t.Optional[t.Any]: + """The parsed JSON data if :attr:`mimetype` indicates JSON + (:mimetype:`application/json`, see :attr:`is_json`). + + Calls :meth:`get_json` with default arguments. + """ + return self.get_json() + + # Cached values for ``(silent=False, silent=True)``. Initialized + # with sentinel values. + _cached_json: t.Tuple[t.Any, t.Any] = (Ellipsis, Ellipsis) + + def get_json( + self, force: bool = False, silent: bool = False, cache: bool = True + ) -> t.Optional[t.Any]: + """Parse :attr:`data` as JSON. + + If the mimetype does not indicate JSON + (:mimetype:`application/json`, see :attr:`is_json`), this + returns ``None``. + + If parsing fails, :meth:`on_json_loading_failed` is called and + its return value is used as the return value. + + :param force: Ignore the mimetype and always try to parse JSON. + :param silent: Silence parsing errors and return ``None`` + instead. + :param cache: Store the parsed JSON to return for subsequent + calls. + """ + if cache and self._cached_json[silent] is not Ellipsis: + return self._cached_json[silent] + + if not (force or self.is_json): + return None + + data = self.get_data(cache=cache) + + try: + rv = self.json_module.loads(data) + except ValueError as e: + if silent: + rv = None + + if cache: + normal_rv, _ = self._cached_json + self._cached_json = (normal_rv, rv) + else: + rv = self.on_json_loading_failed(e) + + if cache: + _, silent_rv = self._cached_json + self._cached_json = (rv, silent_rv) + else: + if cache: + self._cached_json = (rv, rv) + + return rv + + def on_json_loading_failed(self, e: ValueError) -> t.Any: + """Called if :meth:`get_json` parsing fails and isn't silenced. + If this method returns a value, it is used as the return value + for :meth:`get_json`. The default implementation raises + :exc:`~werkzeug.exceptions.BadRequest`. + """ + raise BadRequest(f"Failed to decode JSON object: {e}") + + +class StreamOnlyMixin: + """Mixin to create a ``Request`` that disables the ``data``, + ``form``, and ``files`` properties. Only ``stream`` is available. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Create the request with + ``shallow=True`` instead. + + .. versionadded:: 0.9 + """ + + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'StreamOnlyMixin' is deprecated and will be removed in" + " Werkzeug 2.1. Create the request with 'shallow=True'" + " instead.", + DeprecationWarning, + stacklevel=2, + ) + kwargs["shallow"] = True + super().__init__(*args, **kwargs) + + +class PlainRequest(StreamOnlyMixin, Request): + """A request object without ``data``, ``form``, and ``files``. + + .. deprecated:: 2.0 + Will be removed in Werkzeug 2.1. Create the request with + ``shallow=True`` instead. + + .. versionadded:: 0.9 + """ + + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'PlainRequest' is deprecated and will be removed in" + " Werkzeug 2.1. Create the request with 'shallow=True'" + " instead.", + DeprecationWarning, + stacklevel=2, + ) + + # Don't show the DeprecationWarning for StreamOnlyMixin. + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + super().__init__(*args, **kwargs) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/response.py b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/response.py new file mode 100644 index 0000000..8378e74 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/response.py @@ -0,0 +1,891 @@ +import json +import typing +import typing as t +import warnings +from http import HTTPStatus + +from .._internal import _to_bytes +from ..datastructures import Headers +from ..http import remove_entity_headers +from ..sansio.response import Response as _SansIOResponse +from ..urls import iri_to_uri +from ..urls import url_join +from ..utils import cached_property +from ..wsgi import ClosingIterator +from ..wsgi import get_current_url +from werkzeug._internal import _get_environ +from werkzeug.http import generate_etag +from werkzeug.http import http_date +from werkzeug.http import is_resource_modified +from werkzeug.http import parse_etags +from werkzeug.http import parse_range_header +from werkzeug.wsgi import _RangeWrapper + +if t.TYPE_CHECKING: + import typing_extensions as te + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + from .request import Request + + +def _warn_if_string(iterable: t.Iterable) -> None: + """Helper for the response objects to check if the iterable returned + to the WSGI server is not a string. + """ + if isinstance(iterable, str): + warnings.warn( + "Response iterable was set to a string. This will appear to" + " work but means that the server will send the data to the" + " client one character at a time. This is almost never" + " intended behavior, use 'response.data' to assign strings" + " to the response object.", + stacklevel=2, + ) + + +def _iter_encoded( + iterable: t.Iterable[t.Union[str, bytes]], charset: str +) -> t.Iterator[bytes]: + for item in iterable: + if isinstance(item, str): + yield item.encode(charset) + else: + yield item + + +def _clean_accept_ranges(accept_ranges: t.Union[bool, str]) -> str: + if accept_ranges is True: + return "bytes" + elif accept_ranges is False: + return "none" + elif isinstance(accept_ranges, str): + return accept_ranges + raise ValueError("Invalid accept_ranges value") + + +class Response(_SansIOResponse): + """Represents an outgoing WSGI HTTP response with body, status, and + headers. Has properties and methods for using the functionality + defined by various HTTP specs. + + The response body is flexible to support different use cases. The + simple form is passing bytes, or a string which will be encoded as + UTF-8. Passing an iterable of bytes or strings makes this a + streaming response. A generator is particularly useful for building + a CSV file in memory or using SSE (Server Sent Events). A file-like + object is also iterable, although the + :func:`~werkzeug.utils.send_file` helper should be used in that + case. + + The response object is itself a WSGI application callable. When + called (:meth:`__call__`) with ``environ`` and ``start_response``, + it will pass its status and headers to ``start_response`` then + return its body as an iterable. + + .. code-block:: python + + from werkzeug.wrappers.response import Response + + def index(): + return Response("Hello, World!") + + def application(environ, start_response): + path = environ.get("PATH_INFO") or "/" + + if path == "/": + response = index() + else: + response = Response("Not Found", status=404) + + return response(environ, start_response) + + :param response: The data for the body of the response. A string or + bytes, or tuple or list of strings or bytes, for a fixed-length + response, or any other iterable of strings or bytes for a + streaming response. Defaults to an empty body. + :param status: The status code for the response. Either an int, in + which case the default status message is added, or a string in + the form ``{code} {message}``, like ``404 Not Found``. Defaults + to 200. + :param headers: A :class:`~werkzeug.datastructures.Headers` object, + or a list of ``(key, value)`` tuples that will be converted to a + ``Headers`` object. + :param mimetype: The mime type (content type without charset or + other parameters) of the response. If the value starts with + ``text/`` (or matches some other special cases), the charset + will be added to create the ``content_type``. + :param content_type: The full content type of the response. + Overrides building the value from ``mimetype``. + :param direct_passthrough: Pass the response body directly through + as the WSGI iterable. This can be used when the body is a binary + file or other iterator of bytes, to skip some unnecessary + checks. Use :func:`~werkzeug.utils.send_file` instead of setting + this manually. + + .. versionchanged:: 2.0 + Combine ``BaseResponse`` and mixins into a single ``Response`` + class. Using the old classes is deprecated and will be removed + in Werkzeug 2.1. + + .. versionchanged:: 0.5 + The ``direct_passthrough`` parameter was added. + """ + + #: if set to `False` accessing properties on the response object will + #: not try to consume the response iterator and convert it into a list. + #: + #: .. versionadded:: 0.6.2 + #: + #: That attribute was previously called `implicit_seqence_conversion`. + #: (Notice the typo). If you did use this feature, you have to adapt + #: your code to the name change. + implicit_sequence_conversion = True + + #: Should this response object correct the location header to be RFC + #: conformant? This is true by default. + #: + #: .. versionadded:: 0.8 + autocorrect_location_header = True + + #: Should this response object automatically set the content-length + #: header if possible? This is true by default. + #: + #: .. versionadded:: 0.8 + automatically_set_content_length = True + + #: The response body to send as the WSGI iterable. A list of strings + #: or bytes represents a fixed-length response, any other iterable + #: is a streaming response. Strings are encoded to bytes as UTF-8. + #: + #: Do not set to a plain string or bytes, that will cause sending + #: the response to be very inefficient as it will iterate one byte + #: at a time. + response: t.Union[t.Iterable[str], t.Iterable[bytes]] + + def __init__( + self, + response: t.Optional[ + t.Union[t.Iterable[bytes], bytes, t.Iterable[str], str] + ] = None, + status: t.Optional[t.Union[int, str, HTTPStatus]] = None, + headers: t.Optional[ + t.Union[ + t.Mapping[str, t.Union[str, int, t.Iterable[t.Union[str, int]]]], + t.Iterable[t.Tuple[str, t.Union[str, int]]], + ] + ] = None, + mimetype: t.Optional[str] = None, + content_type: t.Optional[str] = None, + direct_passthrough: bool = False, + ) -> None: + super().__init__( + status=status, + headers=headers, + mimetype=mimetype, + content_type=content_type, + ) + + #: Pass the response body directly through as the WSGI iterable. + #: This can be used when the body is a binary file or other + #: iterator of bytes, to skip some unnecessary checks. Use + #: :func:`~werkzeug.utils.send_file` instead of setting this + #: manually. + self.direct_passthrough = direct_passthrough + self._on_close: t.List[t.Callable[[], t.Any]] = [] + + # we set the response after the headers so that if a class changes + # the charset attribute, the data is set in the correct charset. + if response is None: + self.response = [] + elif isinstance(response, (str, bytes, bytearray)): + self.set_data(response) + else: + self.response = response + + def call_on_close(self, func: t.Callable[[], t.Any]) -> t.Callable[[], t.Any]: + """Adds a function to the internal list of functions that should + be called as part of closing down the response. Since 0.7 this + function also returns the function that was passed so that this + can be used as a decorator. + + .. versionadded:: 0.6 + """ + self._on_close.append(func) + return func + + def __repr__(self) -> str: + if self.is_sequence: + body_info = f"{sum(map(len, self.iter_encoded()))} bytes" + else: + body_info = "streamed" if self.is_streamed else "likely-streamed" + return f"<{type(self).__name__} {body_info} [{self.status}]>" + + @classmethod + def force_type( + cls, response: "Response", environ: t.Optional["WSGIEnvironment"] = None + ) -> "Response": + """Enforce that the WSGI response is a response object of the current + type. Werkzeug will use the :class:`Response` internally in many + situations like the exceptions. If you call :meth:`get_response` on an + exception you will get back a regular :class:`Response` object, even + if you are using a custom subclass. + + This method can enforce a given response type, and it will also + convert arbitrary WSGI callables into response objects if an environ + is provided:: + + # convert a Werkzeug response object into an instance of the + # MyResponseClass subclass. + response = MyResponseClass.force_type(response) + + # convert any WSGI application into a response object + response = MyResponseClass.force_type(response, environ) + + This is especially useful if you want to post-process responses in + the main dispatcher and use functionality provided by your subclass. + + Keep in mind that this will modify response objects in place if + possible! + + :param response: a response object or wsgi application. + :param environ: a WSGI environment object. + :return: a response object. + """ + if not isinstance(response, Response): + if environ is None: + raise TypeError( + "cannot convert WSGI application into response" + " objects without an environ" + ) + + from ..test import run_wsgi_app + + response = Response(*run_wsgi_app(response, environ)) + + response.__class__ = cls + return response + + @classmethod + def from_app( + cls, app: "WSGIApplication", environ: "WSGIEnvironment", buffered: bool = False + ) -> "Response": + """Create a new response object from an application output. This + works best if you pass it an application that returns a generator all + the time. Sometimes applications may use the `write()` callable + returned by the `start_response` function. This tries to resolve such + edge cases automatically. But if you don't get the expected output + you should set `buffered` to `True` which enforces buffering. + + :param app: the WSGI application to execute. + :param environ: the WSGI environment to execute against. + :param buffered: set to `True` to enforce buffering. + :return: a response object. + """ + from ..test import run_wsgi_app + + return cls(*run_wsgi_app(app, environ, buffered)) + + @typing.overload + def get_data(self, as_text: "te.Literal[False]" = False) -> bytes: + ... + + @typing.overload + def get_data(self, as_text: "te.Literal[True]") -> str: + ... + + def get_data(self, as_text: bool = False) -> t.Union[bytes, str]: + """The string representation of the response body. Whenever you call + this property the response iterable is encoded and flattened. This + can lead to unwanted behavior if you stream big data. + + This behavior can be disabled by setting + :attr:`implicit_sequence_conversion` to `False`. + + If `as_text` is set to `True` the return value will be a decoded + string. + + .. versionadded:: 0.9 + """ + self._ensure_sequence() + rv = b"".join(self.iter_encoded()) + + if as_text: + return rv.decode(self.charset) + + return rv + + def set_data(self, value: t.Union[bytes, str]) -> None: + """Sets a new string as response. The value must be a string or + bytes. If a string is set it's encoded to the charset of the + response (utf-8 by default). + + .. versionadded:: 0.9 + """ + # if a string is set, it's encoded directly so that we + # can set the content length + if isinstance(value, str): + value = value.encode(self.charset) + else: + value = bytes(value) + self.response = [value] + if self.automatically_set_content_length: + self.headers["Content-Length"] = str(len(value)) + + data = property( + get_data, + set_data, + doc="A descriptor that calls :meth:`get_data` and :meth:`set_data`.", + ) + + def calculate_content_length(self) -> t.Optional[int]: + """Returns the content length if available or `None` otherwise.""" + try: + self._ensure_sequence() + except RuntimeError: + return None + return sum(len(x) for x in self.iter_encoded()) + + def _ensure_sequence(self, mutable: bool = False) -> None: + """This method can be called by methods that need a sequence. If + `mutable` is true, it will also ensure that the response sequence + is a standard Python list. + + .. versionadded:: 0.6 + """ + if self.is_sequence: + # if we need a mutable object, we ensure it's a list. + if mutable and not isinstance(self.response, list): + self.response = list(self.response) # type: ignore + return + if self.direct_passthrough: + raise RuntimeError( + "Attempted implicit sequence conversion but the" + " response object is in direct passthrough mode." + ) + if not self.implicit_sequence_conversion: + raise RuntimeError( + "The response object required the iterable to be a" + " sequence, but the implicit conversion was disabled." + " Call make_sequence() yourself." + ) + self.make_sequence() + + def make_sequence(self) -> None: + """Converts the response iterator in a list. By default this happens + automatically if required. If `implicit_sequence_conversion` is + disabled, this method is not automatically called and some properties + might raise exceptions. This also encodes all the items. + + .. versionadded:: 0.6 + """ + if not self.is_sequence: + # if we consume an iterable we have to ensure that the close + # method of the iterable is called if available when we tear + # down the response + close = getattr(self.response, "close", None) + self.response = list(self.iter_encoded()) + if close is not None: + self.call_on_close(close) + + def iter_encoded(self) -> t.Iterator[bytes]: + """Iter the response encoded with the encoding of the response. + If the response object is invoked as WSGI application the return + value of this method is used as application iterator unless + :attr:`direct_passthrough` was activated. + """ + if __debug__: + _warn_if_string(self.response) + # Encode in a separate function so that self.response is fetched + # early. This allows us to wrap the response with the return + # value from get_app_iter or iter_encoded. + return _iter_encoded(self.response, self.charset) + + @property + def is_streamed(self) -> bool: + """If the response is streamed (the response is not an iterable with + a length information) this property is `True`. In this case streamed + means that there is no information about the number of iterations. + This is usually `True` if a generator is passed to the response object. + + This is useful for checking before applying some sort of post + filtering that should not take place for streamed responses. + """ + try: + len(self.response) # type: ignore + except (TypeError, AttributeError): + return True + return False + + @property + def is_sequence(self) -> bool: + """If the iterator is buffered, this property will be `True`. A + response object will consider an iterator to be buffered if the + response attribute is a list or tuple. + + .. versionadded:: 0.6 + """ + return isinstance(self.response, (tuple, list)) + + def close(self) -> None: + """Close the wrapped response if possible. You can also use the object + in a with statement which will automatically close it. + + .. versionadded:: 0.9 + Can now be used in a with statement. + """ + if hasattr(self.response, "close"): + self.response.close() # type: ignore + for func in self._on_close: + func() + + def __enter__(self) -> "Response": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.close() + + def freeze(self, no_etag: None = None) -> None: + """Make the response object ready to be pickled. Does the + following: + + * Buffer the response into a list, ignoring + :attr:`implicity_sequence_conversion` and + :attr:`direct_passthrough`. + * Set the ``Content-Length`` header. + * Generate an ``ETag`` header if one is not already set. + + .. versionchanged:: 2.0 + An ``ETag`` header is added, the ``no_etag`` parameter is + deprecated and will be removed in Werkzeug 2.1. + + .. versionchanged:: 0.6 + The ``Content-Length`` header is set. + """ + # Always freeze the encoded response body, ignore + # implicit_sequence_conversion and direct_passthrough. + self.response = list(self.iter_encoded()) + self.headers["Content-Length"] = str(sum(map(len, self.response))) + + if no_etag is not None: + warnings.warn( + "The 'no_etag' parameter is deprecated and will be" + " removed in Werkzeug 2.1.", + DeprecationWarning, + stacklevel=2, + ) + + self.add_etag() + + def get_wsgi_headers(self, environ: "WSGIEnvironment") -> Headers: + """This is automatically called right before the response is started + and returns headers modified for the given environment. It returns a + copy of the headers from the response with some modifications applied + if necessary. + + For example the location header (if present) is joined with the root + URL of the environment. Also the content length is automatically set + to zero here for certain status codes. + + .. versionchanged:: 0.6 + Previously that function was called `fix_headers` and modified + the response object in place. Also since 0.6, IRIs in location + and content-location headers are handled properly. + + Also starting with 0.6, Werkzeug will attempt to set the content + length if it is able to figure it out on its own. This is the + case if all the strings in the response iterable are already + encoded and the iterable is buffered. + + :param environ: the WSGI environment of the request. + :return: returns a new :class:`~werkzeug.datastructures.Headers` + object. + """ + headers = Headers(self.headers) + location: t.Optional[str] = None + content_location: t.Optional[str] = None + content_length: t.Optional[t.Union[str, int]] = None + status = self.status_code + + # iterate over the headers to find all values in one go. Because + # get_wsgi_headers is used each response that gives us a tiny + # speedup. + for key, value in headers: + ikey = key.lower() + if ikey == "location": + location = value + elif ikey == "content-location": + content_location = value + elif ikey == "content-length": + content_length = value + + # make sure the location header is an absolute URL + if location is not None: + old_location = location + if isinstance(location, str): + # Safe conversion is necessary here as we might redirect + # to a broken URI scheme (for instance itms-services). + location = iri_to_uri(location, safe_conversion=True) + + if self.autocorrect_location_header: + current_url = get_current_url(environ, strip_querystring=True) + if isinstance(current_url, str): + current_url = iri_to_uri(current_url) + location = url_join(current_url, location) + if location != old_location: + headers["Location"] = location + + # make sure the content location is a URL + if content_location is not None and isinstance(content_location, str): + headers["Content-Location"] = iri_to_uri(content_location) + + if 100 <= status < 200 or status == 204: + # Per section 3.3.2 of RFC 7230, "a server MUST NOT send a + # Content-Length header field in any response with a status + # code of 1xx (Informational) or 204 (No Content)." + headers.remove("Content-Length") + elif status == 304: + remove_entity_headers(headers) + + # if we can determine the content length automatically, we + # should try to do that. But only if this does not involve + # flattening the iterator or encoding of strings in the + # response. We however should not do that if we have a 304 + # response. + if ( + self.automatically_set_content_length + and self.is_sequence + and content_length is None + and status not in (204, 304) + and not (100 <= status < 200) + ): + try: + content_length = sum(len(_to_bytes(x, "ascii")) for x in self.response) + except UnicodeError: + # Something other than bytes, can't safely figure out + # the length of the response. + pass + else: + headers["Content-Length"] = str(content_length) + + return headers + + def get_app_iter(self, environ: "WSGIEnvironment") -> t.Iterable[bytes]: + """Returns the application iterator for the given environ. Depending + on the request method and the current status code the return value + might be an empty response rather than the one from the response. + + If the request method is `HEAD` or the status code is in a range + where the HTTP specification requires an empty response, an empty + iterable is returned. + + .. versionadded:: 0.6 + + :param environ: the WSGI environment of the request. + :return: a response iterable. + """ + status = self.status_code + if ( + environ["REQUEST_METHOD"] == "HEAD" + or 100 <= status < 200 + or status in (204, 304) + ): + iterable: t.Iterable[bytes] = () + elif self.direct_passthrough: + if __debug__: + _warn_if_string(self.response) + return self.response # type: ignore + else: + iterable = self.iter_encoded() + return ClosingIterator(iterable, self.close) + + def get_wsgi_response( + self, environ: "WSGIEnvironment" + ) -> t.Tuple[t.Iterable[bytes], str, t.List[t.Tuple[str, str]]]: + """Returns the final WSGI response as tuple. The first item in + the tuple is the application iterator, the second the status and + the third the list of headers. The response returned is created + specially for the given environment. For example if the request + method in the WSGI environment is ``'HEAD'`` the response will + be empty and only the headers and status code will be present. + + .. versionadded:: 0.6 + + :param environ: the WSGI environment of the request. + :return: an ``(app_iter, status, headers)`` tuple. + """ + headers = self.get_wsgi_headers(environ) + app_iter = self.get_app_iter(environ) + return app_iter, self.status, headers.to_wsgi_list() + + def __call__( + self, environ: "WSGIEnvironment", start_response: "StartResponse" + ) -> t.Iterable[bytes]: + """Process this response as WSGI application. + + :param environ: the WSGI environment. + :param start_response: the response callable provided by the WSGI + server. + :return: an application iterator + """ + app_iter, status, headers = self.get_wsgi_response(environ) + start_response(status, headers) + return app_iter + + # JSON + + #: A module or other object that has ``dumps`` and ``loads`` + #: functions that match the API of the built-in :mod:`json` module. + json_module = json + + @property + def json(self) -> t.Optional[t.Any]: + """The parsed JSON data if :attr:`mimetype` indicates JSON + (:mimetype:`application/json`, see :attr:`is_json`). + + Calls :meth:`get_json` with default arguments. + """ + return self.get_json() + + def get_json(self, force: bool = False, silent: bool = False) -> t.Optional[t.Any]: + """Parse :attr:`data` as JSON. Useful during testing. + + If the mimetype does not indicate JSON + (:mimetype:`application/json`, see :attr:`is_json`), this + returns ``None``. + + Unlike :meth:`Request.get_json`, the result is not cached. + + :param force: Ignore the mimetype and always try to parse JSON. + :param silent: Silence parsing errors and return ``None`` + instead. + """ + if not (force or self.is_json): + return None + + data = self.get_data() + + try: + return self.json_module.loads(data) + except ValueError: + if not silent: + raise + + return None + + # Stream + + @cached_property + def stream(self) -> "ResponseStream": + """The response iterable as write-only stream.""" + return ResponseStream(self) + + def _wrap_range_response(self, start: int, length: int) -> None: + """Wrap existing Response in case of Range Request context.""" + if self.status_code == 206: + self.response = _RangeWrapper(self.response, start, length) # type: ignore + + def _is_range_request_processable(self, environ: "WSGIEnvironment") -> bool: + """Return ``True`` if `Range` header is present and if underlying + resource is considered unchanged when compared with `If-Range` header. + """ + return ( + "HTTP_IF_RANGE" not in environ + or not is_resource_modified( + environ, + self.headers.get("etag"), + None, + self.headers.get("last-modified"), + ignore_if_range=False, + ) + ) and "HTTP_RANGE" in environ + + def _process_range_request( + self, + environ: "WSGIEnvironment", + complete_length: t.Optional[int] = None, + accept_ranges: t.Optional[t.Union[bool, str]] = None, + ) -> bool: + """Handle Range Request related headers (RFC7233). If `Accept-Ranges` + header is valid, and Range Request is processable, we set the headers + as described by the RFC, and wrap the underlying response in a + RangeWrapper. + + Returns ``True`` if Range Request can be fulfilled, ``False`` otherwise. + + :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable` + if `Range` header could not be parsed or satisfied. + + .. versionchanged:: 2.0 + Returns ``False`` if the length is 0. + """ + from ..exceptions import RequestedRangeNotSatisfiable + + if ( + accept_ranges is None + or complete_length is None + or complete_length == 0 + or not self._is_range_request_processable(environ) + ): + return False + + parsed_range = parse_range_header(environ.get("HTTP_RANGE")) + + if parsed_range is None: + raise RequestedRangeNotSatisfiable(complete_length) + + range_tuple = parsed_range.range_for_length(complete_length) + content_range_header = parsed_range.to_content_range_header(complete_length) + + if range_tuple is None or content_range_header is None: + raise RequestedRangeNotSatisfiable(complete_length) + + content_length = range_tuple[1] - range_tuple[0] + self.headers["Content-Length"] = content_length + self.headers["Accept-Ranges"] = accept_ranges + self.content_range = content_range_header # type: ignore + self.status_code = 206 + self._wrap_range_response(range_tuple[0], content_length) + return True + + def make_conditional( + self, + request_or_environ: t.Union["WSGIEnvironment", "Request"], + accept_ranges: t.Union[bool, str] = False, + complete_length: t.Optional[int] = None, + ) -> "Response": + """Make the response conditional to the request. This method works + best if an etag was defined for the response already. The `add_etag` + method can be used to do that. If called without etag just the date + header is set. + + This does nothing if the request method in the request or environ is + anything but GET or HEAD. + + For optimal performance when handling range requests, it's recommended + that your response data object implements `seekable`, `seek` and `tell` + methods as described by :py:class:`io.IOBase`. Objects returned by + :meth:`~werkzeug.wsgi.wrap_file` automatically implement those methods. + + It does not remove the body of the response because that's something + the :meth:`__call__` function does for us automatically. + + Returns self so that you can do ``return resp.make_conditional(req)`` + but modifies the object in-place. + + :param request_or_environ: a request object or WSGI environment to be + used to make the response conditional + against. + :param accept_ranges: This parameter dictates the value of + `Accept-Ranges` header. If ``False`` (default), + the header is not set. If ``True``, it will be set + to ``"bytes"``. If ``None``, it will be set to + ``"none"``. If it's a string, it will use this + value. + :param complete_length: Will be used only in valid Range Requests. + It will set `Content-Range` complete length + value and compute `Content-Length` real value. + This parameter is mandatory for successful + Range Requests completion. + :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable` + if `Range` header could not be parsed or satisfied. + + .. versionchanged:: 2.0 + Range processing is skipped if length is 0 instead of + raising a 416 Range Not Satisfiable error. + """ + environ = _get_environ(request_or_environ) + if environ["REQUEST_METHOD"] in ("GET", "HEAD"): + # if the date is not in the headers, add it now. We however + # will not override an already existing header. Unfortunately + # this header will be overriden by many WSGI servers including + # wsgiref. + if "date" not in self.headers: + self.headers["Date"] = http_date() + accept_ranges = _clean_accept_ranges(accept_ranges) + is206 = self._process_range_request(environ, complete_length, accept_ranges) + if not is206 and not is_resource_modified( + environ, + self.headers.get("etag"), + None, + self.headers.get("last-modified"), + ): + if parse_etags(environ.get("HTTP_IF_MATCH")): + self.status_code = 412 + else: + self.status_code = 304 + if ( + self.automatically_set_content_length + and "content-length" not in self.headers + ): + length = self.calculate_content_length() + if length is not None: + self.headers["Content-Length"] = length + return self + + def add_etag(self, overwrite: bool = False, weak: bool = False) -> None: + """Add an etag for the current response if there is none yet. + + .. versionchanged:: 2.0 + SHA-1 is used to generate the value. MD5 may not be + available in some environments. + """ + if overwrite or "etag" not in self.headers: + self.set_etag(generate_etag(self.get_data()), weak) + + +class ResponseStream: + """A file descriptor like object used by the :class:`ResponseStreamMixin` to + represent the body of the stream. It directly pushes into the response + iterable of the response object. + """ + + mode = "wb+" + + def __init__(self, response: Response): + self.response = response + self.closed = False + + def write(self, value: bytes) -> int: + if self.closed: + raise ValueError("I/O operation on closed file") + self.response._ensure_sequence(mutable=True) + self.response.response.append(value) # type: ignore + self.response.headers.pop("Content-Length", None) + return len(value) + + def writelines(self, seq: t.Iterable[bytes]) -> None: + for item in seq: + self.write(item) + + def close(self) -> None: + self.closed = True + + def flush(self) -> None: + if self.closed: + raise ValueError("I/O operation on closed file") + + def isatty(self) -> bool: + if self.closed: + raise ValueError("I/O operation on closed file") + return False + + def tell(self) -> int: + self.response._ensure_sequence() + return sum(map(len, self.response.response)) + + @property + def encoding(self) -> str: + return self.response.charset + + +class ResponseStreamMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'ResponseStreamMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Response' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/user_agent.py b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/user_agent.py new file mode 100644 index 0000000..e69ab05 --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/wrappers/user_agent.py @@ -0,0 +1,14 @@ +import typing as t +import warnings + + +class UserAgentMixin: + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + warnings.warn( + "'UserAgentMixin' is deprecated and will be removed in" + " Werkzeug 2.1. 'Request' now includes the functionality" + " directly.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(*args, **kwargs) diff --git a/myvenv/lib/python3.10/site-packages/werkzeug/wsgi.py b/myvenv/lib/python3.10/site-packages/werkzeug/wsgi.py new file mode 100644 index 0000000..9cfa74d --- /dev/null +++ b/myvenv/lib/python3.10/site-packages/werkzeug/wsgi.py @@ -0,0 +1,982 @@ +import io +import re +import typing as t +from functools import partial +from functools import update_wrapper +from itertools import chain + +from ._internal import _make_encode_wrapper +from ._internal import _to_bytes +from ._internal import _to_str +from .sansio import utils as _sansio_utils +from .sansio.utils import host_is_trusted # noqa: F401 # Imported as part of API +from .urls import _URLTuple +from .urls import uri_to_iri +from .urls import url_join +from .urls import url_parse +from .urls import url_quote + +if t.TYPE_CHECKING: + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + +def responder(f: t.Callable[..., "WSGIApplication"]) -> "WSGIApplication": + """Marks a function as responder. Decorate a function with it and it + will automatically call the return value as WSGI application. + + Example:: + + @responder + def application(environ, start_response): + return Response('Hello World!') + """ + return update_wrapper(lambda *a: f(*a)(*a[-2:]), f) + + +def get_current_url( + environ: "WSGIEnvironment", + root_only: bool = False, + strip_querystring: bool = False, + host_only: bool = False, + trusted_hosts: t.Optional[t.Iterable[str]] = None, +) -> str: + """Recreate the URL for a request from the parts in a WSGI + environment. + + The URL is an IRI, not a URI, so it may contain Unicode characters. + Use :func:`~werkzeug.urls.iri_to_uri` to convert it to ASCII. + + :param environ: The WSGI environment to get the URL parts from. + :param root_only: Only build the root path, don't include the + remaining path or query string. + :param strip_querystring: Don't include the query string. + :param host_only: Only build the scheme and host. + :param trusted_hosts: A list of trusted host names to validate the + host against. + """ + parts = { + "scheme": environ["wsgi.url_scheme"], + "host": get_host(environ, trusted_hosts), + } + + if not host_only: + parts["root_path"] = environ.get("SCRIPT_NAME", "") + + if not root_only: + parts["path"] = environ.get("PATH_INFO", "") + + if not strip_querystring: + parts["query_string"] = environ.get("QUERY_STRING", "").encode("latin1") + + return _sansio_utils.get_current_url(**parts) + + +def _get_server( + environ: "WSGIEnvironment", +) -> t.Optional[t.Tuple[str, t.Optional[int]]]: + name = environ.get("SERVER_NAME") + + if name is None: + return None + + try: + port: t.Optional[int] = int(environ.get("SERVER_PORT", None)) + except (TypeError, ValueError): + # unix socket + port = None + + return name, port + + +def get_host( + environ: "WSGIEnvironment", trusted_hosts: t.Optional[t.Iterable[str]] = None +) -> str: + """Return the host for the given WSGI environment. + + The ``Host`` header is preferred, then ``SERVER_NAME`` if it's not + set. The returned host will only contain the port if it is different + than the standard port for the protocol. + + Optionally, verify that the host is trusted using + :func:`host_is_trusted` and raise a + :exc:`~werkzeug.exceptions.SecurityError` if it is not. + + :param environ: A WSGI environment dict. + :param trusted_hosts: A list of trusted host names. + + :return: Host, with port if necessary. + :raise ~werkzeug.exceptions.SecurityError: If the host is not + trusted. + """ + return _sansio_utils.get_host( + environ["wsgi.url_scheme"], + environ.get("HTTP_HOST"), + _get_server(environ), + trusted_hosts, + ) + + +def get_content_length(environ: "WSGIEnvironment") -> t.Optional[int]: + """Returns the content length from the WSGI environment as + integer. If it's not available or chunked transfer encoding is used, + ``None`` is returned. + + .. versionadded:: 0.9 + + :param environ: the WSGI environ to fetch the content length from. + """ + if environ.get("HTTP_TRANSFER_ENCODING", "") == "chunked": + return None + + content_length = environ.get("CONTENT_LENGTH") + if content_length is not None: + try: + return max(0, int(content_length)) + except (ValueError, TypeError): + pass + return None + + +def get_input_stream( + environ: "WSGIEnvironment", safe_fallback: bool = True +) -> t.IO[bytes]: + """Returns the input stream from the WSGI environment and wraps it + in the most sensible way possible. The stream returned is not the + raw WSGI stream in most cases but one that is safe to read from + without taking into account the content length. + + If content length is not set, the stream will be empty for safety reasons. + If the WSGI server supports chunked or infinite streams, it should set + the ``wsgi.input_terminated`` value in the WSGI environ to indicate that. + + .. versionadded:: 0.9 + + :param environ: the WSGI environ to fetch the stream from. + :param safe_fallback: use an empty stream as a safe fallback when the + content length is not set. Disabling this allows infinite streams, + which can be a denial-of-service risk. + """ + stream = t.cast(t.IO[bytes], environ["wsgi.input"]) + content_length = get_content_length(environ) + + # A wsgi extension that tells us if the input is terminated. In + # that case we return the stream unchanged as we know we can safely + # read it until the end. + if environ.get("wsgi.input_terminated"): + return stream + + # If the request doesn't specify a content length, returning the stream is + # potentially dangerous because it could be infinite, malicious or not. If + # safe_fallback is true, return an empty stream instead for safety. + if content_length is None: + return io.BytesIO() if safe_fallback else stream + + # Otherwise limit the stream to the content length + return t.cast(t.IO[bytes], LimitedStream(stream, content_length)) + + +def get_query_string(environ: "WSGIEnvironment") -> str: + """Returns the ``QUERY_STRING`` from the WSGI environment. This also + takes care of the WSGI decoding dance. The string returned will be + restricted to ASCII characters. + + :param environ: WSGI environment to get the query string from. + + .. versionadded:: 0.9 + """ + qs = environ.get("QUERY_STRING", "").encode("latin1") + # QUERY_STRING really should be ascii safe but some browsers + # will send us some unicode stuff (I am looking at you IE). + # In that case we want to urllib quote it badly. + return url_quote(qs, safe=":&%=+$!*'(),") + + +def get_path_info( + environ: "WSGIEnvironment", charset: str = "utf-8", errors: str = "replace" +) -> str: + """Return the ``PATH_INFO`` from the WSGI environment and decode it + unless ``charset`` is ``None``. + + :param environ: WSGI environment to get the path from. + :param charset: The charset for the path info, or ``None`` if no + decoding should be performed. + :param errors: The decoding error handling. + + .. versionadded:: 0.9 + """ + path = environ.get("PATH_INFO", "").encode("latin1") + return _to_str(path, charset, errors, allow_none_charset=True) # type: ignore + + +def get_script_name( + environ: "WSGIEnvironment", charset: str = "utf-8", errors: str = "replace" +) -> str: + """Return the ``SCRIPT_NAME`` from the WSGI environment and decode + it unless `charset` is set to ``None``. + + :param environ: WSGI environment to get the path from. + :param charset: The charset for the path, or ``None`` if no decoding + should be performed. + :param errors: The decoding error handling. + + .. versionadded:: 0.9 + """ + path = environ.get("SCRIPT_NAME", "").encode("latin1") + return _to_str(path, charset, errors, allow_none_charset=True) # type: ignore + + +def pop_path_info( + environ: "WSGIEnvironment", charset: str = "utf-8", errors: str = "replace" +) -> t.Optional[str]: + """Removes and returns the next segment of `PATH_INFO`, pushing it onto + `SCRIPT_NAME`. Returns `None` if there is nothing left on `PATH_INFO`. + + If the `charset` is set to `None` bytes are returned. + + If there are empty segments (``'/foo//bar``) these are ignored but + properly pushed to the `SCRIPT_NAME`: + + >>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'} + >>> pop_path_info(env) + 'a' + >>> env['SCRIPT_NAME'] + '/foo/a' + >>> pop_path_info(env) + 'b' + >>> env['SCRIPT_NAME'] + '/foo/a/b' + + .. versionadded:: 0.5 + + .. versionchanged:: 0.9 + The path is now decoded and a charset and encoding + parameter can be provided. + + :param environ: the WSGI environment that is modified. + :param charset: The ``encoding`` parameter passed to + :func:`bytes.decode`. + :param errors: The ``errors`` paramater passed to + :func:`bytes.decode`. + """ + path = environ.get("PATH_INFO") + if not path: + return None + + script_name = environ.get("SCRIPT_NAME", "") + + # shift multiple leading slashes over + old_path = path + path = path.lstrip("/") + if path != old_path: + script_name += "/" * (len(old_path) - len(path)) + + if "/" not in path: + environ["PATH_INFO"] = "" + environ["SCRIPT_NAME"] = script_name + path + rv = path.encode("latin1") + else: + segment, path = path.split("/", 1) + environ["PATH_INFO"] = f"/{path}" + environ["SCRIPT_NAME"] = script_name + segment + rv = segment.encode("latin1") + + return _to_str(rv, charset, errors, allow_none_charset=True) # type: ignore + + +def peek_path_info( + environ: "WSGIEnvironment", charset: str = "utf-8", errors: str = "replace" +) -> t.Optional[str]: + """Returns the next segment on the `PATH_INFO` or `None` if there + is none. Works like :func:`pop_path_info` without modifying the + environment: + + >>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'} + >>> peek_path_info(env) + 'a' + >>> peek_path_info(env) + 'a' + + If the `charset` is set to `None` bytes are returned. + + .. versionadded:: 0.5 + + .. versionchanged:: 0.9 + The path is now decoded and a charset and encoding + parameter can be provided. + + :param environ: the WSGI environment that is checked. + """ + segments = environ.get("PATH_INFO", "").lstrip("/").split("/", 1) + if segments: + return _to_str( # type: ignore + segments[0].encode("latin1"), charset, errors, allow_none_charset=True + ) + return None + + +def extract_path_info( + environ_or_baseurl: t.Union[str, "WSGIEnvironment"], + path_or_url: t.Union[str, _URLTuple], + charset: str = "utf-8", + errors: str = "werkzeug.url_quote", + collapse_http_schemes: bool = True, +) -> t.Optional[str]: + """Extracts the path info from the given URL (or WSGI environment) and + path. The path info returned is a string. The URLs might also be IRIs. + + If the path info could not be determined, `None` is returned. + + Some examples: + + >>> extract_path_info('http://example.com/app', '/app/hello') + '/hello' + >>> extract_path_info('http://example.com/app', + ... 'https://example.com/app/hello') + '/hello' + >>> extract_path_info('http://example.com/app', + ... 'https://example.com/app/hello', + ... collapse_http_schemes=False) is None + True + + Instead of providing a base URL you can also pass a WSGI environment. + + :param environ_or_baseurl: a WSGI environment dict, a base URL or + base IRI. This is the root of the + application. + :param path_or_url: an absolute path from the server root, a + relative path (in which case it's the path info) + or a full URL. + :param charset: the charset for byte data in URLs + :param errors: the error handling on decode + :param collapse_http_schemes: if set to `False` the algorithm does + not assume that http and https on the + same server point to the same + resource. + + .. versionchanged:: 0.15 + The ``errors`` parameter defaults to leaving invalid bytes + quoted instead of replacing them. + + .. versionadded:: 0.6 + """ + + def _normalize_netloc(scheme: str, netloc: str) -> str: + parts = netloc.split("@", 1)[-1].split(":", 1) + port: t.Optional[str] + + if len(parts) == 2: + netloc, port = parts + if (scheme == "http" and port == "80") or ( + scheme == "https" and port == "443" + ): + port = None + else: + netloc = parts[0] + port = None + + if port is not None: + netloc += f":{port}" + + return netloc + + # make sure whatever we are working on is a IRI and parse it + path = uri_to_iri(path_or_url, charset, errors) + if isinstance(environ_or_baseurl, dict): + environ_or_baseurl = get_current_url(environ_or_baseurl, root_only=True) + base_iri = uri_to_iri(environ_or_baseurl, charset, errors) + base_scheme, base_netloc, base_path = url_parse(base_iri)[:3] + cur_scheme, cur_netloc, cur_path = url_parse(url_join(base_iri, path))[:3] + + # normalize the network location + base_netloc = _normalize_netloc(base_scheme, base_netloc) + cur_netloc = _normalize_netloc(cur_scheme, cur_netloc) + + # is that IRI even on a known HTTP scheme? + if collapse_http_schemes: + for scheme in base_scheme, cur_scheme: + if scheme not in ("http", "https"): + return None + else: + if not (base_scheme in ("http", "https") and base_scheme == cur_scheme): + return None + + # are the netlocs compatible? + if base_netloc != cur_netloc: + return None + + # are we below the application path? + base_path = base_path.rstrip("/") + if not cur_path.startswith(base_path): + return None + + return f"/{cur_path[len(base_path) :].lstrip('/')}" + + +class ClosingIterator: + """The WSGI specification requires that all middlewares and gateways + respect the `close` callback of the iterable returned by the application. + Because it is useful to add another close action to a returned iterable + and adding a custom iterable is a boring task this class can be used for + that:: + + return ClosingIterator(app(environ, start_response), [cleanup_session, + cleanup_locals]) + + If there is just one close function it can be passed instead of the list. + + A closing iterator is not needed if the application uses response objects + and finishes the processing if the response is started:: + + try: + return response(environ, start_response) + finally: + cleanup_session() + cleanup_locals() + """ + + def __init__( + self, + iterable: t.Iterable[bytes], + callbacks: t.Optional[ + t.Union[t.Callable[[], None], t.Iterable[t.Callable[[], None]]] + ] = None, + ) -> None: + iterator = iter(iterable) + self._next = t.cast(t.Callable[[], bytes], partial(next, iterator)) + if callbacks is None: + callbacks = [] + elif callable(callbacks): + callbacks = [callbacks] + else: + callbacks = list(callbacks) + iterable_close = getattr(iterable, "close", None) + if iterable_close: + callbacks.insert(0, iterable_close) + self._callbacks = callbacks + + def __iter__(self) -> "ClosingIterator": + return self + + def __next__(self) -> bytes: + return self._next() + + def close(self) -> None: + for callback in self._callbacks: + callback() + + +def wrap_file( + environ: "WSGIEnvironment", file: t.IO[bytes], buffer_size: int = 8192 +) -> t.Iterable[bytes]: + """Wraps a file. This uses the WSGI server's file wrapper if available + or otherwise the generic :class:`FileWrapper`. + + .. versionadded:: 0.5 + + If the file wrapper from the WSGI server is used it's important to not + iterate over it from inside the application but to pass it through + unchanged. If you want to pass out a file wrapper inside a response + object you have to set :attr:`Response.direct_passthrough` to `True`. + + More information about file wrappers are available in :pep:`333`. + + :param file: a :class:`file`-like object with a :meth:`~file.read` method. + :param buffer_size: number of bytes for one iteration. + """ + return environ.get("wsgi.file_wrapper", FileWrapper)( # type: ignore + file, buffer_size + ) + + +class FileWrapper: + """This class can be used to convert a :class:`file`-like object into + an iterable. It yields `buffer_size` blocks until the file is fully + read. + + You should not use this class directly but rather use the + :func:`wrap_file` function that uses the WSGI server's file wrapper + support if it's available. + + .. versionadded:: 0.5 + + If you're using this object together with a :class:`Response` you have + to use the `direct_passthrough` mode. + + :param file: a :class:`file`-like object with a :meth:`~file.read` method. + :param buffer_size: number of bytes for one iteration. + """ + + def __init__(self, file: t.IO[bytes], buffer_size: int = 8192) -> None: + self.file = file + self.buffer_size = buffer_size + + def close(self) -> None: + if hasattr(self.file, "close"): + self.file.close() + + def seekable(self) -> bool: + if hasattr(self.file, "seekable"): + return self.file.seekable() + if hasattr(self.file, "seek"): + return True + return False + + def seek(self, *args: t.Any) -> None: + if hasattr(self.file, "seek"): + self.file.seek(*args) + + def tell(self) -> t.Optional[int]: + if hasattr(self.file, "tell"): + return self.file.tell() + return None + + def __iter__(self) -> "FileWrapper": + return self + + def __next__(self) -> bytes: + data = self.file.read(self.buffer_size) + if data: + return data + raise StopIteration() + + +class _RangeWrapper: + # private for now, but should we make it public in the future ? + + """This class can be used to convert an iterable object into + an iterable that will only yield a piece of the underlying content. + It yields blocks until the underlying stream range is fully read. + The yielded blocks will have a size that can't exceed the original + iterator defined block size, but that can be smaller. + + If you're using this object together with a :class:`Response` you have + to use the `direct_passthrough` mode. + + :param iterable: an iterable object with a :meth:`__next__` method. + :param start_byte: byte from which read will start. + :param byte_range: how many bytes to read. + """ + + def __init__( + self, + iterable: t.Union[t.Iterable[bytes], t.IO[bytes]], + start_byte: int = 0, + byte_range: t.Optional[int] = None, + ): + self.iterable = iter(iterable) + self.byte_range = byte_range + self.start_byte = start_byte + self.end_byte = None + + if byte_range is not None: + self.end_byte = start_byte + byte_range + + self.read_length = 0 + self.seekable = ( + hasattr(iterable, "seekable") and iterable.seekable() # type: ignore + ) + self.end_reached = False + + def __iter__(self) -> "_RangeWrapper": + return self + + def _next_chunk(self) -> bytes: + try: + chunk = next(self.iterable) + self.read_length += len(chunk) + return chunk + except StopIteration: + self.end_reached = True + raise + + def _first_iteration(self) -> t.Tuple[t.Optional[bytes], int]: + chunk = None + if self.seekable: + self.iterable.seek(self.start_byte) # type: ignore + self.read_length = self.iterable.tell() # type: ignore + contextual_read_length = self.read_length + else: + while self.read_length <= self.start_byte: + chunk = self._next_chunk() + if chunk is not None: + chunk = chunk[self.start_byte - self.read_length :] + contextual_read_length = self.start_byte + return chunk, contextual_read_length + + def _next(self) -> bytes: + if self.end_reached: + raise StopIteration() + chunk = None + contextual_read_length = self.read_length + if self.read_length == 0: + chunk, contextual_read_length = self._first_iteration() + if chunk is None: + chunk = self._next_chunk() + if self.end_byte is not None and self.read_length >= self.end_byte: + self.end_reached = True + return chunk[: self.end_byte - contextual_read_length] + return chunk + + def __next__(self) -> bytes: + chunk = self._next() + if chunk: + return chunk + self.end_reached = True + raise StopIteration() + + def close(self) -> None: + if hasattr(self.iterable, "close"): + self.iterable.close() # type: ignore + + +def _make_chunk_iter( + stream: t.Union[t.Iterable[bytes], t.IO[bytes]], + limit: t.Optional[int], + buffer_size: int, +) -> t.Iterator[bytes]: + """Helper for the line and chunk iter functions.""" + if isinstance(stream, (bytes, bytearray, str)): + raise TypeError( + "Passed a string or byte object instead of true iterator or stream." + ) + if not hasattr(stream, "read"): + for item in stream: + if item: + yield item + return + stream = t.cast(t.IO[bytes], stream) + if not isinstance(stream, LimitedStream) and limit is not None: + stream = t.cast(t.IO[bytes], LimitedStream(stream, limit)) + _read = stream.read + while True: + item = _read(buffer_size) + if not item: + break + yield item + + +def make_line_iter( + stream: t.Union[t.Iterable[bytes], t.IO[bytes]], + limit: t.Optional[int] = None, + buffer_size: int = 10 * 1024, + cap_at_buffer: bool = False, +) -> t.Iterator[bytes]: + """Safely iterates line-based over an input stream. If the input stream + is not a :class:`LimitedStream` the `limit` parameter is mandatory. + + This uses the stream's :meth:`~file.read` method internally as opposite + to the :meth:`~file.readline` method that is unsafe and can only be used + in violation of the WSGI specification. The same problem applies to the + `__iter__` function of the input stream which calls :meth:`~file.readline` + without arguments. + + If you need line-by-line processing it's strongly recommended to iterate + over the input stream using this helper function. + + .. versionchanged:: 0.8 + This function now ensures that the limit was reached. + + .. versionadded:: 0.9 + added support for iterators as input stream. + + .. versionadded:: 0.11.10 + added support for the `cap_at_buffer` parameter. + + :param stream: the stream or iterate to iterate over. + :param limit: the limit in bytes for the stream. (Usually + content length. Not necessary if the `stream` + is a :class:`LimitedStream`. + :param buffer_size: The optional buffer size. + :param cap_at_buffer: if this is set chunks are split if they are longer + than the buffer size. Internally this is implemented + that the buffer size might be exhausted by a factor + of two however. + """ + _iter = _make_chunk_iter(stream, limit, buffer_size) + + first_item = next(_iter, "") + if not first_item: + return + + s = _make_encode_wrapper(first_item) + empty = t.cast(bytes, s("")) + cr = t.cast(bytes, s("\r")) + lf = t.cast(bytes, s("\n")) + crlf = t.cast(bytes, s("\r\n")) + + _iter = t.cast(t.Iterator[bytes], chain((first_item,), _iter)) + + def _iter_basic_lines() -> t.Iterator[bytes]: + _join = empty.join + buffer: t.List[bytes] = [] + while True: + new_data = next(_iter, "") + if not new_data: + break + new_buf: t.List[bytes] = [] + buf_size = 0 + for item in t.cast( + t.Iterator[bytes], chain(buffer, new_data.splitlines(True)) + ): + new_buf.append(item) + buf_size += len(item) + if item and item[-1:] in crlf: + yield _join(new_buf) + new_buf = [] + elif cap_at_buffer and buf_size >= buffer_size: + rv = _join(new_buf) + while len(rv) >= buffer_size: + yield rv[:buffer_size] + rv = rv[buffer_size:] + new_buf = [rv] + buffer = new_buf + if buffer: + yield _join(buffer) + + # This hackery is necessary to merge 'foo\r' and '\n' into one item + # of 'foo\r\n' if we were unlucky and we hit a chunk boundary. + previous = empty + for item in _iter_basic_lines(): + if item == lf and previous[-1:] == cr: + previous += item + item = empty + if previous: + yield previous + previous = item + if previous: + yield previous + + +def make_chunk_iter( + stream: t.Union[t.Iterable[bytes], t.IO[bytes]], + separator: bytes, + limit: t.Optional[int] = None, + buffer_size: int = 10 * 1024, + cap_at_buffer: bool = False, +) -> t.Iterator[bytes]: + """Works like :func:`make_line_iter` but accepts a separator + which divides chunks. If you want newline based processing + you should use :func:`make_line_iter` instead as it + supports arbitrary newline markers. + + .. versionadded:: 0.8 + + .. versionadded:: 0.9 + added support for iterators as input stream. + + .. versionadded:: 0.11.10 + added support for the `cap_at_buffer` parameter. + + :param stream: the stream or iterate to iterate over. + :param separator: the separator that divides chunks. + :param limit: the limit in bytes for the stream. (Usually + content length. Not necessary if the `stream` + is otherwise already limited). + :param buffer_size: The optional buffer size. + :param cap_at_buffer: if this is set chunks are split if they are longer + than the buffer size. Internally this is implemented + that the buffer size might be exhausted by a factor + of two however. + """ + _iter = _make_chunk_iter(stream, limit, buffer_size) + + first_item = next(_iter, b"") + if not first_item: + return + + _iter = t.cast(t.Iterator[bytes], chain((first_item,), _iter)) + if isinstance(first_item, str): + separator = _to_str(separator) + _split = re.compile(f"({re.escape(separator)})").split + _join = "".join + else: + separator = _to_bytes(separator) + _split = re.compile(b"(" + re.escape(separator) + b")").split + _join = b"".join + + buffer: t.List[bytes] = [] + while True: + new_data = next(_iter, b"") + if not new_data: + break + chunks = _split(new_data) + new_buf: t.List[bytes] = [] + buf_size = 0 + for item in chain(buffer, chunks): + if item == separator: + yield _join(new_buf) + new_buf = [] + buf_size = 0 + else: + buf_size += len(item) + new_buf.append(item) + + if cap_at_buffer and buf_size >= buffer_size: + rv = _join(new_buf) + while len(rv) >= buffer_size: + yield rv[:buffer_size] + rv = rv[buffer_size:] + new_buf = [rv] + buf_size = len(rv) + + buffer = new_buf + if buffer: + yield _join(buffer) + + +class LimitedStream(io.IOBase): + """Wraps a stream so that it doesn't read more than n bytes. If the + stream is exhausted and the caller tries to get more bytes from it + :func:`on_exhausted` is called which by default returns an empty + string. The return value of that function is forwarded + to the reader function. So if it returns an empty string + :meth:`read` will return an empty string as well. + + The limit however must never be higher than what the stream can + output. Otherwise :meth:`readlines` will try to read past the + limit. + + .. admonition:: Note on WSGI compliance + + calls to :meth:`readline` and :meth:`readlines` are not + WSGI compliant because it passes a size argument to the + readline methods. Unfortunately the WSGI PEP is not safely + implementable without a size argument to :meth:`readline` + because there is no EOF marker in the stream. As a result + of that the use of :meth:`readline` is discouraged. + + For the same reason iterating over the :class:`LimitedStream` + is not portable. It internally calls :meth:`readline`. + + We strongly suggest using :meth:`read` only or using the + :func:`make_line_iter` which safely iterates line-based + over a WSGI input stream. + + :param stream: the stream to wrap. + :param limit: the limit for the stream, must not be longer than + what the string can provide if the stream does not + end with `EOF` (like `wsgi.input`) + """ + + def __init__(self, stream: t.IO[bytes], limit: int) -> None: + self._read = stream.read + self._readline = stream.readline + self._pos = 0 + self.limit = limit + + def __iter__(self) -> "LimitedStream": + return self + + @property + def is_exhausted(self) -> bool: + """If the stream is exhausted this attribute is `True`.""" + return self._pos >= self.limit + + def on_exhausted(self) -> bytes: + """This is called when the stream tries to read past the limit. + The return value of this function is returned from the reading + function. + """ + # Read null bytes from the stream so that we get the + # correct end of stream marker. + return self._read(0) + + def on_disconnect(self) -> bytes: + """What should happen if a disconnect is detected? The return + value of this function is returned from read functions in case + the client went away. By default a + :exc:`~werkzeug.exceptions.ClientDisconnected` exception is raised. + """ + from .exceptions import ClientDisconnected + + raise ClientDisconnected() + + def exhaust(self, chunk_size: int = 1024 * 64) -> None: + """Exhaust the stream. This consumes all the data left until the + limit is reached. + + :param chunk_size: the size for a chunk. It will read the chunk + until the stream is exhausted and throw away + the results. + """ + to_read = self.limit - self._pos + chunk = chunk_size + while to_read > 0: + chunk = min(to_read, chunk) + self.read(chunk) + to_read -= chunk + + def read(self, size: t.Optional[int] = None) -> bytes: + """Read `size` bytes or if size is not provided everything is read. + + :param size: the number of bytes read. + """ + if self._pos >= self.limit: + return self.on_exhausted() + if size is None or size == -1: # -1 is for consistence with file + size = self.limit + to_read = min(self.limit - self._pos, size) + try: + read = self._read(to_read) + except (OSError, ValueError): + return self.on_disconnect() + if to_read and len(read) != to_read: + return self.on_disconnect() + self._pos += len(read) + return read + + def readline(self, size: t.Optional[int] = None) -> bytes: + """Reads one line from the stream.""" + if self._pos >= self.limit: + return self.on_exhausted() + if size is None: + size = self.limit - self._pos + else: + size = min(size, self.limit - self._pos) + try: + line = self._readline(size) + except (ValueError, OSError): + return self.on_disconnect() + if size and not line: + return self.on_disconnect() + self._pos += len(line) + return line + + def readlines(self, size: t.Optional[int] = None) -> t.List[bytes]: + """Reads a file into a list of strings. It calls :meth:`readline` + until the file is read to the end. It does support the optional + `size` argument if the underlying stream supports it for + `readline`. + """ + last_pos = self._pos + result = [] + if size is not None: + end = min(self.limit, last_pos + size) + else: + end = self.limit + while True: + if size is not None: + size -= last_pos - self._pos + if self._pos >= end: + break + result.append(self.readline(size)) + if size is not None: + last_pos = self._pos + return result + + def tell(self) -> int: + """Returns the position of the stream. + + .. versionadded:: 0.9 + """ + return self._pos + + def __next__(self) -> bytes: + line = self.readline() + if not line: + raise StopIteration() + return line + + def readable(self) -> bool: + return True diff --git a/myvenv/lib64 b/myvenv/lib64 new file mode 120000 index 0000000..7951405 --- /dev/null +++ b/myvenv/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/myvenv/pyvenv.cfg b/myvenv/pyvenv.cfg new file mode 100644 index 0000000..0d8baee --- /dev/null +++ b/myvenv/pyvenv.cfg @@ -0,0 +1,3 @@ +home = /usr/bin +include-system-site-packages = false +version = 3.10.2 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8c6a035 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +flask>=2.0.3 diff --git a/static/styles/main.css b/static/styles/main.css new file mode 100644 index 0000000..3da6d6e --- /dev/null +++ b/static/styles/main.css @@ -0,0 +1,44 @@ +html { + background-color: #393c42; + font-family: 'Source Sans Pro'; + color: lightgrey; +} + +h1 { + color: green; +} + + +.notetitle { + color: green; + font-size: 1.5em; +} + +.notetime { + color: grey; + font-style: italic; +} + +.aaaa { + background: black; + color: springgreen; + font-weight: bold; + border-radius: 5px; + outline: none; + border-width: 2px; +} + +.message { + background: black; + color: beige; + border-radius: 5px; + border-style: inset; + outline: none; + border-width: 2px; + height: 200px; + font-size: 15px; +} + + + + diff --git a/templates/homepage.html b/templates/homepage.html new file mode 100644 index 0000000..5e0a82a --- /dev/null +++ b/templates/homepage.html @@ -0,0 +1,24 @@ + + + + + + squip_notes + + + +

    $ ~/squip_notes

    +
    + + + + + +
    + {{ nr }} +
    + + + + +