From e9a843bdc8833f804b53bfadc9aa30985e9f1c13 Mon Sep 17 00:00:00 2001 From: Gloomer Date: Wed, 6 May 2026 17:30:35 +0300 Subject: [PATCH] Base WEB UI structure --- .../flaskui/__pycache__/flask.cpython-310.pyc | Bin 0 -> 524 bytes core/flaskui/flask.py | 13 + core/flaskui/templates/chats.html | 18 + core/flaskui/templates/index.html | 22 + main.py | 9 +- .../blinker-1.9.0.dist-info/INSTALLER | 1 + .../blinker-1.9.0.dist-info/LICENSE.txt | 20 + .../blinker-1.9.0.dist-info/METADATA | 60 + .../blinker-1.9.0.dist-info/RECORD | 12 + .../blinker-1.9.0.dist-info/WHEEL | 4 + venv/Lib/site-packages/blinker/__init__.py | 17 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 457 bytes .../__pycache__/_utilities.cpython-310.pyc | Bin 0 -> 2155 bytes .../blinker/__pycache__/base.cpython-310.pyc | Bin 0 -> 17590 bytes venv/Lib/site-packages/blinker/_utilities.py | 64 + venv/Lib/site-packages/blinker/base.py | 512 +++ venv/Lib/site-packages/blinker/py.typed | 0 .../click-8.3.3.dist-info/INSTALLER | 1 + .../click-8.3.3.dist-info/METADATA | 84 + .../click-8.3.3.dist-info/RECORD | 40 + .../site-packages/click-8.3.3.dist-info/WHEEL | 4 + .../licenses/LICENSE.txt | 28 + venv/Lib/site-packages/click/__init__.py | 124 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 3677 bytes .../click/__pycache__/_compat.cpython-310.pyc | Bin 0 -> 16014 bytes .../__pycache__/_termui_impl.cpython-310.pyc | Bin 0 -> 18508 bytes .../__pycache__/_textwrap.cpython-310.pyc | Bin 0 -> 1641 bytes .../click/__pycache__/_utils.cpython-310.pyc | Bin 0 -> 890 bytes .../__pycache__/_winconsole.cpython-310.pyc | Bin 0 -> 8254 bytes .../click/__pycache__/core.cpython-310.pyc | Bin 0 -> 100669 bytes .../__pycache__/decorators.cpython-310.pyc | Bin 0 -> 17545 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 11011 bytes .../__pycache__/formatting.cpython-310.pyc | Bin 0 -> 9681 bytes .../click/__pycache__/globals.cpython-310.pyc | Bin 0 -> 2461 bytes .../click/__pycache__/parser.cpython-310.pyc | Bin 0 -> 14135 bytes .../shell_completion.cpython-310.pyc | Bin 0 -> 18282 bytes .../click/__pycache__/termui.cpython-310.pyc | Bin 0 -> 29355 bytes .../click/__pycache__/testing.cpython-310.pyc | Bin 0 -> 22307 bytes .../click/__pycache__/types.cpython-310.pyc | Bin 0 -> 37266 bytes .../click/__pycache__/utils.cpython-310.pyc | Bin 0 -> 19113 bytes venv/Lib/site-packages/click/_compat.py | 622 +++ venv/Lib/site-packages/click/_termui_impl.py | 870 +++++ venv/Lib/site-packages/click/_textwrap.py | 51 + venv/Lib/site-packages/click/_utils.py | 36 + venv/Lib/site-packages/click/_winconsole.py | 296 ++ venv/Lib/site-packages/click/core.py | 3476 +++++++++++++++++ venv/Lib/site-packages/click/decorators.py | 551 +++ venv/Lib/site-packages/click/exceptions.py | 308 ++ venv/Lib/site-packages/click/formatting.py | 301 ++ venv/Lib/site-packages/click/globals.py | 67 + venv/Lib/site-packages/click/parser.py | 532 +++ venv/Lib/site-packages/click/py.typed | 0 .../site-packages/click/shell_completion.py | 667 ++++ venv/Lib/site-packages/click/termui.py | 891 +++++ venv/Lib/site-packages/click/testing.py | 669 ++++ venv/Lib/site-packages/click/types.py | 1209 ++++++ venv/Lib/site-packages/click/utils.py | 630 +++ .../colorama-0.4.6.dist-info/INSTALLER | 1 + .../colorama-0.4.6.dist-info/METADATA | 441 +++ .../colorama-0.4.6.dist-info/RECORD | 31 + .../colorama-0.4.6.dist-info/WHEEL | 5 + .../licenses/LICENSE.txt | 27 + venv/Lib/site-packages/colorama/__init__.py | 7 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 446 bytes .../colorama/__pycache__/ansi.cpython-310.pyc | Bin 0 -> 2972 bytes .../__pycache__/ansitowin32.cpython-310.pyc | Bin 0 -> 8484 bytes .../__pycache__/initialise.cpython-310.pyc | Bin 0 -> 2229 bytes .../__pycache__/win32.cpython-310.pyc | Bin 0 -> 4439 bytes .../__pycache__/winterm.cpython-310.pyc | Bin 0 -> 5135 bytes venv/Lib/site-packages/colorama/ansi.py | 102 + .../Lib/site-packages/colorama/ansitowin32.py | 277 ++ venv/Lib/site-packages/colorama/initialise.py | 121 + .../site-packages/colorama/tests/__init__.py | 1 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 166 bytes .../__pycache__/ansi_test.cpython-310.pyc | Bin 0 -> 2258 bytes .../ansitowin32_test.cpython-310.pyc | Bin 0 -> 11569 bytes .../initialise_test.cpython-310.pyc | Bin 0 -> 6866 bytes .../__pycache__/isatty_test.cpython-310.pyc | Bin 0 -> 2657 bytes .../tests/__pycache__/utils.cpython-310.pyc | Bin 0 -> 1566 bytes .../__pycache__/winterm_test.cpython-310.pyc | Bin 0 -> 3159 bytes .../site-packages/colorama/tests/ansi_test.py | 76 + .../colorama/tests/ansitowin32_test.py | 294 ++ .../colorama/tests/initialise_test.py | 189 + .../colorama/tests/isatty_test.py | 57 + .../Lib/site-packages/colorama/tests/utils.py | 49 + .../colorama/tests/winterm_test.py | 131 + venv/Lib/site-packages/colorama/win32.py | 180 + venv/Lib/site-packages/colorama/winterm.py | 195 + .../flask-3.1.3.dist-info/INSTALLER | 1 + .../flask-3.1.3.dist-info/METADATA | 91 + .../flask-3.1.3.dist-info/RECORD | 58 + .../flask-3.1.3.dist-info/REQUESTED | 0 .../site-packages/flask-3.1.3.dist-info/WHEEL | 4 + .../flask-3.1.3.dist-info/entry_points.txt | 3 + .../licenses/LICENSE.txt | 28 + venv/Lib/site-packages/flask/__init__.py | 61 + venv/Lib/site-packages/flask/__main__.py | 3 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 2301 bytes .../__pycache__/__main__.cpython-310.pyc | Bin 0 -> 197 bytes .../flask/__pycache__/app.cpython-310.pyc | Bin 0 -> 50865 bytes .../__pycache__/blueprints.cpython-310.pyc | Bin 0 -> 4152 bytes .../flask/__pycache__/cli.cpython-310.pyc | Bin 0 -> 29965 bytes .../flask/__pycache__/config.cpython-310.pyc | Bin 0 -> 13390 bytes .../flask/__pycache__/ctx.cpython-310.pyc | Bin 0 -> 15314 bytes .../__pycache__/debughelpers.cpython-310.pyc | Bin 0 -> 6576 bytes .../flask/__pycache__/globals.cpython-310.pyc | Bin 0 -> 1565 bytes .../flask/__pycache__/helpers.cpython-310.pyc | Bin 0 -> 22112 bytes .../flask/__pycache__/logging.cpython-310.pyc | Bin 0 -> 2532 bytes .../__pycache__/sessions.cpython-310.pyc | Bin 0 -> 13626 bytes .../flask/__pycache__/signals.cpython-310.pyc | Bin 0 -> 795 bytes .../__pycache__/templating.cpython-310.pyc | Bin 0 -> 7094 bytes .../flask/__pycache__/testing.cpython-310.pyc | Bin 0 -> 9819 bytes .../flask/__pycache__/typing.cpython-310.pyc | Bin 0 -> 1872 bytes .../flask/__pycache__/views.cpython-310.pyc | Bin 0 -> 5533 bytes .../__pycache__/wrappers.cpython-310.pyc | Bin 0 -> 8375 bytes venv/Lib/site-packages/flask/app.py | 1536 ++++++++ venv/Lib/site-packages/flask/blueprints.py | 128 + venv/Lib/site-packages/flask/cli.py | 1135 ++++++ venv/Lib/site-packages/flask/config.py | 367 ++ venv/Lib/site-packages/flask/ctx.py | 459 +++ venv/Lib/site-packages/flask/debughelpers.py | 178 + venv/Lib/site-packages/flask/globals.py | 51 + venv/Lib/site-packages/flask/helpers.py | 641 +++ venv/Lib/site-packages/flask/json/__init__.py | 170 + .../json/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 5979 bytes .../json/__pycache__/provider.cpython-310.pyc | Bin 0 -> 7671 bytes .../json/__pycache__/tag.cpython-310.pyc | Bin 0 -> 11629 bytes venv/Lib/site-packages/flask/json/provider.py | 215 + venv/Lib/site-packages/flask/json/tag.py | 327 ++ venv/Lib/site-packages/flask/logging.py | 79 + venv/Lib/site-packages/flask/py.typed | 0 venv/Lib/site-packages/flask/sansio/README.md | 6 + .../sansio/__pycache__/app.cpython-310.pyc | Bin 0 -> 28625 bytes .../__pycache__/blueprints.cpython-310.pyc | Bin 0 -> 22918 bytes .../__pycache__/scaffold.cpython-310.pyc | Bin 0 -> 23835 bytes venv/Lib/site-packages/flask/sansio/app.py | 964 +++++ .../site-packages/flask/sansio/blueprints.py | 632 +++ .../site-packages/flask/sansio/scaffold.py | 792 ++++ venv/Lib/site-packages/flask/sessions.py | 385 ++ venv/Lib/site-packages/flask/signals.py | 17 + venv/Lib/site-packages/flask/templating.py | 220 ++ venv/Lib/site-packages/flask/testing.py | 298 ++ venv/Lib/site-packages/flask/typing.py | 93 + venv/Lib/site-packages/flask/views.py | 191 + venv/Lib/site-packages/flask/wrappers.py | 257 ++ .../itsdangerous-2.2.0.dist-info/INSTALLER | 1 + .../itsdangerous-2.2.0.dist-info/LICENSE.txt | 28 + .../itsdangerous-2.2.0.dist-info/METADATA | 60 + .../itsdangerous-2.2.0.dist-info/RECORD | 22 + .../itsdangerous-2.2.0.dist-info/WHEEL | 4 + .../site-packages/itsdangerous/__init__.py | 38 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1446 bytes .../__pycache__/_json.cpython-310.pyc | Bin 0 -> 964 bytes .../__pycache__/encoding.cpython-310.pyc | Bin 0 -> 1915 bytes .../__pycache__/exc.cpython-310.pyc | Bin 0 -> 3380 bytes .../__pycache__/serializer.cpython-310.pyc | Bin 0 -> 11712 bytes .../__pycache__/signer.cpython-310.pyc | Bin 0 -> 8991 bytes .../__pycache__/timed.cpython-310.pyc | Bin 0 -> 6551 bytes .../__pycache__/url_safe.cpython-310.pyc | Bin 0 -> 2845 bytes venv/Lib/site-packages/itsdangerous/_json.py | 18 + .../site-packages/itsdangerous/encoding.py | 54 + venv/Lib/site-packages/itsdangerous/exc.py | 106 + venv/Lib/site-packages/itsdangerous/py.typed | 0 .../site-packages/itsdangerous/serializer.py | 406 ++ venv/Lib/site-packages/itsdangerous/signer.py | 266 ++ venv/Lib/site-packages/itsdangerous/timed.py | 228 ++ .../site-packages/itsdangerous/url_safe.py | 83 + .../jinja2-3.1.6.dist-info/INSTALLER | 1 + .../jinja2-3.1.6.dist-info/METADATA | 84 + .../jinja2-3.1.6.dist-info/RECORD | 57 + .../jinja2-3.1.6.dist-info/WHEEL | 4 + .../jinja2-3.1.6.dist-info/entry_points.txt | 3 + .../licenses/LICENSE.txt | 28 + venv/Lib/site-packages/jinja2/__init__.py | 38 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 1586 bytes .../__pycache__/_identifier.cpython-310.pyc | Bin 0 -> 2061 bytes .../__pycache__/async_utils.cpython-310.pyc | Bin 0 -> 3438 bytes .../__pycache__/bccache.cpython-310.pyc | Bin 0 -> 13952 bytes .../__pycache__/compiler.cpython-310.pyc | Bin 0 -> 55232 bytes .../__pycache__/constants.cpython-310.pyc | Bin 0 -> 1522 bytes .../jinja2/__pycache__/debug.cpython-310.pyc | Bin 0 -> 3978 bytes .../__pycache__/defaults.cpython-310.pyc | Bin 0 -> 1322 bytes .../__pycache__/environment.cpython-310.pyc | Bin 0 -> 53430 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 5521 bytes .../jinja2/__pycache__/ext.cpython-310.pyc | Bin 0 -> 25860 bytes .../__pycache__/filters.cpython-310.pyc | Bin 0 -> 52104 bytes .../__pycache__/idtracking.cpython-310.pyc | Bin 0 -> 11073 bytes .../jinja2/__pycache__/lexer.cpython-310.pyc | Bin 0 -> 20429 bytes .../__pycache__/loaders.cpython-310.pyc | Bin 0 -> 21239 bytes .../jinja2/__pycache__/meta.cpython-310.pyc | Bin 0 -> 3802 bytes .../__pycache__/nativetypes.cpython-310.pyc | Bin 0 -> 4997 bytes .../jinja2/__pycache__/nodes.cpython-310.pyc | Bin 0 -> 40288 bytes .../__pycache__/optimizer.cpython-310.pyc | Bin 0 -> 1942 bytes .../jinja2/__pycache__/parser.cpython-310.pyc | Bin 0 -> 28095 bytes .../__pycache__/runtime.cpython-310.pyc | Bin 0 -> 32340 bytes .../__pycache__/sandbox.cpython-310.pyc | Bin 0 -> 12222 bytes .../jinja2/__pycache__/tests.cpython-310.pyc | Bin 0 -> 6682 bytes .../jinja2/__pycache__/utils.cpython-310.pyc | Bin 0 -> 24809 bytes .../__pycache__/visitor.cpython-310.pyc | Bin 0 -> 3961 bytes venv/Lib/site-packages/jinja2/_identifier.py | 6 + venv/Lib/site-packages/jinja2/async_utils.py | 99 + venv/Lib/site-packages/jinja2/bccache.py | 408 ++ venv/Lib/site-packages/jinja2/compiler.py | 1998 ++++++++++ venv/Lib/site-packages/jinja2/constants.py | 20 + venv/Lib/site-packages/jinja2/debug.py | 191 + venv/Lib/site-packages/jinja2/defaults.py | 48 + venv/Lib/site-packages/jinja2/environment.py | 1672 ++++++++ venv/Lib/site-packages/jinja2/exceptions.py | 166 + venv/Lib/site-packages/jinja2/ext.py | 870 +++++ venv/Lib/site-packages/jinja2/filters.py | 1873 +++++++++ venv/Lib/site-packages/jinja2/idtracking.py | 318 ++ venv/Lib/site-packages/jinja2/lexer.py | 868 ++++ venv/Lib/site-packages/jinja2/loaders.py | 693 ++++ venv/Lib/site-packages/jinja2/meta.py | 112 + venv/Lib/site-packages/jinja2/nativetypes.py | 130 + venv/Lib/site-packages/jinja2/nodes.py | 1206 ++++++ venv/Lib/site-packages/jinja2/optimizer.py | 48 + venv/Lib/site-packages/jinja2/parser.py | 1049 +++++ venv/Lib/site-packages/jinja2/py.typed | 0 venv/Lib/site-packages/jinja2/runtime.py | 1062 +++++ venv/Lib/site-packages/jinja2/sandbox.py | 436 +++ venv/Lib/site-packages/jinja2/tests.py | 256 ++ venv/Lib/site-packages/jinja2/utils.py | 766 ++++ venv/Lib/site-packages/jinja2/visitor.py | 92 + .../markupsafe-3.0.3.dist-info/INSTALLER | 1 + .../markupsafe-3.0.3.dist-info/METADATA | 74 + .../markupsafe-3.0.3.dist-info/RECORD | 14 + .../markupsafe-3.0.3.dist-info/WHEEL | 5 + .../licenses/LICENSE.txt | 28 + .../markupsafe-3.0.3.dist-info/top_level.txt | 1 + venv/Lib/site-packages/markupsafe/__init__.py | 396 ++ .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 15415 bytes .../__pycache__/_native.cpython-310.pyc | Bin 0 -> 396 bytes venv/Lib/site-packages/markupsafe/_native.py | 8 + venv/Lib/site-packages/markupsafe/_speedups.c | 200 + .../markupsafe/_speedups.cp310-win_amd64.pyd | Bin 0 -> 13312 bytes .../site-packages/markupsafe/_speedups.pyi | 1 + venv/Lib/site-packages/markupsafe/py.typed | 0 .../werkzeug-3.1.8.dist-info/INSTALLER | 1 + .../werkzeug-3.1.8.dist-info/METADATA | 109 + .../werkzeug-3.1.8.dist-info/RECORD | 116 + .../werkzeug-3.1.8.dist-info/WHEEL | 4 + .../licenses/LICENSE.txt | 28 + venv/Lib/site-packages/werkzeug/__init__.py | 4 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 308 bytes .../__pycache__/_internal.cpython-310.pyc | Bin 0 -> 7034 bytes .../__pycache__/_reloader.cpython-310.pyc | Bin 0 -> 13003 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 28177 bytes .../__pycache__/formparser.cpython-310.pyc | Bin 0 -> 12798 bytes .../werkzeug/__pycache__/http.cpython-310.pyc | Bin 0 -> 40100 bytes .../__pycache__/local.cpython-310.pyc | Bin 0 -> 21025 bytes .../__pycache__/security.cpython-310.pyc | Bin 0 -> 6652 bytes .../__pycache__/serving.cpython-310.pyc | Bin 0 -> 30632 bytes .../werkzeug/__pycache__/test.cpython-310.pyc | Bin 0 -> 42995 bytes .../__pycache__/testapp.cpython-310.pyc | Bin 0 -> 6589 bytes .../werkzeug/__pycache__/urls.cpython-310.pyc | Bin 0 -> 5808 bytes .../__pycache__/user_agent.cpython-310.pyc | Bin 0 -> 1846 bytes .../__pycache__/utils.cpython-310.pyc | Bin 0 -> 21987 bytes .../werkzeug/__pycache__/wsgi.cpython-310.pyc | Bin 0 -> 20692 bytes venv/Lib/site-packages/werkzeug/_internal.py | 211 + venv/Lib/site-packages/werkzeug/_reloader.py | 465 +++ .../werkzeug/datastructures/__init__.py | 64 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 2214 bytes .../__pycache__/accept.cpython-310.pyc | Bin 0 -> 12485 bytes .../__pycache__/auth.cpython-310.pyc | Bin 0 -> 10514 bytes .../__pycache__/cache_control.cpython-310.pyc | Bin 0 -> 10268 bytes .../__pycache__/csp.cpython-310.pyc | Bin 0 -> 4914 bytes .../__pycache__/etag.cpython-310.pyc | Bin 0 -> 4247 bytes .../__pycache__/file_storage.cpython-310.pyc | Bin 0 -> 6777 bytes .../__pycache__/headers.cpython-310.pyc | Bin 0 -> 23348 bytes .../__pycache__/mixins.cpython-310.pyc | Bin 0 -> 13212 bytes .../__pycache__/range.cpython-310.pyc | Bin 0 -> 7508 bytes .../__pycache__/structures.cpython-310.pyc | Bin 0 -> 44473 bytes .../werkzeug/datastructures/accept.py | 350 ++ .../werkzeug/datastructures/auth.py | 320 ++ .../werkzeug/datastructures/cache_control.py | 273 ++ .../werkzeug/datastructures/csp.py | 100 + .../werkzeug/datastructures/etag.py | 106 + .../werkzeug/datastructures/file_storage.py | 209 + .../werkzeug/datastructures/headers.py | 662 ++++ .../werkzeug/datastructures/mixins.py | 317 ++ .../werkzeug/datastructures/range.py | 214 + .../werkzeug/datastructures/structures.py | 1239 ++++++ .../site-packages/werkzeug/debug/__init__.py | 574 +++ .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 15043 bytes .../debug/__pycache__/console.cpython-310.pyc | Bin 0 -> 8332 bytes .../debug/__pycache__/repr.cpython-310.pyc | Bin 0 -> 9027 bytes .../debug/__pycache__/tbtools.cpython-310.pyc | Bin 0 -> 11936 bytes .../site-packages/werkzeug/debug/console.py | 219 ++ venv/Lib/site-packages/werkzeug/debug/repr.py | 282 ++ .../werkzeug/debug/shared/ICON_LICENSE.md | 6 + .../werkzeug/debug/shared/console.png | Bin 0 -> 507 bytes .../werkzeug/debug/shared/debugger.js | 344 ++ .../werkzeug/debug/shared/less.png | Bin 0 -> 191 bytes .../werkzeug/debug/shared/more.png | Bin 0 -> 200 bytes .../werkzeug/debug/shared/style.css | 150 + .../site-packages/werkzeug/debug/tbtools.py | 449 +++ venv/Lib/site-packages/werkzeug/exceptions.py | 905 +++++ venv/Lib/site-packages/werkzeug/formparser.py | 430 ++ venv/Lib/site-packages/werkzeug/http.py | 1443 +++++++ venv/Lib/site-packages/werkzeug/local.py | 653 ++++ .../werkzeug/middleware/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 171 bytes .../__pycache__/dispatcher.cpython-310.pyc | Bin 0 -> 2820 bytes .../__pycache__/http_proxy.cpython-310.pyc | Bin 0 -> 6913 bytes .../__pycache__/lint.cpython-310.pyc | Bin 0 -> 13064 bytes .../__pycache__/profiler.cpython-310.pyc | Bin 0 -> 5612 bytes .../__pycache__/proxy_fix.cpython-310.pyc | Bin 0 -> 6013 bytes .../__pycache__/shared_data.cpython-310.pyc | Bin 0 -> 9327 bytes .../werkzeug/middleware/dispatcher.py | 81 + .../werkzeug/middleware/http_proxy.py | 236 ++ .../site-packages/werkzeug/middleware/lint.py | 439 +++ .../werkzeug/middleware/profiler.py | 155 + .../werkzeug/middleware/proxy_fix.py | 183 + .../werkzeug/middleware/shared_data.py | 283 ++ venv/Lib/site-packages/werkzeug/py.typed | 0 .../werkzeug/routing/__init__.py | 134 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 4609 bytes .../__pycache__/converters.cpython-310.pyc | Bin 0 -> 9188 bytes .../__pycache__/exceptions.cpython-310.pyc | Bin 0 -> 5657 bytes .../routing/__pycache__/map.cpython-310.pyc | Bin 0 -> 30586 bytes .../__pycache__/matcher.cpython-310.pyc | Bin 0 -> 5132 bytes .../routing/__pycache__/rules.cpython-310.pyc | Bin 0 -> 28057 bytes .../werkzeug/routing/converters.py | 261 ++ .../werkzeug/routing/exceptions.py | 152 + .../Lib/site-packages/werkzeug/routing/map.py | 928 +++++ .../site-packages/werkzeug/routing/matcher.py | 202 + .../site-packages/werkzeug/routing/rules.py | 927 +++++ .../site-packages/werkzeug/sansio/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 167 bytes .../sansio/__pycache__/http.cpython-310.pyc | Bin 0 -> 4151 bytes .../__pycache__/multipart.cpython-310.pyc | Bin 0 -> 7655 bytes .../__pycache__/request.cpython-310.pyc | Bin 0 -> 17358 bytes .../__pycache__/response.cpython-310.pyc | Bin 0 -> 24853 bytes .../sansio/__pycache__/utils.cpython-310.pyc | Bin 0 -> 6687 bytes .../Lib/site-packages/werkzeug/sansio/http.py | 170 + .../werkzeug/sansio/multipart.py | 331 ++ .../site-packages/werkzeug/sansio/request.py | 536 +++ .../site-packages/werkzeug/sansio/response.py | 763 ++++ .../site-packages/werkzeug/sansio/utils.py | 224 ++ venv/Lib/site-packages/werkzeug/security.py | 201 + venv/Lib/site-packages/werkzeug/serving.py | 1126 ++++++ venv/Lib/site-packages/werkzeug/test.py | 1464 +++++++ venv/Lib/site-packages/werkzeug/testapp.py | 194 + venv/Lib/site-packages/werkzeug/urls.py | 203 + venv/Lib/site-packages/werkzeug/user_agent.py | 47 + venv/Lib/site-packages/werkzeug/utils.py | 684 ++++ .../werkzeug/wrappers/__init__.py | 3 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 286 bytes .../__pycache__/request.cpython-310.pyc | Bin 0 -> 21766 bytes .../__pycache__/response.cpython-310.pyc | Bin 0 -> 28616 bytes .../werkzeug/wrappers/request.py | 650 +++ .../werkzeug/wrappers/response.py | 838 ++++ venv/Lib/site-packages/werkzeug/wsgi.py | 609 +++ venv/Scripts/flask.exe | Bin 0 -> 108393 bytes 355 files changed, 62691 insertions(+), 7 deletions(-) create mode 100644 core/flaskui/__pycache__/flask.cpython-310.pyc create mode 100644 core/flaskui/flask.py create mode 100644 core/flaskui/templates/chats.html create mode 100644 core/flaskui/templates/index.html create mode 100644 venv/Lib/site-packages/blinker-1.9.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/blinker-1.9.0.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/blinker-1.9.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/blinker-1.9.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/blinker-1.9.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/blinker/__init__.py create mode 100644 venv/Lib/site-packages/blinker/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/blinker/__pycache__/_utilities.cpython-310.pyc create mode 100644 venv/Lib/site-packages/blinker/__pycache__/base.cpython-310.pyc create mode 100644 venv/Lib/site-packages/blinker/_utilities.py create mode 100644 venv/Lib/site-packages/blinker/base.py create mode 100644 venv/Lib/site-packages/blinker/py.typed create mode 100644 venv/Lib/site-packages/click-8.3.3.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/click-8.3.3.dist-info/METADATA create mode 100644 venv/Lib/site-packages/click-8.3.3.dist-info/RECORD create mode 100644 venv/Lib/site-packages/click-8.3.3.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/click-8.3.3.dist-info/licenses/LICENSE.txt create mode 100644 venv/Lib/site-packages/click/__init__.py create mode 100644 venv/Lib/site-packages/click/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_compat.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_textwrap.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_utils.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/_winconsole.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/core.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/decorators.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/exceptions.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/formatting.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/globals.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/parser.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/shell_completion.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/termui.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/testing.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/types.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/__pycache__/utils.cpython-310.pyc create mode 100644 venv/Lib/site-packages/click/_compat.py create mode 100644 venv/Lib/site-packages/click/_termui_impl.py create mode 100644 venv/Lib/site-packages/click/_textwrap.py create mode 100644 venv/Lib/site-packages/click/_utils.py create mode 100644 venv/Lib/site-packages/click/_winconsole.py create mode 100644 venv/Lib/site-packages/click/core.py create mode 100644 venv/Lib/site-packages/click/decorators.py create mode 100644 venv/Lib/site-packages/click/exceptions.py create mode 100644 venv/Lib/site-packages/click/formatting.py create mode 100644 venv/Lib/site-packages/click/globals.py create mode 100644 venv/Lib/site-packages/click/parser.py create mode 100644 venv/Lib/site-packages/click/py.typed create mode 100644 venv/Lib/site-packages/click/shell_completion.py create mode 100644 venv/Lib/site-packages/click/termui.py create mode 100644 venv/Lib/site-packages/click/testing.py create mode 100644 venv/Lib/site-packages/click/types.py create mode 100644 venv/Lib/site-packages/click/utils.py create mode 100644 venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA create mode 100644 venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD create mode 100644 venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt create mode 100644 venv/Lib/site-packages/colorama/__init__.py create mode 100644 venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/win32.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/ansi.py create mode 100644 venv/Lib/site-packages/colorama/ansitowin32.py create mode 100644 venv/Lib/site-packages/colorama/initialise.py create mode 100644 venv/Lib/site-packages/colorama/tests/__init__.py create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/ansi_test.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/initialise_test.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/isatty_test.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/utils.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/__pycache__/winterm_test.cpython-310.pyc create mode 100644 venv/Lib/site-packages/colorama/tests/ansi_test.py create mode 100644 venv/Lib/site-packages/colorama/tests/ansitowin32_test.py create mode 100644 venv/Lib/site-packages/colorama/tests/initialise_test.py create mode 100644 venv/Lib/site-packages/colorama/tests/isatty_test.py create mode 100644 venv/Lib/site-packages/colorama/tests/utils.py create mode 100644 venv/Lib/site-packages/colorama/tests/winterm_test.py create mode 100644 venv/Lib/site-packages/colorama/win32.py create mode 100644 venv/Lib/site-packages/colorama/winterm.py create mode 100644 venv/Lib/site-packages/flask-3.1.3.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/flask-3.1.3.dist-info/METADATA create mode 100644 venv/Lib/site-packages/flask-3.1.3.dist-info/RECORD create mode 100644 venv/Lib/site-packages/flask-3.1.3.dist-info/REQUESTED create mode 100644 venv/Lib/site-packages/flask-3.1.3.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/flask-3.1.3.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/flask-3.1.3.dist-info/licenses/LICENSE.txt create mode 100644 venv/Lib/site-packages/flask/__init__.py create mode 100644 venv/Lib/site-packages/flask/__main__.py create mode 100644 venv/Lib/site-packages/flask/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/__main__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/app.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/blueprints.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/cli.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/config.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/ctx.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/debughelpers.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/globals.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/helpers.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/logging.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/sessions.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/signals.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/templating.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/testing.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/typing.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/views.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/__pycache__/wrappers.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/app.py create mode 100644 venv/Lib/site-packages/flask/blueprints.py create mode 100644 venv/Lib/site-packages/flask/cli.py create mode 100644 venv/Lib/site-packages/flask/config.py create mode 100644 venv/Lib/site-packages/flask/ctx.py create mode 100644 venv/Lib/site-packages/flask/debughelpers.py create mode 100644 venv/Lib/site-packages/flask/globals.py create mode 100644 venv/Lib/site-packages/flask/helpers.py create mode 100644 venv/Lib/site-packages/flask/json/__init__.py create mode 100644 venv/Lib/site-packages/flask/json/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/json/__pycache__/provider.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/json/__pycache__/tag.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/json/provider.py create mode 100644 venv/Lib/site-packages/flask/json/tag.py create mode 100644 venv/Lib/site-packages/flask/logging.py create mode 100644 venv/Lib/site-packages/flask/py.typed create mode 100644 venv/Lib/site-packages/flask/sansio/README.md create mode 100644 venv/Lib/site-packages/flask/sansio/__pycache__/app.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/sansio/__pycache__/blueprints.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/sansio/__pycache__/scaffold.cpython-310.pyc create mode 100644 venv/Lib/site-packages/flask/sansio/app.py create mode 100644 venv/Lib/site-packages/flask/sansio/blueprints.py create mode 100644 venv/Lib/site-packages/flask/sansio/scaffold.py create mode 100644 venv/Lib/site-packages/flask/sessions.py create mode 100644 venv/Lib/site-packages/flask/signals.py create mode 100644 venv/Lib/site-packages/flask/templating.py create mode 100644 venv/Lib/site-packages/flask/testing.py create mode 100644 venv/Lib/site-packages/flask/typing.py create mode 100644 venv/Lib/site-packages/flask/views.py create mode 100644 venv/Lib/site-packages/flask/wrappers.py create mode 100644 venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/LICENSE.txt create mode 100644 venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/METADATA create mode 100644 venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/RECORD create mode 100644 venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/itsdangerous/__init__.py create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/_json.cpython-310.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/encoding.cpython-310.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/exc.cpython-310.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/serializer.cpython-310.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/signer.cpython-310.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/timed.cpython-310.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/__pycache__/url_safe.cpython-310.pyc create mode 100644 venv/Lib/site-packages/itsdangerous/_json.py create mode 100644 venv/Lib/site-packages/itsdangerous/encoding.py create mode 100644 venv/Lib/site-packages/itsdangerous/exc.py create mode 100644 venv/Lib/site-packages/itsdangerous/py.typed create mode 100644 venv/Lib/site-packages/itsdangerous/serializer.py create mode 100644 venv/Lib/site-packages/itsdangerous/signer.py create mode 100644 venv/Lib/site-packages/itsdangerous/timed.py create mode 100644 venv/Lib/site-packages/itsdangerous/url_safe.py create mode 100644 venv/Lib/site-packages/jinja2-3.1.6.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/jinja2-3.1.6.dist-info/METADATA create mode 100644 venv/Lib/site-packages/jinja2-3.1.6.dist-info/RECORD create mode 100644 venv/Lib/site-packages/jinja2-3.1.6.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/jinja2-3.1.6.dist-info/entry_points.txt create mode 100644 venv/Lib/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt create mode 100644 venv/Lib/site-packages/jinja2/__init__.py create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/_identifier.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/async_utils.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/bccache.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/compiler.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/constants.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/debug.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/defaults.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/environment.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/exceptions.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/ext.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/filters.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/idtracking.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/lexer.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/loaders.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/meta.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/nativetypes.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/nodes.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/optimizer.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/parser.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/runtime.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/sandbox.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/tests.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/utils.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/__pycache__/visitor.cpython-310.pyc create mode 100644 venv/Lib/site-packages/jinja2/_identifier.py create mode 100644 venv/Lib/site-packages/jinja2/async_utils.py create mode 100644 venv/Lib/site-packages/jinja2/bccache.py create mode 100644 venv/Lib/site-packages/jinja2/compiler.py create mode 100644 venv/Lib/site-packages/jinja2/constants.py create mode 100644 venv/Lib/site-packages/jinja2/debug.py create mode 100644 venv/Lib/site-packages/jinja2/defaults.py create mode 100644 venv/Lib/site-packages/jinja2/environment.py create mode 100644 venv/Lib/site-packages/jinja2/exceptions.py create mode 100644 venv/Lib/site-packages/jinja2/ext.py create mode 100644 venv/Lib/site-packages/jinja2/filters.py create mode 100644 venv/Lib/site-packages/jinja2/idtracking.py create mode 100644 venv/Lib/site-packages/jinja2/lexer.py create mode 100644 venv/Lib/site-packages/jinja2/loaders.py create mode 100644 venv/Lib/site-packages/jinja2/meta.py create mode 100644 venv/Lib/site-packages/jinja2/nativetypes.py create mode 100644 venv/Lib/site-packages/jinja2/nodes.py create mode 100644 venv/Lib/site-packages/jinja2/optimizer.py create mode 100644 venv/Lib/site-packages/jinja2/parser.py create mode 100644 venv/Lib/site-packages/jinja2/py.typed create mode 100644 venv/Lib/site-packages/jinja2/runtime.py create mode 100644 venv/Lib/site-packages/jinja2/sandbox.py create mode 100644 venv/Lib/site-packages/jinja2/tests.py create mode 100644 venv/Lib/site-packages/jinja2/utils.py create mode 100644 venv/Lib/site-packages/jinja2/visitor.py create mode 100644 venv/Lib/site-packages/markupsafe-3.0.3.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/markupsafe-3.0.3.dist-info/METADATA create mode 100644 venv/Lib/site-packages/markupsafe-3.0.3.dist-info/RECORD create mode 100644 venv/Lib/site-packages/markupsafe-3.0.3.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/markupsafe-3.0.3.dist-info/licenses/LICENSE.txt create mode 100644 venv/Lib/site-packages/markupsafe-3.0.3.dist-info/top_level.txt create mode 100644 venv/Lib/site-packages/markupsafe/__init__.py create mode 100644 venv/Lib/site-packages/markupsafe/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/markupsafe/__pycache__/_native.cpython-310.pyc create mode 100644 venv/Lib/site-packages/markupsafe/_native.py create mode 100644 venv/Lib/site-packages/markupsafe/_speedups.c create mode 100644 venv/Lib/site-packages/markupsafe/_speedups.cp310-win_amd64.pyd create mode 100644 venv/Lib/site-packages/markupsafe/_speedups.pyi create mode 100644 venv/Lib/site-packages/markupsafe/py.typed create mode 100644 venv/Lib/site-packages/werkzeug-3.1.8.dist-info/INSTALLER create mode 100644 venv/Lib/site-packages/werkzeug-3.1.8.dist-info/METADATA create mode 100644 venv/Lib/site-packages/werkzeug-3.1.8.dist-info/RECORD create mode 100644 venv/Lib/site-packages/werkzeug-3.1.8.dist-info/WHEEL create mode 100644 venv/Lib/site-packages/werkzeug-3.1.8.dist-info/licenses/LICENSE.txt create mode 100644 venv/Lib/site-packages/werkzeug/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/_internal.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/_reloader.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/exceptions.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/formparser.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/http.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/local.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/security.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/serving.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/test.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/testapp.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/urls.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/user_agent.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/utils.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/__pycache__/wsgi.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/_internal.py create mode 100644 venv/Lib/site-packages/werkzeug/_reloader.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/__pycache__/accept.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/__pycache__/auth.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/__pycache__/cache_control.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/__pycache__/csp.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/__pycache__/etag.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/__pycache__/file_storage.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/__pycache__/headers.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/__pycache__/mixins.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/__pycache__/range.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/__pycache__/structures.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/accept.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/auth.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/cache_control.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/csp.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/etag.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/file_storage.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/headers.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/mixins.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/range.py create mode 100644 venv/Lib/site-packages/werkzeug/datastructures/structures.py create mode 100644 venv/Lib/site-packages/werkzeug/debug/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/debug/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/debug/__pycache__/console.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/debug/__pycache__/repr.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/debug/__pycache__/tbtools.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/debug/console.py create mode 100644 venv/Lib/site-packages/werkzeug/debug/repr.py create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/ICON_LICENSE.md create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/console.png create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/debugger.js create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/less.png create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/more.png create mode 100644 venv/Lib/site-packages/werkzeug/debug/shared/style.css create mode 100644 venv/Lib/site-packages/werkzeug/debug/tbtools.py create mode 100644 venv/Lib/site-packages/werkzeug/exceptions.py create mode 100644 venv/Lib/site-packages/werkzeug/formparser.py create mode 100644 venv/Lib/site-packages/werkzeug/http.py create mode 100644 venv/Lib/site-packages/werkzeug/local.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/lint.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/middleware/dispatcher.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/http_proxy.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/lint.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/profiler.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/proxy_fix.py create mode 100644 venv/Lib/site-packages/werkzeug/middleware/shared_data.py create mode 100644 venv/Lib/site-packages/werkzeug/py.typed create mode 100644 venv/Lib/site-packages/werkzeug/routing/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/routing/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/routing/__pycache__/converters.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/routing/__pycache__/exceptions.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/routing/__pycache__/map.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/routing/__pycache__/matcher.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/routing/__pycache__/rules.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/routing/converters.py create mode 100644 venv/Lib/site-packages/werkzeug/routing/exceptions.py create mode 100644 venv/Lib/site-packages/werkzeug/routing/map.py create mode 100644 venv/Lib/site-packages/werkzeug/routing/matcher.py create mode 100644 venv/Lib/site-packages/werkzeug/routing/rules.py create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__pycache__/http.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__pycache__/request.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__pycache__/response.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/sansio/__pycache__/utils.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/sansio/http.py create mode 100644 venv/Lib/site-packages/werkzeug/sansio/multipart.py create mode 100644 venv/Lib/site-packages/werkzeug/sansio/request.py create mode 100644 venv/Lib/site-packages/werkzeug/sansio/response.py create mode 100644 venv/Lib/site-packages/werkzeug/sansio/utils.py create mode 100644 venv/Lib/site-packages/werkzeug/security.py create mode 100644 venv/Lib/site-packages/werkzeug/serving.py create mode 100644 venv/Lib/site-packages/werkzeug/test.py create mode 100644 venv/Lib/site-packages/werkzeug/testapp.py create mode 100644 venv/Lib/site-packages/werkzeug/urls.py create mode 100644 venv/Lib/site-packages/werkzeug/user_agent.py create mode 100644 venv/Lib/site-packages/werkzeug/utils.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__init__.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/request.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/__pycache__/response.cpython-310.pyc create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/request.py create mode 100644 venv/Lib/site-packages/werkzeug/wrappers/response.py create mode 100644 venv/Lib/site-packages/werkzeug/wsgi.py create mode 100644 venv/Scripts/flask.exe diff --git a/core/flaskui/__pycache__/flask.cpython-310.pyc b/core/flaskui/__pycache__/flask.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1372f4753ee15f5b676b2466393a6f88ca6e307d GIT binary patch literal 524 zcmZXR%}T>S5XX0uY`Yd)U%`Wy2)1~YQv3kHOA+)G0x@J)8qG)9B%nP>EBYGt=&gA2 zqR+6o<|+t2f(60Z?ZJQ(c4l^Fcm6-JX}jG*U>85%;U~lowW*s0n|)Z>AvlH@j?g*c zm>V+xHoC^!i4dg zKo#p|z~%^6M#1GEJHv3<7*1OHe0P+`k&T@V!Qr~_avqN(Kj+U#C}8cDJN}+~D$*++ z56DI^(LCaGDH*dq1~*A zCU(fN0Страница чатов" + diff --git a/core/flaskui/templates/chats.html b/core/flaskui/templates/chats.html new file mode 100644 index 0000000..0ad5ba1 --- /dev/null +++ b/core/flaskui/templates/chats.html @@ -0,0 +1,18 @@ + + + + + + Страница чатов + + + + + + + + + + \ No newline at end of file diff --git a/core/flaskui/templates/index.html b/core/flaskui/templates/index.html new file mode 100644 index 0000000..6d93ea5 --- /dev/null +++ b/core/flaskui/templates/index.html @@ -0,0 +1,22 @@ + + + + + + + + + + Главная страница + + + + + + + + + + \ No newline at end of file diff --git a/main.py b/main.py index 357d36c..7dd6e30 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,5 @@ import sys -from PySide6.QtWidgets import QApplication -from core.ui.uimain import MainWindow - +from core.flaskui.flask import ui if __name__ == "__main__": - app = QApplication(sys.argv) - window = MainWindow() - window.show() - sys.exit(app.exec()) \ No newline at end of file + ui.run(debug=True) \ No newline at end of file diff --git a/venv/Lib/site-packages/blinker-1.9.0.dist-info/INSTALLER b/venv/Lib/site-packages/blinker-1.9.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/blinker-1.9.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/blinker-1.9.0.dist-info/LICENSE.txt b/venv/Lib/site-packages/blinker-1.9.0.dist-info/LICENSE.txt new file mode 100644 index 0000000..79c9825 --- /dev/null +++ b/venv/Lib/site-packages/blinker-1.9.0.dist-info/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright 2010 Jason Kirtland + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/venv/Lib/site-packages/blinker-1.9.0.dist-info/METADATA b/venv/Lib/site-packages/blinker-1.9.0.dist-info/METADATA new file mode 100644 index 0000000..6d343f5 --- /dev/null +++ b/venv/Lib/site-packages/blinker-1.9.0.dist-info/METADATA @@ -0,0 +1,60 @@ +Metadata-Version: 2.3 +Name: blinker +Version: 1.9.0 +Summary: Fast, simple object-to-object and broadcast signaling +Author: Jason Kirtland +Maintainer-email: Pallets Ecosystem +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Typing :: Typed +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://blinker.readthedocs.io +Project-URL: Source, https://github.com/pallets-eco/blinker/ + +# Blinker + +Blinker provides a fast dispatching system that allows any number of +interested parties to subscribe to events, or "signals". + + +## Pallets Community Ecosystem + +> [!IMPORTANT]\ +> This project is part of the Pallets Community Ecosystem. Pallets is the open +> source organization that maintains Flask; Pallets-Eco enables community +> maintenance of related projects. If you are interested in helping maintain +> this project, please reach out on [the Pallets Discord server][discord]. +> +> [discord]: https://discord.gg/pallets + + +## Example + +Signal receivers can subscribe to specific senders or receive signals +sent by any sender. + +```pycon +>>> from blinker import signal +>>> started = signal('round-started') +>>> def each(round): +... print(f"Round {round}") +... +>>> started.connect(each) + +>>> def round_two(round): +... print("This is round two.") +... +>>> started.connect(round_two, sender=2) + +>>> for round in range(1, 4): +... started.send(round) +... +Round 1! +Round 2! +This is round two. +Round 3! +``` + diff --git a/venv/Lib/site-packages/blinker-1.9.0.dist-info/RECORD b/venv/Lib/site-packages/blinker-1.9.0.dist-info/RECORD new file mode 100644 index 0000000..e6ed9e3 --- /dev/null +++ b/venv/Lib/site-packages/blinker-1.9.0.dist-info/RECORD @@ -0,0 +1,12 @@ +blinker-1.9.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +blinker-1.9.0.dist-info/LICENSE.txt,sha256=nrc6HzhZekqhcCXSrhvjg5Ykx5XphdTw6Xac4p-spGc,1054 +blinker-1.9.0.dist-info/METADATA,sha256=uIRiM8wjjbHkCtbCyTvctU37IAZk0kEe5kxAld1dvzA,1633 +blinker-1.9.0.dist-info/RECORD,, +blinker-1.9.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82 +blinker/__init__.py,sha256=I2EdZqpy4LyjX17Hn1yzJGWCjeLaVaPzsMgHkLfj_cQ,317 +blinker/__pycache__/__init__.cpython-310.pyc,, +blinker/__pycache__/_utilities.cpython-310.pyc,, +blinker/__pycache__/base.cpython-310.pyc,, +blinker/_utilities.py,sha256=0J7eeXXTUx0Ivf8asfpx0ycVkp0Eqfqnj117x2mYX9E,1675 +blinker/base.py,sha256=QpDuvXXcwJF49lUBcH5BiST46Rz9wSG7VW_p7N_027M,19132 +blinker/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/Lib/site-packages/blinker-1.9.0.dist-info/WHEEL b/venv/Lib/site-packages/blinker-1.9.0.dist-info/WHEEL new file mode 100644 index 0000000..e3c6fee --- /dev/null +++ b/venv/Lib/site-packages/blinker-1.9.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.10.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/Lib/site-packages/blinker/__init__.py b/venv/Lib/site-packages/blinker/__init__.py new file mode 100644 index 0000000..1772fa4 --- /dev/null +++ b/venv/Lib/site-packages/blinker/__init__.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from .base import ANY +from .base import default_namespace +from .base import NamedSignal +from .base import Namespace +from .base import Signal +from .base import signal + +__all__ = [ + "ANY", + "default_namespace", + "NamedSignal", + "Namespace", + "Signal", + "signal", +] diff --git a/venv/Lib/site-packages/blinker/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/blinker/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a4fd5f97532fac5b2fd8a0d31c2c3884862939c GIT binary patch literal 457 zcmaKo%Sr<=6o!+zWG-z%A7d8kUR10*L5k3oVIZbWT4FPql4J_LjyqpzwywH!TUVZp zSX_Am|M~utlWRMflnCPE^PsLV~=vM(81 zb-uHbF&qA%PZr$C|JU^yV_a*--jSVz&4PU0O)u+5+rG%9clGps**05l;9d1jnqB>% x7PV7ep20&ld@WtQ(8_G2yf*pbjJ3sH_m1^Nt@Dws0;;KL+V};iXx&Q6(ood2_QL%CSK2MI*r#}W@b|* zT233O2mVBIjKnYUl@m9vu>p66IR^J4i7e8 z5<;~CfD=x0GN1{i2s^nma1(C|H2iP}W8noo2X`Y@>8f?1G+a*{Ydf*FY`N#b!wG`PD)_|j|Ar`+eQvv$(t z%RJ!oppJi|SPK;E@0#I$GYHptldS!n4`$m|7S|%+_s4Z$WEMz8BXkMg6 zcyLhmUW&{-i4G3p@CPvzsS!NP(mdCp5@w_dU=%?JL#G1~N_$r(;=tn8*Vn^Z(v!#i zvV0tcQooI`Y&&XH-*JoIlHsIXPv1B3W}a&OQ>u2k%*?JfYISuuvY9Y-9*Im+Tj7mejy<8SYLOo+6BXen_(DCp@Ex`5n;n!FI0S#!| z38+U;JGHChsWz3jRTknc%h3z?Z(?HMX|$X{MQjr_58F#>b&s6N(<1NNjamjmJ>3DQ zYB44YX&4J(r}4Ico_23U*R+fR{(2yczm*E5O3<5AiE0J*Aod@qgfVE~Kyq29si0Z#A1R4q)?FcWzWEN^1Eh%6Xbt8N_9b+veyISjL?+}hY+OtL-9mz2k{ZRpqMhFW6 zqayWnlT#SJ%9VwQz%YPk+-gh3AEZZuN&YF&>SFcox*f-JTdw*H&$h6W_MqCYKFH!t zfYHX?U3egNclP&JLoDF^aBSBrXl01LRnRWnw21}%5P%y;2k62vouGT&YseNkq~u4y zsXoRDqonW#NJDGFDk9#bM_uyP4iLWvM2%^UT_KrvdO-daoWo&7bluoovWrf8Oq|YqNeg+!TSk1 zS?#SImA`W`6!17GD-iXxZnkR9*krA%4O^=#Y#rN^#>#Ayw$q|5+J-N1I&ScAarqzg C^6vHk literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/blinker/__pycache__/base.cpython-310.pyc b/venv/Lib/site-packages/blinker/__pycache__/base.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..80b6577dc4fe2adc9b121baec4fd2adb5e71ac02 GIT binary patch literal 17590 zcmeHPON<=HdG7A%nVp@TT`rdok<_d8j8<8V9=4-+P0ON4QKBqwWQmfZA?4ZLsovco z=drBrA+?iXESU_%i<4U}b^;Oz66lg!ZULNQfaH*4Fo#?W1n|vh9{Q>c9T~`ych!_GV`*7Ct}y`G2ySlqWU%s!z#pxn9O^p*`KH)GJcQZO?S7^{SMM?b*&;eNM`y z_P)-1eIDg0Rc`O^EYugIJdN^!`T;3dP(D~cXj{S5g+pp4a4#HIRg~{rcaBaW zNN`ZiU9;;)r!ltrvDYlMPt9Mq)ciH4em~Cks|B1b1ovMn)E~h40d)}P2ZINLhk^&# z3a&ql>xa}~TtAGqJzRMtc=(!Ce-!l|Q}-RWrmf)73y;?y$Nk6Dk>jAjJJ#aSpOI#Z z_GrfMc6*T@wR+ufh36_*_xtTgwVDx5=bF846nqqQ{I0(dXk6LX^qZT3YHaD=R-mJ8 zTsqJSn?2p@N3CwK-tRWq*v~+Hwm;SJF9wa4La{7G9jv2VdS|<{)@zUK(>0tG*LwY~ z8Wm2ge9+{$@}DA3Ugmt3&|+ZKZBWX=yxR3b;MQHdU9nzLPB5hk@~&m|sw%3|br*D< z4tRzclCz3BtzJ{JYVLZmj@eNA)*bb_I;`%)c~zZJZ>VGHew1f}xxihw)wk6{>f!4} zw6iamQ*SCyJtFPQPfw$zY4zxjZS@`Xlv=z#Ro@?9dm7hP)HAAvYYXZR)U)cj>u&vk zdP}{azJk(0Y5BYAtLkg$>5zI`eO)c%`eF5sIO^tIMoO_@S3=(M6J(6vg%!)JNR*M7%Sz2>rab~9ih zjC8BJ;jQXp}E9(V)B#Tk`crzY}z$5T(G|!kYwq z0NlEYy34E(dybbqQytGnU!z>gtuizqq=Z8(6L8p>hZY_mv9N^0N1Q?S-_n z!K3LbuVWTG-%FbJ;x`RzpV^}V=HWA2&w*e#bHkv${+Xrk!|BdTCzj8@rF$1Zl<@qC zGtFLS%kQGM^Ou9}<@0a0*3O5mD0pVeZ(f8vhUeGXt?tD@KfC6KL2YY$w2*Yzh&v3_ zBH9fd94ga;qlRs2yKCKXUiE%Fx>)dkiYC&xyW@l^Lcv|;^b1X9zOi&3Q(}MKe8y9l zOlfWs&u)U7V)jDG$c*xye!ACfZ$JICcV#o^CJdDy1L~4b1C@>;_-L!$YPO>Gb`3n6 zwb~R^_11#4@wIJ?$MgG9uLDVKdRe<^W74hfk>}dm;GFe92jFp)F%g6|7Ke>q5PCpd z3KS^5MM)3|Nn1-qgp$+kdF@^o_{|adm;F}TUuy?m)Z=-d5@8-rS~uv>3e(0Cm`E?2 zz`L};;$6JVWwf~kbwIgBXIcAr=diyPHg#)_m=pEV$Cv#m(#xsvWz4!Nvj)_GF4^$< zTM(gUKLpW)ETD4-{T=ID*pXP%2>ALxK7#ZW&2DFnC~667v;T^Z``esQ|3Kp|{fO1|yfp zCdD}Uu*@&PH(W+xXzSw89$F2nW#MSUsNk6s6^7OYs6&*CxpGNr!SDfCJ1eIb%cH4A z1IW~9jHZ*}hof>UY;^ijpmYIU=zTbhs*SbnMp9)|;h7=xXtqJ_jnBeGM<3=xe3?lK zl^cy#w-q%SXK*tlHC?-8JFfd1r%)jn z_B99OD-PY`R#(Vt%D7e<7LQstr&z}Z!7r4DuACR;oGsh>yTc;xOkYN6FpE~v*3HTg zHKf()Vez>2zSW&}t!QTZE!*PX2UgdL)!H~Que9)*R3kvvvHp{{%&C^WD1f&%B!|sY^UkcJb@l>-1mqZNFd;SG z30(rm;~t3Jd_yzxns*`vM6cs>Scxf;Q)5Ezp4Wlh-`av=FwcgVb!nCKp0Ka7x>2hW z0AASyKQY}Xh+qOuVCF>TM#~vMb3L^z<`pm0u^L z`|U8mT#Pl>!|UlQtuXK~gk&!AV4lfT(xlPY9t0pqIDHhjv{=xO<3~Ti!|(9Iye4qz zSsX?sutMX^DLu`T!wqq#Vs)6pv3`ivA7PCGOjPah^iiH39Dl&f@o4%iZ2niZ?&%RA znOEOX!poz>Kyw|%Nx+|q>3$Al34b4l^87*DbsXS) z^>)RbcjoN{2Opfz-L4i~+qu0^aG(>)cKNnjuKrgQ_ncq5#lc*x72*a@?<_QHCu68N zckc9uOWx@Zn+5cce5eZe#Wg#mMp9FsTA$jVI_m}Hs^azP)xxjs|pL+z0v>Oife+88=<^Ydrd!G-JY1J=rQh$k) zRJ^}socyMVXiyvMWxzk5GUUiv?}~A#=OO94+**c{V2|2ja^A3R!f?A`-*9dfXu`b% ze;h$D{S{Qg&)4wtS;2r_ipV)De>xW7p1y^eAt7eYcJ09v;{esT-12M3+dW8i_);x> zW@XWp9>k0+;T%fVYBg9C z66D!=uvvR8=mvnfo<2t}V@W&?a~ua!f-03ylLY)d6h5KdEB2_weL%#)j4HebD;9RC zb<-Z&w1pMGH9Nb4xASknb2&jvguz7^CK6qQrNB^8(Afg$>K62T{DL7+G|@7vtAt=6 zUOMBU#IT@dTQFUGR3&_&+8RiH5td%ig#Qv6OF)c~Io{M1y?R&YY`*U#Yqb4NO`rsI>4zq}b0V#%s3*)52@nb#Jms3X;(Mq zk+e~X-vIf5kCrusZxP>b6HL1(5K47%16pXqLTDm}&r{ZiPhBlW&JYU``iY^V7_c}N z!TrBx|A2u2Tp!x&HljI&zq0?-zFO**5p)>4GISBjC=N^O4&nGxb?7oc?>`HTMZ+1^ zUpr$8OfXQG(bbf>W5nHMW2O*1=@Y-oSYRGa6N{GqcV-b!y6$Ovi9>GeQgO=e>b84t z%i;vkpOb;0Xxh^1>Ql)$mM|hNVR)DQc0X98BH^Uvv7B}}0s;t>CJ&i)gLwFm1gujT zPjSgxL--YeUV5jk?mC*3U~Mi^l7uniXlJ^Xl3!4Oi|?nMdD@2~$-pJlRx{W#c9qOW zdTwx*t8e}WJQKu7Gr^M(G~Gg|uVAgeu|X#nK}5f`u^AyS0ESgd2{l3du-}nB%#_2` z!CNDiof5Cr0e!f70tYA~h=2uFoy=%+Ud@CrAgq-=Ho;8>RY2+`P|t&_D&ZS&TG~4S zBiii2vmSdiAACvc#$Au2!m{`Ncx(hAVunVtF{P|7Xp*+Zp3@agz$fet@(qlR4hD-; zVvmGI1Yc|3X$U~@kxy~~H+uax=+z$&JJ3fn`KzJ)cY`&M9#Q0SVx-5II?j5P( zpco6|udJvgZRh4AGnAW~KEnTIx$4ES*F3>xEWR1Ua-4dQk3l2z_ zHr52cD!?4S;v<6fr7EECc+8enDc)Gj#h_PWoc$}fX-eG)J=2Gc}Mfoc63 zOX4KG&eDXFR3YdZ-EteZL&h$gk4s)2s2^|$)d1@KIiNPr+~$$BxCoZr6UZzF`5HD-)WTB}&jP%u!j(vMYs5Rq#_r?~P@ z>kC;}<^^^o45Y(Kn9LZlA-uOX4CKo}&3h+EQ)bdiRMD#v;_zuSO2R=bXWjm=||Fu=8v_X12GVI37i#)0cb_$FE7{ z|6^P=QM9lmyb5OytI!TtiZ3`=|2@XvsBo10jjKnkSFEdr?o@L1rh5zL!-BYpa2hXI zH?dZIi|g2s3P`|nmmZ6-QXNfQC|`5*>u?ug9t_bJ?wx{@IX!fv0@sQ!JvJ;c;DFzv z`Q>;P9sdl-^Fb?sO*>|2a?Un`%syr{o$6gr=52cK{W~VWFp?4b zrjUTNIkL17CX6Q^w}Z8h43LX0n3w?9wEhI^ns-aZ;J3@RD~lDwwX&@D2xebccjO|8 zXM0rG2%=G?g%xg--!__}aAS#dRN!F-3v@|-)ff~L3G0QH3ib1Bkx-1>2$A6lG}fnZ z?=%_yNfZFXGi3LQa~KR?cB&{lcGbbE8_Euj5QSe14#t9z(uh#egw2?K7|7rkO2Q87 zC?K=K0*6gSHn`PA_=33=1^k-43+rZK=njSNn51&S1*b3Kw|JqXe^-9N;ml${mP5fr zkQqhh7t!M~#(yfM1o$o)7<`s>jbx#>BECsRGCVYnni39elWmyuRIZRg$Rd>jl6Gz%E|hz1P|WYQ1^3q(60 z-4qcII47$MmlklRl+*>UK*zH#u`;Q{)`qs6O9+g0G#4-+MM5SIjOA;aq zM^k(mC}AO4vM3`W#y1(2*80th!7hiCq$GZAnIu)_A~$lJnGIc~MuF^152BMWH4!(0-wu9~ z%(*l#0l1va9%^tbW2uNKPbLC)_vDML`r8=U&SUrRZnA2pA3*gcD42d&=Gz_M+wpFM z@aQcotlj{Z+^`~pB$(A~X5CRC+zv;xNMJ*1hu>}ufC*Yefm4n+GzayWW7u8fKzLr3 z)~4dTia+NxO8Qt~-ga*fYB3Uy=Taop$X5B{$GOJGUNo~FvR4yKP8Ju0of^C^-G7fI zjzK?-!=JH#zWDr|3h@yXkwvv5A5XR=2BWLz42MANSYU2vB z0D&3q4TCdtajK-5y`p5*==ac)V21Ws`a>K5#xm!eSg@CH)(gCuvX|y`|23zU@H_`> zSRivP?K!=%;h!!bPqLQJrn%NFQUkUY?v7Wkq~DCW)>uuLYYC;lOqjb`g6~xt+<%w; z$7o49QYcPR8jqs@d=>v|owPf26@J*zzFD|sU9)jsJ?~s~h}hvVTt&{M0*zs3&Rr6V z7Z&ELXz(orOcBlQN;*--+Z<=Q>C^lSzKaOxxbV<_hF4_VmSe4}BtGnk~L! zGVhnf@{MD^bmlufc#v0uHReQzS*qA+Y@)ch)n<*Vm1af%G973h9V)qs+bb`;ycs8Q#x;h+U2j>jO(J*9;p6gODt=n09ldQJow{&eWa~96hZ{=>49+Lwx z>MdbZaYS-GdOqCvcx{EYW)u7dS4FCY{boj8K28FlRnB^jkxde0D@0Txc64G6eC;xFS3@*@?U+<3LJo-|R~gI>3*nAS!7U^C**VNQ)&>cD|0S31F*q zaL5vs&0UsGRc#$p{Q@@d$<-a9j(yE(3gycn7ba_;fW7;@tIENPz@ zOyr~`BN^n-Kyj&%mRwUZzWCt9Q<>XC5lSe|HN$MP!|rqy66R5KH!5<3Iu|(caCOhc z_^J>1CEq@F4BW zQ?K9@K9uw-HP7(=8+WSdPWH-I0Hpajh(6k8jH+i6wsgdAd={I>e#lv$vh#5`;S5pY|1`jPB zGAYcgC9)HZmXOa^EX0kA3{oH+cnT6&!Df+z^9$7%oMVnNciXuS*XEqxIOVzjaK2`K zD=$1D%XY^BR-Nv5J%Jtbgc8K?@^p;n5^xlV#}(dn~6LW)~~?H)AV4@vp=+GA% zzTxfIFTwHCc#|lH-u{j?RG*up`Z*VUQ(}t|Fw1af;c|H0MGXZs47OHIJP)vDOxuA|uD9Y*<^l z^Bc=JFk9(|Wqwa`91o#Dj>7(|8SV)~loB&Hw2wnC)Az$j?7>c)Tr??QF{2pEk~fo8 zAhJ5Q>@*o^5nC^dRb#cj){Ht0K7>OP=`EZ(9C&9d|4ccf9KTG87xoDAt*SQPv@;>Du^NQ*V_#OsW2FG>{12|-=2_5xSG!BJ7L9FlNYHofq zI{h(j{F3c+P-Sc0*1R&>AIF=;CJ-UhB=zDGv<}w$;5k?anq$KMdoX*ckm4WK{Iw>w z72#kv^?BYc;a>*OkhUZJU7jN)U3Olvw(J-kRp4MS$-CWJ6YF3!Elwo1Xog}-jHcil zZo%J`?4Ww3fqx^>MlM(onw?M6XjWI)_7t}&z2{@2(n-*b@nrnRi0-z@nwO>> Symbol('foo') is Symbol('foo') + True + >>> Symbol('foo') + foo + """ + + symbols: t.ClassVar[dict[str, Symbol]] = {} + + def __new__(cls, name: str) -> Symbol: + if name in cls.symbols: + return cls.symbols[name] + + obj = super().__new__(cls) + cls.symbols[name] = obj + return obj + + def __init__(self, name: str) -> None: + self.name = name + + def __repr__(self) -> str: + return self.name + + def __getnewargs__(self) -> tuple[t.Any, ...]: + return (self.name,) + + +def make_id(obj: object) -> c.Hashable: + """Get a stable identifier for a receiver or sender, to be used as a dict + key or in a set. + """ + if inspect.ismethod(obj): + # The id of a bound method is not stable, but the id of the unbound + # function and instance are. + return id(obj.__func__), id(obj.__self__) + + if isinstance(obj, (str, int)): + # Instances with the same value always compare equal and have the same + # hash, even if the id may change. + return obj + + # Assume other types are not hashable but will always be the same instance. + return id(obj) + + +def make_ref(obj: T, callback: c.Callable[[ref[T]], None] | None = None) -> ref[T]: + if inspect.ismethod(obj): + return WeakMethod(obj, callback) # type: ignore[arg-type, return-value] + + return ref(obj, callback) diff --git a/venv/Lib/site-packages/blinker/base.py b/venv/Lib/site-packages/blinker/base.py new file mode 100644 index 0000000..d051b94 --- /dev/null +++ b/venv/Lib/site-packages/blinker/base.py @@ -0,0 +1,512 @@ +from __future__ import annotations + +import collections.abc as c +import sys +import typing as t +import weakref +from collections import defaultdict +from contextlib import contextmanager +from functools import cached_property +from inspect import iscoroutinefunction + +from ._utilities import make_id +from ._utilities import make_ref +from ._utilities import Symbol + +F = t.TypeVar("F", bound=c.Callable[..., t.Any]) + +ANY = Symbol("ANY") +"""Symbol for "any sender".""" + +ANY_ID = 0 + + +class Signal: + """A notification emitter. + + :param doc: The docstring for the signal. + """ + + ANY = ANY + """An alias for the :data:`~blinker.ANY` sender symbol.""" + + set_class: type[set[t.Any]] = set + """The set class to use for tracking connected receivers and senders. + Python's ``set`` is unordered. If receivers must be dispatched in the order + they were connected, an ordered set implementation can be used. + + .. versionadded:: 1.7 + """ + + @cached_property + def receiver_connected(self) -> Signal: + """Emitted at the end of each :meth:`connect` call. + + The signal sender is the signal instance, and the :meth:`connect` + arguments are passed through: ``receiver``, ``sender``, and ``weak``. + + .. versionadded:: 1.2 + """ + return Signal(doc="Emitted after a receiver connects.") + + @cached_property + def receiver_disconnected(self) -> Signal: + """Emitted at the end of each :meth:`disconnect` call. + + The sender is the signal instance, and the :meth:`disconnect` arguments + are passed through: ``receiver`` and ``sender``. + + This signal is emitted **only** when :meth:`disconnect` is called + explicitly. This signal cannot be emitted by an automatic disconnect + when a weakly referenced receiver or sender goes out of scope, as the + instance is no longer be available to be used as the sender for this + signal. + + An alternative approach is available by subscribing to + :attr:`receiver_connected` and setting up a custom weakref cleanup + callback on weak receivers and senders. + + .. versionadded:: 1.2 + """ + return Signal(doc="Emitted after a receiver disconnects.") + + def __init__(self, doc: str | None = None) -> None: + if doc: + self.__doc__ = doc + + self.receivers: dict[ + t.Any, weakref.ref[c.Callable[..., t.Any]] | c.Callable[..., t.Any] + ] = {} + """The map of connected receivers. Useful to quickly check if any + receivers are connected to the signal: ``if s.receivers:``. The + structure and data is not part of the public API, but checking its + boolean value is. + """ + + self.is_muted: bool = False + self._by_receiver: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) + self._by_sender: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) + self._weak_senders: dict[t.Any, weakref.ref[t.Any]] = {} + + def connect(self, receiver: F, sender: t.Any = ANY, weak: bool = True) -> F: + """Connect ``receiver`` to be called when the signal is sent by + ``sender``. + + :param receiver: The callable to call when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument + along with any extra keyword arguments. + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. A receiver may be connected + to multiple senders by calling :meth:`connect` multiple times. + :param weak: Track the receiver with a :mod:`weakref`. The receiver will + be automatically disconnected when it is garbage collected. When + connecting a receiver defined within a function, set to ``False``, + otherwise it will be disconnected when the function scope ends. + """ + receiver_id = make_id(receiver) + sender_id = ANY_ID if sender is ANY else make_id(sender) + + if weak: + self.receivers[receiver_id] = make_ref( + receiver, self._make_cleanup_receiver(receiver_id) + ) + else: + self.receivers[receiver_id] = receiver + + self._by_sender[sender_id].add(receiver_id) + self._by_receiver[receiver_id].add(sender_id) + + if sender is not ANY and sender_id not in self._weak_senders: + # store a cleanup for weakref-able senders + try: + self._weak_senders[sender_id] = make_ref( + sender, self._make_cleanup_sender(sender_id) + ) + except TypeError: + pass + + if "receiver_connected" in self.__dict__ and self.receiver_connected.receivers: + try: + self.receiver_connected.send( + self, receiver=receiver, sender=sender, weak=weak + ) + except TypeError: + # TODO no explanation or test for this + self.disconnect(receiver, sender) + raise + + return receiver + + def connect_via(self, sender: t.Any, weak: bool = False) -> c.Callable[[F], F]: + """Connect the decorated function to be called when the signal is sent + by ``sender``. + + The decorated function will be called when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument along + with any extra keyword arguments. + + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. A receiver may be connected + to multiple senders by calling :meth:`connect` multiple times. + :param weak: Track the receiver with a :mod:`weakref`. The receiver will + be automatically disconnected when it is garbage collected. When + connecting a receiver defined within a function, set to ``False``, + otherwise it will be disconnected when the function scope ends.= + + .. versionadded:: 1.1 + """ + + def decorator(fn: F) -> F: + self.connect(fn, sender, weak) + return fn + + return decorator + + @contextmanager + def connected_to( + self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY + ) -> c.Generator[None, None, None]: + """A context manager that temporarily connects ``receiver`` to the + signal while a ``with`` block executes. When the block exits, the + receiver is disconnected. Useful for tests. + + :param receiver: The callable to call when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument + along with any extra keyword arguments. + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. + + .. versionadded:: 1.1 + """ + self.connect(receiver, sender=sender, weak=False) + + try: + yield None + finally: + self.disconnect(receiver) + + @contextmanager + def muted(self) -> c.Generator[None, None, None]: + """A context manager that temporarily disables the signal. No receivers + will be called if the signal is sent, until the ``with`` block exits. + Useful for tests. + """ + self.is_muted = True + + try: + yield None + finally: + self.is_muted = False + + def send( + self, + sender: t.Any | None = None, + /, + *, + _async_wrapper: c.Callable[ + [c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]]], c.Callable[..., t.Any] + ] + | None = None, + **kwargs: t.Any, + ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: + """Call all receivers that are connected to the given ``sender`` + or :data:`ANY`. Each receiver is called with ``sender`` as a positional + argument along with any extra keyword arguments. Return a list of + ``(receiver, return value)`` tuples. + + The order receivers are called is undefined, but can be influenced by + setting :attr:`set_class`. + + If a receiver raises an exception, that exception will propagate up. + This makes debugging straightforward, with an assumption that correctly + implemented receivers will not raise. + + :param sender: Call receivers connected to this sender, in addition to + those connected to :data:`ANY`. + :param _async_wrapper: Will be called on any receivers that are async + coroutines to turn them into sync callables. For example, could run + the receiver with an event loop. + :param kwargs: Extra keyword arguments to pass to each receiver. + + .. versionchanged:: 1.7 + Added the ``_async_wrapper`` argument. + """ + if self.is_muted: + return [] + + results = [] + + for receiver in self.receivers_for(sender): + if iscoroutinefunction(receiver): + if _async_wrapper is None: + raise RuntimeError("Cannot send to a coroutine function.") + + result = _async_wrapper(receiver)(sender, **kwargs) + else: + result = receiver(sender, **kwargs) + + results.append((receiver, result)) + + return results + + async def send_async( + self, + sender: t.Any | None = None, + /, + *, + _sync_wrapper: c.Callable[ + [c.Callable[..., t.Any]], c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]] + ] + | None = None, + **kwargs: t.Any, + ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: + """Await all receivers that are connected to the given ``sender`` + or :data:`ANY`. Each receiver is called with ``sender`` as a positional + argument along with any extra keyword arguments. Return a list of + ``(receiver, return value)`` tuples. + + The order receivers are called is undefined, but can be influenced by + setting :attr:`set_class`. + + If a receiver raises an exception, that exception will propagate up. + This makes debugging straightforward, with an assumption that correctly + implemented receivers will not raise. + + :param sender: Call receivers connected to this sender, in addition to + those connected to :data:`ANY`. + :param _sync_wrapper: Will be called on any receivers that are sync + callables to turn them into async coroutines. For example, + could call the receiver in a thread. + :param kwargs: Extra keyword arguments to pass to each receiver. + + .. versionadded:: 1.7 + """ + if self.is_muted: + return [] + + results = [] + + for receiver in self.receivers_for(sender): + if not iscoroutinefunction(receiver): + if _sync_wrapper is None: + raise RuntimeError("Cannot send to a non-coroutine function.") + + result = await _sync_wrapper(receiver)(sender, **kwargs) + else: + result = await receiver(sender, **kwargs) + + results.append((receiver, result)) + + return results + + def has_receivers_for(self, sender: t.Any) -> bool: + """Check if there is at least one receiver that will be called with the + given ``sender``. A receiver connected to :data:`ANY` will always be + called, regardless of sender. Does not check if weakly referenced + receivers are still live. See :meth:`receivers_for` for a stronger + search. + + :param sender: Check for receivers connected to this sender, in addition + to those connected to :data:`ANY`. + """ + if not self.receivers: + return False + + if self._by_sender[ANY_ID]: + return True + + if sender is ANY: + return False + + return make_id(sender) in self._by_sender + + def receivers_for( + self, sender: t.Any + ) -> c.Generator[c.Callable[..., t.Any], None, None]: + """Yield each receiver to be called for ``sender``, in addition to those + to be called for :data:`ANY`. Weakly referenced receivers that are not + live will be disconnected and skipped. + + :param sender: Yield receivers connected to this sender, in addition + to those connected to :data:`ANY`. + """ + # TODO: test receivers_for(ANY) + if not self.receivers: + return + + sender_id = make_id(sender) + + if sender_id in self._by_sender: + ids = self._by_sender[ANY_ID] | self._by_sender[sender_id] + else: + ids = self._by_sender[ANY_ID].copy() + + for receiver_id in ids: + receiver = self.receivers.get(receiver_id) + + if receiver is None: + continue + + if isinstance(receiver, weakref.ref): + strong = receiver() + + if strong is None: + self._disconnect(receiver_id, ANY_ID) + continue + + yield strong + else: + yield receiver + + def disconnect(self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY) -> None: + """Disconnect ``receiver`` from being called when the signal is sent by + ``sender``. + + :param receiver: A connected receiver callable. + :param sender: Disconnect from only this sender. By default, disconnect + from all senders. + """ + sender_id: c.Hashable + + if sender is ANY: + sender_id = ANY_ID + else: + sender_id = make_id(sender) + + receiver_id = make_id(receiver) + self._disconnect(receiver_id, sender_id) + + if ( + "receiver_disconnected" in self.__dict__ + and self.receiver_disconnected.receivers + ): + self.receiver_disconnected.send(self, receiver=receiver, sender=sender) + + def _disconnect(self, receiver_id: c.Hashable, sender_id: c.Hashable) -> None: + if sender_id == ANY_ID: + if self._by_receiver.pop(receiver_id, None) is not None: + for bucket in self._by_sender.values(): + bucket.discard(receiver_id) + + self.receivers.pop(receiver_id, None) + else: + self._by_sender[sender_id].discard(receiver_id) + self._by_receiver[receiver_id].discard(sender_id) + + def _make_cleanup_receiver( + self, receiver_id: c.Hashable + ) -> c.Callable[[weakref.ref[c.Callable[..., t.Any]]], None]: + """Create a callback function to disconnect a weakly referenced + receiver when it is garbage collected. + """ + + def cleanup(ref: weakref.ref[c.Callable[..., t.Any]]) -> None: + # If the interpreter is shutting down, disconnecting can result in a + # weird ignored exception. Don't call it in that case. + if not sys.is_finalizing(): + self._disconnect(receiver_id, ANY_ID) + + return cleanup + + def _make_cleanup_sender( + self, sender_id: c.Hashable + ) -> c.Callable[[weakref.ref[t.Any]], None]: + """Create a callback function to disconnect all receivers for a weakly + referenced sender when it is garbage collected. + """ + assert sender_id != ANY_ID + + def cleanup(ref: weakref.ref[t.Any]) -> None: + self._weak_senders.pop(sender_id, None) + + for receiver_id in self._by_sender.pop(sender_id, ()): + self._by_receiver[receiver_id].discard(sender_id) + + return cleanup + + def _cleanup_bookkeeping(self) -> None: + """Prune unused sender/receiver bookkeeping. Not threadsafe. + + Connecting & disconnecting leaves behind a small amount of bookkeeping + data. Typical workloads using Blinker, for example in most web apps, + Flask, CLI scripts, etc., are not adversely affected by this + bookkeeping. + + With a long-running process performing dynamic signal routing with high + volume, e.g. connecting to function closures, senders are all unique + object instances. Doing all of this over and over may cause memory usage + to grow due to extraneous bookkeeping. (An empty ``set`` for each stale + sender/receiver pair.) + + This method will prune that bookkeeping away, with the caveat that such + pruning is not threadsafe. The risk is that cleanup of a fully + disconnected receiver/sender pair occurs while another thread is + connecting that same pair. If you are in the highly dynamic, unique + receiver/sender situation that has lead you to this method, that failure + mode is perhaps not a big deal for you. + """ + for mapping in (self._by_sender, self._by_receiver): + for ident, bucket in list(mapping.items()): + if not bucket: + mapping.pop(ident, None) + + def _clear_state(self) -> None: + """Disconnect all receivers and senders. Useful for tests.""" + self._weak_senders.clear() + self.receivers.clear() + self._by_sender.clear() + self._by_receiver.clear() + + +class NamedSignal(Signal): + """A named generic notification emitter. The name is not used by the signal + itself, but matches the key in the :class:`Namespace` that it belongs to. + + :param name: The name of the signal within the namespace. + :param doc: The docstring for the signal. + """ + + def __init__(self, name: str, doc: str | None = None) -> None: + super().__init__(doc) + + #: The name of this signal. + self.name: str = name + + def __repr__(self) -> str: + base = super().__repr__() + return f"{base[:-1]}; {self.name!r}>" # noqa: E702 + + +class Namespace(dict[str, NamedSignal]): + """A dict mapping names to signals.""" + + def signal(self, name: str, doc: str | None = None) -> NamedSignal: + """Return the :class:`NamedSignal` for the given ``name``, creating it + if required. Repeated calls with the same name return the same signal. + + :param name: The name of the signal. + :param doc: The docstring of the signal. + """ + if name not in self: + self[name] = NamedSignal(name, doc) + + return self[name] + + +class _PNamespaceSignal(t.Protocol): + def __call__(self, name: str, doc: str | None = None) -> NamedSignal: ... + + +default_namespace: Namespace = Namespace() +"""A default :class:`Namespace` for creating named signals. :func:`signal` +creates a :class:`NamedSignal` in this namespace. +""" + +signal: _PNamespaceSignal = default_namespace.signal +"""Return a :class:`NamedSignal` in :data:`default_namespace` with the given +``name``, creating it if required. Repeated calls with the same name return the +same signal. +""" diff --git a/venv/Lib/site-packages/blinker/py.typed b/venv/Lib/site-packages/blinker/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/click-8.3.3.dist-info/INSTALLER b/venv/Lib/site-packages/click-8.3.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/click-8.3.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/click-8.3.3.dist-info/METADATA b/venv/Lib/site-packages/click-8.3.3.dist-info/METADATA new file mode 100644 index 0000000..30fbba6 --- /dev/null +++ b/venv/Lib/site-packages/click-8.3.3.dist-info/METADATA @@ -0,0 +1,84 @@ +Metadata-Version: 2.4 +Name: click +Version: 8.3.3 +Summary: Composable command line interface toolkit +Maintainer-email: Pallets +Requires-Python: >=3.10 +Description-Content-Type: text/markdown +License-Expression: BSD-3-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Typing :: Typed +License-File: LICENSE.txt +Requires-Dist: colorama; platform_system == 'Windows' +Project-URL: Changes, https://click.palletsprojects.com/page/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/click/ + +
+ +# 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 + + +## A Simple Example + +```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() +``` + +``` +$ 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 + +## Contributing + +See our [detailed contributing documentation][contrib] for many ways to +contribute, including reporting issues, requesting features, asking or answering +questions, and making PRs. + +[contrib]: https://palletsprojects.com/contributing/ + diff --git a/venv/Lib/site-packages/click-8.3.3.dist-info/RECORD b/venv/Lib/site-packages/click-8.3.3.dist-info/RECORD new file mode 100644 index 0000000..978c3e2 --- /dev/null +++ b/venv/Lib/site-packages/click-8.3.3.dist-info/RECORD @@ -0,0 +1,40 @@ +click-8.3.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.3.3.dist-info/METADATA,sha256=_sjhT7_ZKYkyseqbWJtQA8eAcV2CeDJdcNrX_92pSCY,2621 +click-8.3.3.dist-info/RECORD,, +click-8.3.3.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +click-8.3.3.dist-info/licenses/LICENSE.txt,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click/__init__.py,sha256=tq5olp0lVuUNbwOj0p5nMVdqhOyYz4aZzF8S5T0aFMU,4526 +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__/_utils.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=v3xBZkFbvA1BXPRkFfBJc6-pIwPI7345m-kQEnpVAs4,18693 +click/_termui_impl.py,sha256=R3aYjPD2x1xgNXiXEqHSR-jAVhbXKkLKeqgr-4eDOkA,28066 +click/_textwrap.py,sha256=BOae0RQ6vg3FkNgSJyOoGzG1meGMxJ_ukWVZKx_v-0o,1400 +click/_utils.py,sha256=kZwtTf5gMuCilJJceS2iTCvRvCY-0aN5rJq8gKw7p8g,943 +click/_winconsole.py,sha256=_vxUuUaxwBhoR0vUWCNuHY8VUefiMdCIyU2SXPqoF-A,8465 +click/core.py,sha256=wzDpH4Wh45hBfsdw3BHqn_095457WRrb92dyaeIGkHk,134478 +click/decorators.py,sha256=5P7abhJtAQYp_KHgjUvhMv464ERwOzrv2enNknlwHyQ,18461 +click/exceptions.py,sha256=8utf8w6V5hJXMnO_ic1FNrtbwuEn1NUu1aDwV8UqnG4,9954 +click/formatting.py,sha256=RVfwwr0rwWNpgGr8NaHodPzkIr7_tUyVh_nDdanLMNc,9730 +click/globals.py,sha256=gM-Nh6A4M0HB_SgkaF5M4ncGGMDHc_flHXu9_oh4GEU,1923 +click/parser.py,sha256=Q31pH0FlQZEq-UXE_ABRzlygEfvxPTuZbWNh4xfXmzw,19010 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=Cc4GQUFuWpfQBa9sF5qXeeYI7n3tI_1k6ZdSn4BZbT0,20994 +click/termui.py,sha256=0xjUaEZJz1_1y8eqon2kOe2OXrmHzZCJoVohFb43yTs,31434 +click/testing.py,sha256=PYLYW4ZjCAP6HRWtNhFpr3imJxQVEPFq3EW3JL0LTfA,22714 +click/types.py,sha256=ek54BNSFwPKsqtfT7jsqcc4WHui8AIFVMKM4oVZIXhc,39927 +click/utils.py,sha256=vep6qqMkv3WkzA9OCu7f6Ku-3beZHNpo8qJqL-uzUk8,20282 diff --git a/venv/Lib/site-packages/click-8.3.3.dist-info/WHEEL b/venv/Lib/site-packages/click-8.3.3.dist-info/WHEEL new file mode 100644 index 0000000..d8b9936 --- /dev/null +++ b/venv/Lib/site-packages/click-8.3.3.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/Lib/site-packages/click-8.3.3.dist-info/licenses/LICENSE.txt b/venv/Lib/site-packages/click-8.3.3.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..d12a849 --- /dev/null +++ b/venv/Lib/site-packages/click-8.3.3.dist-info/licenses/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2014 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/click/__init__.py b/venv/Lib/site-packages/click/__init__.py new file mode 100644 index 0000000..fa485a0 --- /dev/null +++ b/venv/Lib/site-packages/click/__init__.py @@ -0,0 +1,124 @@ +""" +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 __future__ import annotations + +from .core import Argument as Argument +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 Option as Option +from .core import Parameter as Parameter +from .core import ParameterSource as ParameterSource +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 .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + + +def __getattr__(name: str) -> object: + import warnings + + if name == "BaseCommand": + from .core import _BaseCommand + + warnings.warn( + "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Command' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _BaseCommand + + if name == "MultiCommand": + from .core import _MultiCommand + + warnings.warn( + "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Group' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _MultiCommand + + if name == "OptionParser": + from .parser import _OptionParser + + warnings.warn( + "'OptionParser' is deprecated and will be removed in Click 9.0. The" + " old parser is available in 'optparse'.", + DeprecationWarning, + stacklevel=2, + ) + return _OptionParser + + if name == "__version__": + import importlib.metadata + import warnings + + warnings.warn( + "The '__version__' attribute is deprecated and will be removed in" + " Click 9.1. Use feature detection or" + " 'importlib.metadata.version(\"click\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("click") + + raise AttributeError(name) diff --git a/venv/Lib/site-packages/click/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3b7141873aadfca45d5fbb453989ad0bc887c22 GIT binary patch literal 3677 zcmc(iNmCrl5yxkS*%wyrYYQ#INHBnQ5Rw>HA?X1jAZ%9<(a|$qK#Sg@x(6PFxY+ON zlfQs{^u?dRpQNw$MK@o)9Q@Dh8JGwk9M2aI!Jl7cXH{ioRc8%erBY1b^Vh%q-TKo| zBJme$^nUu#m`@~Q>4d>edM+-1H4)`Z4<7kp3LW7h0Z=f1eld=LCUJYXKRA3Bf3 zBj)>d)fp3G%n!gdQDc4xek>j{KLS4yPnfIVaWT$32A&WT%r$Ub)R`ZHC&eW56Y!Ln zVjc%ii)rQw@Kf=Wxek6No-t3_&z%?I1@jd6rFh9a4SpqFF+T;r7O$C~foH@F^KTIp$a3x8g1HYw$bqj(Gjl zR%p5VTFY}B!!m&)rb|NUoVxMCb#u8-C|$Vbf5C zBSWcR9yo7od2Q8_Fy)P&nGhc32Ii5L=k8g`;jnZhGoCB6-EnacgQikPxvE;$WX!*yM>@KCSV%c(8DGqe-2P+70I(oa5*S#%l;CUvCJa)H7 zQhomVxx`^Dd>O zY4OHmkk$eFXKkbHwhrK)@r`ysYcBc9+gCCOnz09(j?af6pb^P62v2Ns&9&VwrWZCg zKEsq|we?jRXuFL!7Esz*-~6(%xV*Ksyo6?YXJ?K7c0wK4<4o#%nBT>5r5=<* z!}oR5qKpkuvuU}8I?;nrNyCA0fQ*##3sb@KrOV?)X&Nc&gLjlwGHfds2eSDUFHVbI zv+gGg$l!O8F5*7}a0w^u#98uN!b}~e)HS|{aVekSQvR0w+Gl3Mw3#(?zwJXiwUvNh z9)9ESE8z0ZoROXUiamaP>{o){Jp9Ub*2$gWVrRcfk6)Vo`r#)yj{I5XJRTZzpvO1! zweb6*IrxXttpjNP?P^b}N)^_WzLG5* z2h=#!t7FTywWidRbi52m&#}#2z^j^d1R<*bJKL)~u?;?z-9KW`Bp96lUDtfQs)T;J2fBu}?xM)1v)O;SF zZ>J-}vZ=6BnyOys_NmsRlCF1)nSS~^>R9cuR5jEOWi{I&I{us;n2vW~PZ~Jb5?dF> z7lP&~t%`dR#S+(G9hKiOjnJrfVi+53(S15vyU3M5Rf4Hjj`GKba#00?D2*B})hhNf zN|P}fUh13;6y;yUUg{$~&c-QFpQ$nYpA$#-(mmS!Y&CZSE0hyHjvwl2u*-{^ z)OE|XLS3)>C$uA|8ozSpLW({)8}jM^YclTayARFhPWBu`Qxp?@V* z2_lN;cWj`_WUY|&lMIjylHmW1gc>FpA-P0yndAy2D$4Fv7^tgcy+(4KMB!c|r1$-dQE$gg0F@^kT5O?)rLVfhJ?PLy+H9GCy!Pk-OP0b+jWng9R* literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/_compat.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/_compat.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89b5d3f0a5ce984b5044f891d77bb0e42fd7bcee GIT binary patch literal 16014 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/_termui_impl.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca2a1e408065f4695685f4bbddaad984fae8a69c GIT binary patch literal 18508 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/_textwrap.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/_textwrap.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..964cbf92528f851d1726d4ac1ff7b58a69f13496 GIT binary patch literal 1641 zcmZWpOK;>v5bkcbJsxLvvUzNh1(wGFX?tr&K+OBGOBDIksPLHN)WNzAdX37_))ce*? zr#j9vC2gzrq_R$?O6#6xqCB#mmgQ6{%l8T;QvmGsXoH6}euZEOa7t3~%Gm+PF4)Yi z$dKv}D=OGSGV>}=kjniP{Z{2w09@_+6`QlFbp`NNKCXQNFh3&|GoREmbnaXcoW(4@ zp|f{Q;jZgj7$LXLI61TT2U;ehI4{&;d|GH>tcTyNgmosxBy3x5WPYHrc-=zdqd^(i zz)X@<#<@HwEzc8ES|^=?(bD`*^bRb!+1>rD|Gh4TGA&Ji_i0*;Cy4?Q`zKPJ^uNmn zeUp{)BRFRSJeq!*XX)tU7^yu4{Cks^b}c?il*r+pyAlyRvk9i#@|aIM@C5L3x=#7+ z`J)DTy*Kt+FH=GSMGg>nvymo)wD+`>Iw=eN0%_V$8Jv7KkR$wH5EwN-fb$ChZpe%w zLo=rW5{IOspAtr5n1P5cBYNHu6~7X3;H$L-|n-SGvfdq zO5722#Pt?L4n%CuEB6&N!7K6`L(v|?A7BpoD_RRzqi`(^LeI9PnvRh%i3-fK5*yIy zcVU5k56v1b<-;OVp|`;71I#de0^KRjK&!B!#_|8)tSr!v3zffo40}u$45yobjSuf8 z3)uN~6O+dJy)amG2?DWbmD;QO1tnWIjjp{F3_D7aQZO^612v0+dM`cB#gd)X%eW$PWX81193wLDok#V8hyA`D4zr!J-Jfrg3#$Ni9VG T$>IY(M`r6HI;2y#^Oo}uRj-xX literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/_utils.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/_utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b4808a792ee1bf7c3a623aa4515c6ed65924d78 GIT binary patch literal 890 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͍D3UX0 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 B6{7$E literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/decorators.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/decorators.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4e11fd43349953460648eea7f5b0ad21fd6932a GIT binary patch literal 17545 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/globals.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/globals.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..acb7bd1753cda4f83863d17e2c1391a2282d9635 GIT binary patch literal 2461 zcmZ`*&5s*36d%t_vT2e{x1Y4MQ1=T6Dgh2~h!9Y%T3AsuSd=(pCz z?kYv7+avrB?XmnxbA?N9NIkJaD(`tR?QRt;&CIj?^89;WKS!>vRvj3B{ryk4zv4K5 z;^gos!Q@-VA^JBExWh^0Oh}gyl-Tuq$*bEY=FNj2Fm!X!{?@o!eYR*H5Ku6>rOb>(s1f02@4*wk?8c4{O{FG!UF zki8&BT?g|oU3mFKHQ>|A{DG9wL1W<+ozsqt#s7j<#}FT;I7f>1-e6X%I7Pc_C>)2^ z5leV5{ubTW9T4`a#(7FOG48g<-Sf^f_eThb?-<{dhVJeM#CZgzvDcV8`hqEQZ=cM` zW6*ivaQ_kcg>e7=LYk48(=4&aaKt?ud`-<*(1rWd28S+!(4us69BLY{m<|L@wcwO# z%IJ*kQX{Jr1`7hA-?EX1kjeq(gbtOQP!{jb#zKk0Bv@s1B9)-wB!Ps4Cbt$reZ){w zQvnB9WbI)Z2km~sv@VjQk!e|#!57USarJ`dIB|>Sc z(_oB_EKG}FEcZO@wMIfHU?sExx5JnQV-}CXctk^^L1WCOA=n%+j0zll zB#L{W<7FB*XnP{exZPirRZ9ml5&gpvUl?h%=oGMni;Q!@+im(q>$BW` zJ7J1VXx?RQ8V;!z2E*6iR4EukWO<{}DT`7Fxu$)r-ahKavPicDi#a_sFi%q!Mr;t} zxf2lkAxk3+(jo+)1BWRVYC+jDBW{N=emkSv}kMFJ8geyS$0zfzSXD zgIq!d94*F9b0s4t^8=<$R_6_A_>MegwiCFs5)P%z+OL1e$xllSjaQcJ<_(Y=Z~Z3xDsA4eaNbMGGRp$OH9T z4~nAB8$=H6qQydo5gP%`Yz!C0{kn&cl8_qAhkDUpFasV!o)G*7l@J;=!>+I@k(3Wn zA+d3@^^dVD@?LPR-RNv8yfL6!F(2w`F5_ouuFn8V$!IDdtPs84!kI5YU89*w2`bsG zxMkwI>N?sYx1qG`JqrIl996*=Ucyb%DmLuRWH(OWhMymdO1b()Ty@{b_Ob&t3-a1) zHI$mivudw5OyRBao5pt+~oq6Uoy^e2sr9G(xXC6}L{x_VK+2!Kdz3&s9rn GrT+jT9(PXw literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/parser.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/parser.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59b64c80e5b8f786a08ba6e21cc7be3501803199 GIT binary patch literal 14135 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 RR7 zfYOiciK0>YPLZR_#Y-D6o)isMvMkhw)N#iMx5_}1dK~Tb7(36CLZUslstC9+ssShCA-q+p4ozy|Xxv$cI#|0Qf5u^g zsbgj;z)EO}4BZ&p!q$I=iFOt*HWzWx3B^G=&&};O&H4yqCNk@G3z3T%_#%d;6yfhf z8t}RDk4@-_NB8s(O&DF^%?tc_0xu{>iN{TEKhL0)S1~0&9jGR{pV-~#1o$0&2b-NvWLlDCi{?pyI*ErxKB!7WvRcRl)Bu8Upn}rqSx@lJ1MhE{97L|ro)15wKf8B;_;3q8Ao@K=!jK5qD|a0J5EM6w2~XL+CvLt z7m$~5hj=NjHzFgBsTd}O@OCXUF%uJl7fV7kN;+MV(JLQZI$t^ajq_)JUE+A# zSmD*+wG(m|U#i~D8esu&tfhA#DUS|$_VC9Nh>KmVYE74m@e@!^8~G!UC9X&TKLH1B z|AwRN!%UJpX|C*?!dY4o@!zwGdx{9mz&7DO9A)~?-;jr6*eSS$lZAXKTY9DRR4Io$ KJHK6e;(q`*v7-S1 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/shell_completion.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/shell_completion.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..975700d1d4e25cae2ae5ae19f97ac8b0d120c4bb GIT binary patch literal 18282 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 wKE!VnnWg5uztnvl)e*GfeT zKof{y$GdqF)y5cVq(nHT_8tlbq;{j-YQg|&H=FHyX;BR)3UpZ!%3xL76SEpNEZ&v# zVc;RIPNxldc%%&QvI=sR%#`ldTVLvhVa%i7tNz8y2Y)3Qa6LK*%e2vKz`#KXJW2#x z%ka2Fb>Wo%SKyJ02&nu^{8mS?!}dL-7XVdB=0?Z#86@Pv>Ilw*-Q|MZR~f99vu`!? zz1NMc>H&y(SiN-=uIsTB1kpgOzqN3o#|D|Qf;Zl(2YOWvqT!Pq<_N~<5=4HcYY-zGDo7o1_2R9xj@N z&n(E35%Zb9vwqz!*j9Y)zuQm8$q-426m~3>f)&kY{?^{H501g8D@3JF?bFtOlJ@7N z|7f2c{SWqOd8!1bcAnRDO>?pR(siyTZ<59pPFW4MC&P!Vor$m+UX|arT1O$pth=xb zpw}S9AeEl&S`W?m<@%hI;~;(t=>!=9cgnnrlFW4MX&Qw(=oXuY@Nu^x-=O(MwhNGS zkcQpDgPea3QVYR33leC-c>gOhVi+R|7(rS1y}s+`d-GOocLiL15i(x*QNIVO3w5-z z%f=xx9Lp$(e(hb@x@YFZTG6>C|7}1fji;kA$QkyG)h^7hWLBmoB_BYxfw7sDE&XQY zuf(D8I(xhL1qy?9%(LR&Acs1QJfRpx$H^jp#D>fgl^CQKdo&O&DaIhi_EfY)P$l4U zlz06J8>;Qyd~_A(+T^2gbczSaJ);P#julGK%1A1Sb5qRYh}LJ`=lDwbOV=Zya3fn) zaD<0boT8vZLchZkvP?$!bkv&XAiN|(aWFmmV|`ZKwcgpwJR6%P5B?6dUrEBC;ne$|JemUy?y)}uJdKSUAnZ1pby?I^??*r zRn(!~Aa%h>!3_S*2P~{R^{UPP_@U$HXE~~Xm>5(*WQIP)j-*g1xBb|!p#?3A@hPIk z2o!(RAL8A4rnckUH2=AP@38rR%|B-IPuYB(jmzdoY$PT;z%cL)*Y^dB5?!kCq6F(` z+i6!=9F;%k;a{?$5m`E38Y~?jIs5+rCOhjC literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/termui.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/termui.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d1afd4796ef90115f1c45ae77eaa6bc0d5f7133 GIT binary patch literal 29355 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/click/__pycache__/types.cpython-310.pyc b/venv/Lib/site-packages/click/__pycache__/types.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..449f4f5dc78ec1a7a082df6fe87fe01c066d97ab GIT binary patch literal 37266 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 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[t.Any]) -> 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: str | None, + errors: str | None, + 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(b"") + 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[t.Any], 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[t.Any], 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.Any]) -> t.BinaryIO | None: + # 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.Any]) -> t.BinaryIO | None: + # 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: str | None) -> 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: str | None, errors: str | None +) -> 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[t.Any], + encoding: str | None, + errors: str | None, + is_binary: t.Callable[[t.IO[t.Any], bool], bool], + find_binary: t.Callable[[t.IO[t.Any]], t.BinaryIO | None], + 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[t.Any], + encoding: str | None, + errors: str | None, + 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[t.Any], + encoding: str | None, + errors: str | None, + 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: str | None = None, errors: str | None = 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: str | None = None, errors: str | None = 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: str | None = None, errors: str | None = 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: str | os.PathLike[str] | int, + mode: str, + encoding: str | None, + errors: str | None, +) -> t.IO[t.Any]: + """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 | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + atomic: bool = False, +) -> tuple[t.IO[t.Any], bool]: + binary = "b" in mode + filename = os.fspath(filename) + + # 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: int | None = 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[t.Any], af), True + + +class _AtomicFile: + def __init__(self, f: t.IO[t.Any], 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: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + 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[t.Any]) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.IO[t.Any] | None = None, color: bool | None = 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: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi(stream: t.TextIO, color: bool | None = 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: str) -> int: + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write # type: ignore[method-assign] + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: str | None, errors: str | None + ) -> t.TextIO | None: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO[t.Any]) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.TextIO | None], + wrapper_func: t.Callable[[], t.TextIO], +) -> t.Callable[[], t.TextIO | None]: + cache: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.TextIO | None: + stream = src_func() + + if stream is None: + return None + + 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: cabc.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: cabc.Mapping[str, t.Callable[[str | None, str | None], t.TextIO]] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/venv/Lib/site-packages/click/_termui_impl.py b/venv/Lib/site-packages/click/_termui_impl.py new file mode 100644 index 0000000..c9f1e1c --- /dev/null +++ b/venv/Lib/site-packages/click/_termui_impl.py @@ -0,0 +1,870 @@ +""" +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. +""" + +from __future__ import annotations + +import collections.abc as cabc +import contextlib +import math +import os +import shlex +import sys +import time +import typing as t +from gettext import gettext as _ +from io import StringIO +from pathlib import Path +from types import TracebackType + +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: cabc.Iterable[V] | None, + length: int | None = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + label: str | None = None, + file: t.TextIO | None = None, + color: bool | None = 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.hidden = hidden + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label: str = label or "" + + if file is None: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + file = StringIO() + + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width: int = width + self.autowidth: bool = 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("cabc.Iterable[V]", range(length)) + self.iter: cabc.Iterable[V] = iter(iterable) + self.length = length + self.pos: int = 0 + self.avg: list[float] = [] + self.last_eta: float + self.start: float + self.start = self.last_eta = time.time() + self.eta_known: bool = False + self.finished: bool = False + self.max_width: int | None = None + self.entered: bool = False + self.current_item: V | None = None + self._is_atty = isatty(self.file) + self._last_line: str | None = None + + def __enter__(self) -> ProgressBar[V]: + self.entered = True + self.render_progress() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.render_finish() + + def __iter__(self) -> cabc.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.hidden or not self._is_atty: + 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: + if self.hidden: + return + + if not self._is_atty: + # Only output the label once if the output is not a TTY. + 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: + import shutil + + 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 and self.max_width is not None: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) + 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: V | None = 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) -> cabc.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 not self._is_atty: + 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: cabc.Iterable[str], color: bool | None = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if stdout is None: + stdout = StringIO() + + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + + # Split using POSIX mode (the default) so that quote characters are + # stripped from tokens and quoted Windows paths are preserved. + # Non-POSIX mode retains quotes in tokens, and wrapping tokens + # with shlex.quote re-introduces quoting issues on Windows. + pager_cmd_parts = shlex.split(os.environ.get("PAGER", "")) + if pager_cmd_parts: + if WIN: + if _tempfilepager(generator, pager_cmd_parts, color): + return + elif _pipepager(generator, pager_cmd_parts, color): + return + + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if (WIN or sys.platform.startswith("os2")) and _tempfilepager( + generator, ["more"], color + ): + return + if _pipepager(generator, ["less"], color): + return + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if _pipepager(generator, ["more"], color): + return + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager( + generator: cabc.Iterable[str], cmd_parts: list[str], color: bool | None +) -> bool: + """Page through text by feeding it to another program. + + Invokes the pager via :class:`subprocess.Popen` with an ``argv`` list + produced by :func:`shlex.split`. The command is resolved to an absolute + path with :func:`shutil.which` as recommended by the + :mod:`subprocess` docs for Windows compatibility. + + Invoking a pager through this might support colors: if piping to + ``less`` and the user hasn't decided on colors, ``LESS=-R`` is set + automatically. + + Returns ``True`` if the command was found and executed, ``False`` + otherwise so another pager can be attempted. + """ + # Split the command into the invoked CLI and its parameters. + if not cmd_parts: + return False + + import shutil + + cmd = cmd_parts[0] + cmd_params = cmd_parts[1:] + + cmd_filepath = shutil.which(cmd) + if not cmd_filepath: + return False + + # Produces a normalized absolute path string. + # multi-call binaries such as busybox derive their identity from the symlink + # less -> busybox. resolve() causes them to misbehave. (eg. less becomes busybox) + cmd_path = Path(cmd_filepath).absolute() + cmd_name = cmd_path.name + + import subprocess + + # Make a local copy of the environment to not affect the global one. + env = dict(os.environ) + + # If we're piping to less and the user hasn't decided on colors, we enable + # them by default we find the -R flag in the command line arguments. + if color is None and cmd_name == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_params)}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen( + [str(cmd_path)] + cmd_params, + shell=False, + stdin=subprocess.PIPE, + env=env, + errors="replace", + text=True, + ) + assert c.stdin is not None + try: + for text in generator: + if not color: + text = strip_ansi(text) + + c.stdin.write(text) + except BrokenPipeError: + # In case the pager exited unexpectedly, ignore the broken pipe error. + pass + except Exception as e: + # In case there is an exception we want to close the pager immediately + # and let the caller handle it. + # Otherwise the pager will keep running, and the user may not notice + # the error message, or worse yet it may leave the terminal in a broken state. + c.terminate() + raise e + finally: + # We must close stdin and wait for the pager to exit before we continue + try: + c.stdin.close() + # Close implies flush, so it might throw a BrokenPipeError if the pager + # process exited already. + except BrokenPipeError: + pass + + # 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 + + return True + + +def _tempfilepager( + generator: cabc.Iterable[str], cmd_parts: list[str], color: bool | None +) -> bool: + """Page through text by invoking a program on a temporary file. + + Used as the primary pager strategy on Windows (where piping to + ``more`` adds spurious ``\\r\\n``), and as a fallback on other + platforms. The command is resolved to an absolute path with + :func:`shutil.which`. + + Returns ``True`` if the command was found and executed, ``False`` + otherwise so another pager can be attempted. + """ + # Split the command into the invoked CLI and its parameters. + if not cmd_parts: + return False + + import shutil + + cmd = cmd_parts[0] + + cmd_filepath = shutil.which(cmd) + if not cmd_filepath: + return False + # Produces a normalized absolute path string. + # multi-call binaries such as busybox derive their identity from the symlink + # less -> busybox. resolve() causes them to misbehave. (eg. less becomes busybox) + cmd_path = Path(cmd_filepath).absolute() + + import subprocess + 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: + subprocess.call([str(cmd_path), filename]) + except OSError: + # Command not found + pass + finally: + os.close(fd) + os.unlink(filename) + + return True + + +def _nullpager( + stream: t.TextIO, generator: cabc.Iterable[str], color: bool | None +) -> 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: str | None = None, + env: cabc.Mapping[str, str] | None = 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" + + from shutil import which + + for editor in "sensible-editor", "vim", "nano": + if which(editor) is not None: + return editor + return "vi" + + def edit_files(self, filenames: cabc.Iterable[str]) -> None: + """Open files in the user's editor.""" + import shlex + import subprocess + + editor = self.get_editor() + environ: dict[str, str] | None = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + try: + # Split in POSIX mode (the default) for the same reasons as + # in pager(): strips quotes from tokens and preserves quoted + # Windows paths. + c = subprocess.Popen( + args=shlex.split(editor) + list(filenames), + env=environ, + ) + 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 + + @t.overload + def edit(self, text: bytes | bytearray) -> bytes | None: ... + + # We cannot know whether or not the type expected is str or bytes when None + # is passed, so str is returned as that was what was done before. + @t.overload + def edit(self, text: str | None) -> str | None: ... + + def edit(self, text: str | bytes | bytearray | None) -> str | bytes | None: + import tempfile + + if text is None: + data: bytes | bytearray = 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_files((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") + 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) + args = ["explorer", f"/select,{url}"] + else: + args = ["start"] + if wait: + args.append("/WAIT") + args.append("") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + elif CYGWIN: + if locate: + url = _unquote_file(url) + args = ["cygstart", os.path.dirname(url)] + else: + args = ["cygstart"] + if wait: + args.append("-w") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + + 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) -> None: + 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() + + +if sys.platform == "win32": + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> cabc.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`. + + if echo: + func = t.cast(t.Callable[[], str], msvcrt.getwche) + else: + func = t.cast(t.Callable[[], str], msvcrt.getwch) + + 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 termios + import tty + + @contextlib.contextmanager + def raw_terminal() -> cabc.Iterator[int]: + f: t.TextIO | None + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/venv/Lib/site-packages/click/_textwrap.py b/venv/Lib/site-packages/click/_textwrap.py new file mode 100644 index 0000000..97fbee3 --- /dev/null +++ b/venv/Lib/site-packages/click/_textwrap.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import collections.abc as cabc +import textwrap +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: list[str], + cur_line: 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) -> cabc.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/venv/Lib/site-packages/click/_utils.py b/venv/Lib/site-packages/click/_utils.py new file mode 100644 index 0000000..09fb008 --- /dev/null +++ b/venv/Lib/site-packages/click/_utils.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +import enum +import typing as t + + +class Sentinel(enum.Enum): + """Enum used to define sentinel values. + + .. seealso:: + + `PEP 661 - Sentinel Values `_. + """ + + UNSET = object() + FLAG_NEEDS_VALUE = object() + + def __repr__(self) -> str: + return f"{self.__class__.__name__}.{self.name}" + + +UNSET = Sentinel.UNSET +"""Sentinel used to indicate that a value is not set.""" + +FLAG_NEEDS_VALUE = Sentinel.FLAG_NEEDS_VALUE +"""Sentinel used to indicate 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``. +""" + +T_UNSET = t.Literal[UNSET] # type: ignore[valid-type] +"""Type hint for the :data:`UNSET` sentinel value.""" + +T_FLAG_NEEDS_VALUE = t.Literal[FLAG_NEEDS_VALUE] # type: ignore[valid-type] +"""Type hint for the :data:`FLAG_NEEDS_VALUE` sentinel value.""" diff --git a/venv/Lib/site-packages/click/_winconsole.py b/venv/Lib/site-packages/click/_winconsole.py new file mode 100644 index 0000000..e56c7c6 --- /dev/null +++ b/venv/Lib/site-packages/click/_winconsole.py @@ -0,0 +1,296 @@ +# 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. +from __future__ import annotations + +import collections.abc as cabc +import io +import sys +import time +import typing as t +from ctypes import Array +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 + +if t.TYPE_CHECKING: + try: + # Using `typing_extensions.Buffer` instead of `collections.abc` + # on Windows for some reason does not have `Sized` implemented. + from collections.abc import Buffer # type: ignore + except ImportError: + from typing_extensions import Buffer + +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_ = [ # noqa: RUF012 + ("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: Buffer, writable: bool = False) -> Array[c_char]: + buf = Py_buffer() + flags: int = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + out: Array[c_char] = buffer_type.from_address(buf.buf) + return out + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle: int | None) -> None: + self.handle = handle + + def isatty(self) -> t.Literal[True]: + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self) -> t.Literal[True]: + return True + + def readinto(self, b: Buffer) -> int: + 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) -> t.Literal[True]: + return True + + @staticmethod + def _get_error_message(errno: int) -> str: + 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: Buffer) -> int: + 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: cabc.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) -> str: + 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: cabc.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: str | None, errors: str | None +) -> t.TextIO | None: + if ( + get_buffer is None + or encoding not in {"utf-16-le", None} + or errors not in {"strict", None} + or not _is_console(f) + ): + return None + + func = _stream_factories.get(f.fileno()) + if func is None: + return None + + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/venv/Lib/site-packages/click/core.py b/venv/Lib/site-packages/click/core.py new file mode 100644 index 0000000..d940dd8 --- /dev/null +++ b/venv/Lib/site-packages/click/core.py @@ -0,0 +1,3476 @@ +from __future__ import annotations + +import collections.abc as cabc +import enum +import errno +import inspect +import os +import sys +import typing as t +from collections import abc +from collections import Counter +from contextlib import AbstractContextManager +from contextlib import contextmanager +from contextlib import ExitStack +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat +from types import TracebackType + +from . import types +from ._utils import FLAG_NEEDS_VALUE +from ._utils import UNSET +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import NoArgsIsHelpError +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 _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: + 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 +) -> cabc.Iterator[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(Group, 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_nested_chain( + base_command: Group, cmd_name: str, cmd: Command, register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, Group): + return + + if register: + message = ( + f"It is not possible to add the group {cmd_name!r} to another" + f" group {base_command.name!r} that is in chain mode." + ) + else: + message = ( + f"Found the group {cmd_name!r} as subcommand to another group " + f" {base_command.name!r} that is in chain mode. This is not supported." + ) + + raise RuntimeError(message) + + +def batch(iterable: cabc.Iterable[V], batch_size: int) -> list[tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size), strict=False)) + + +@contextmanager +def augment_usage_errors( + ctx: Context, param: Parameter | None = None +) -> cabc.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: cabc.Sequence[Parameter], + declaration_order: cabc.Sequence[Parameter], +) -> list[Parameter]: + """Returns all declared parameters in the order they should be processed. + + The declared parameters are re-shuffled depending on the order in which + they were invoked, as well as the eagerness of each parameters. + + The invocation order takes precedence over the declaration order. I.e. the + order in which the user provided them to the CLI is respected. + + This behavior and its effect on callback evaluation is detailed at: + https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order + """ + + def sort_key(item: Parameter) -> 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.IntEnum): + """This is an :class:`~enum.IntEnum` 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. + + Members are ordered from most explicit to least explicit source. + This allows comparison to check if a value was explicitly provided: + + .. code-block:: python + + source = ctx.get_parameter_source("port") + if source < click.ParameterSource.DEFAULT_MAP: + ... # value was explicitly set + + .. versionchanged:: 8.3.3 + Use :class:`~enum.IntEnum` and reorder members from most to + least explicit. Supports comparison operators. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a 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_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show the default value for commands. If this + value is not set, it defaults to the value from the parent + context. ``Command.show_default`` overrides this default for the + specific command. + + .. versionchanged:: 8.2 + The ``protected_args`` attribute is deprecated and will be removed in + Click 9.0. ``args`` will contain remaining unparsed tokens. + + .. versionchanged:: 8.1 + The ``show_default`` parameter is overridden by + ``Command.show_default``, instead of the other way around. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: type[HelpFormatter] = HelpFormatter + + def __init__( + self, + command: Command, + parent: Context | None = None, + info_name: str | None = None, + obj: t.Any | None = None, + auto_envvar_prefix: str | None = None, + default_map: cabc.MutableMapping[str, t.Any] | None = None, + terminal_width: int | None = None, + max_content_width: int | None = None, + resilient_parsing: bool = False, + allow_extra_args: bool | None = None, + allow_interspersed_args: bool | None = None, + ignore_unknown_options: bool | None = None, + help_option_names: list[str] | None = None, + token_normalize_func: t.Callable[[str], str] | None = None, + color: bool | None = None, + show_default: bool | None = 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: dict[str, t.Any] = {} + #: the leftover arguments. + self.args: 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: list[str] = [] + #: the collected prefixes of the command's options. + self._opt_prefixes: set[str] = set(parent._opt_prefixes) if parent else set() + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: 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: cabc.MutableMapping[str, t.Any] | None = 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: str | None = 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: int | None = 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: int | None = 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: 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.Callable[[str], str] | None = 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: str | None = 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: bool | None = 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: bool | None = show_default + + self._close_callbacks: list[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + @property + def protected_args(self) -> list[str]: + import warnings + + warnings.warn( + "'protected_args' is deprecated and will be removed in Click 9.0." + " 'args' will contain remaining unparsed tokens.", + DeprecationWarning, + stacklevel=2, + ) + return self._protected_args + + def to_info_dict(self) -> 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: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> bool | None: + self._depth -= 1 + exit_result: bool | None = None + if self._depth == 0: + exit_result = self._close_with_exception_info(exc_type, exc_value, tb) + pop_context() + + return exit_result + + @contextmanager + def scope(self, cleanup: bool = True) -> cabc.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) -> 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: AbstractContextManager[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._close_with_exception_info(None, None, None) + + def _close_with_exception_info( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> bool | None: + """Unwind the exit stack by calling its :meth:`__exit__` providing the exception + information to allow for exception handling by the various resources registered + using :meth;`with_resource` + + :return: Whatever ``exit_stack.__exit__()`` returns. + """ + exit_result = self._exit_stack.__exit__(exc_type, exc_value, tb) + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + return exit_result + + @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: type[V]) -> V | None: + """Finds the closest object of a given type.""" + node: Context | None = 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: 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 + + def _default_map_has(self, name: str | None) -> bool: + """Check if :attr:`default_map` contains a real value for ``name``. + + Returns ``False`` when the key is absent, the map is ``None``, + ``name`` is ``None``, or the stored value is the internal + :data:`UNSET` sentinel. + """ + return ( + name is not None + and self.default_map is not None + and name in self.default_map + and self.default_map[name] is not UNSET + ) + + @t.overload + def lookup_default( + self, name: str, call: t.Literal[True] = True + ) -> t.Any | None: ... + + @t.overload + def lookup_default( + self, name: str, call: t.Literal[False] = ... + ) -> t.Any | t.Callable[[], t.Any] | None: ... + + def lookup_default(self, name: str, call: bool = True) -> t.Any | None: + """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 not self._default_map_has(name): + return None + + # Assert to make the type checker happy. + assert self.default_map is not None + value = self.default_map[name] + + if call and callable(value): + return value() + + return value + + def fail(self, message: str) -> t.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) -> t.NoReturn: + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> t.NoReturn: + """Exits the application with a given exit code. + + .. versionchanged:: 8.2 + Callbacks and context managers registered with :meth:`call_on_close` + and :meth:`with_resource` are closed before exiting. + """ + self.close() + 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) + + @t.overload + def invoke( + self, callback: t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any + ) -> V: ... + + @t.overload + def invoke(self, callback: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: ... + + def invoke( + self, callback: Command | t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any + ) -> t.Any | V: + """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. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + + .. versionchanged:: 3.2 + A new context is created, and missing arguments use default values. + """ + 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 = t.cast("t.Callable[..., V]", 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: + default_value = param.get_default(ctx) + # We explicitly hide the :attr:`UNSET` value to the user, as we + # choose to make it an implementation detail. And because ``invoke`` + # has been designed as part of Click public API, we return ``None`` + # instead. Refs: + # https://github.com/pallets/click/issues/3066 + # https://github.com/pallets/click/issues/3065 + # https://github.com/pallets/click/pull/3068 + if default_value is UNSET: + default_value = None + kwargs[param.name] = param.type_cast_value( # type: ignore + ctx, default_value + ) + + # 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) -> 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) -> ParameterSource | None: + """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 Command: + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + :param deprecated: If ``True`` or non-empty string, issues a message + indicating that the command is deprecated and highlights + its deprecation in --help. The message can be customized + by using a string as the value. + + .. versionchanged:: 8.2 + This is the base class for all commands, not ``BaseCommand``. + ``deprecated`` can be set to a string as well to customize the + deprecation message. + + .. versionchanged:: 8.1 + ``help``, ``epilog``, and ``short_help`` are stored unprocessed, + all formatting is done when outputting help text, not at init, + and is done even if not using the ``@command`` decorator. + + .. versionchanged:: 8.0 + Added a ``repr`` showing the command name. + + .. versionchanged:: 7.1 + Added the ``no_args_is_help`` parameter. + + .. versionchanged:: 2.0 + Added the ``context_settings`` parameter. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: 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: str | None, + context_settings: cabc.MutableMapping[str, t.Any] | None = None, + callback: t.Callable[..., t.Any] | None = None, + params: list[Parameter] | None = None, + help: str | None = None, + epilog: str | None = None, + short_help: str | None = None, + options_metavar: str | None = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool | str = False, + ) -> 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: cabc.MutableMapping[str, t.Any] = 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: list[Parameter] = params or [] + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self._help_option = None + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> dict[str, t.Any]: + return { + "name": self.name, + "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, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + 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) -> list[Parameter]: + params = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + params = [*params, help_option] + + if __debug__: + import warnings + + opts = [opt for param in params for opt in param.opts] + opts_counter = Counter(opts) + duplicate_opts = (opt for opt, count in opts_counter.items() if count > 1) + + for duplicate_opt in duplicate_opts: + warnings.warn( + ( + f"The parameter {duplicate_opt} is used more than once. " + "Remove its duplicate as parameters should be unique." + ), + stacklevel=3, + ) + + return params + + 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) -> 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) -> 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) -> Option | None: + """Returns the help option object. + + Skipped if :attr:`add_help_option` is ``False``. + + .. versionchanged:: 8.1.8 + The help option is now cached to avoid creating it multiple times. + """ + help_option_names = self.get_help_option_names(ctx) + + if not help_option_names or not self.add_help_option: + return None + + # Cache the help option object in private _help_option attribute to + # avoid creating it multiple times. Not doing this will break the + # callback odering by iter_params_for_processing(), which relies on + # object comparison. + if self._help_option is None: + # Avoid circular import. + from .decorators import help_option + + # Apply help_option decorator and pop resulting option + help_option(*help_option_names)(self) + self._help_option = self.params.pop() # type: ignore[assignment] + + return self._help_option + + def make_parser(self, ctx: Context) -> _OptionParser: + """Creates the underlying option parser for this command.""" + parser = _OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + if self.short_help: + text = inspect.cleandoc(self.short_help) + elif self.help: + text = make_default_short_help(self.help, limit) + else: + text = "" + + if self.deprecated: + deprecated_message = ( + f"(DEPRECATED: {self.deprecated})" + if isinstance(self.deprecated, str) + else "(DEPRECATED)" + ) + text = _("{text} {deprecated_message}").format( + text=text, deprecated_message=deprecated_message + ) + + 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.""" + if self.help is not None: + # truncate the help text to the first form feed + text = inspect.cleandoc(self.help).partition("\f")[0] + else: + text = "" + + if self.deprecated: + deprecated_message = ( + f"(DEPRECATED: {self.deprecated})" + if isinstance(self.deprecated, str) + else "(DEPRECATED)" + ) + text = _("{text} {deprecated_message}").format( + text=text, deprecated_message=deprecated_message + ) + + 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: + epilog = inspect.cleandoc(self.epilog) + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(epilog) + + def make_context( + self, + info_name: str | None, + args: list[str], + parent: Context | None = 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'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) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + raise NoArgsIsHelpError(ctx) + + 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)): + _, args = param.handle_parse_result(ctx, opts, args) + + # We now have all parameters' values into `ctx.params`, but the data may contain + # the `UNSET` sentinel. + # Convert `UNSET` to `None` to ensure that the user doesn't see `UNSET`. + # + # Waiting until after the initial parse to convert allows us to treat `UNSET` + # more like a missing value when multiple params use the same name. + # Refs: + # https://github.com/pallets/click/issues/3071 + # https://github.com/pallets/click/pull/3079 + for name, value in ctx.params.items(): + if value is UNSET: + ctx.params[name] = None + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + ctx._opt_prefixes.update(parser._opt_prefixes) + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + extra_message = ( + f" {self.deprecated}" if isinstance(self.deprecated, str) else "" + ) + message = _( + "DeprecationWarning: The command {name!r} is deprecated.{extra_message}" + ).format(name=self.name, extra_message=extra_message) + 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) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. + + :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: 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) + ) + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, Group) 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: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: t.Literal[True] = True, + **extra: t.Any, + ) -> t.NoReturn: ... + + @t.overload + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: ... + + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt) as e: + echo(file=sys.stderr) + raise Abort() from e + 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: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str | None = 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``. + + .. versionchanged:: 8.2.0 + Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). + """ + if complete_var is None: + complete_name = prog_name.replace("-", "_").replace(".", "_") + complete_var = f"_{complete_name}_COMPLETE".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 _FakeSubclassCheck(type): + def __subclasscheck__(cls, subclass: type) -> bool: + return issubclass(subclass, cls.__bases__[0]) + + def __instancecheck__(cls, instance: t.Any) -> bool: + return isinstance(instance, cls.__bases__[0]) + + +class _BaseCommand(Command, metaclass=_FakeSubclassCheck): + """ + .. deprecated:: 8.2 + Will be removed in Click 9.0. Use ``Command`` instead. + """ + + +class Group(Command): + """A group is a command that nests other commands (or more groups). + + :param name: The name of the group command. + :param commands: Map names to :class:`Command` objects. Can be a list, which + will use :attr:`Command.name` as the keys. + :param invoke_without_command: Invoke the group's callback even if a + subcommand is not given. + :param no_args_is_help: If no arguments are given, show the group's help and + exit. Defaults to the opposite of ``invoke_without_command``. + :param subcommand_metavar: How to represent the subcommand argument in help. + The default will represent whether ``chain`` is set or not. + :param chain: Allow passing more than one subcommand argument. After parsing + a command's arguments, if any arguments remain another command will be + matched, and so on. + :param result_callback: A function to call after the group's and + subcommand's callbacks. The value returned by the subcommand is passed. + If ``chain`` is enabled, the value will be a list of values returned by + all the commands. If ``invoke_without_command`` is enabled, the value + will be the value returned by the group's callback, or an empty list if + ``chain`` is enabled. + :param kwargs: Other arguments passed to :class:`Command`. + + .. versionchanged:: 8.0 + The ``commands`` argument can be a list of command objects. + + .. versionchanged:: 8.2 + Merged with and replaces the ``MultiCommand`` base class. + """ + + allow_extra_args = True + allow_interspersed_args = False + + #: 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: type[Command] | None = 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: type[Group] | type[type] | None = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: str | None = None, + commands: cabc.MutableMapping[str, Command] + | cabc.Sequence[Command] + | None = None, + invoke_without_command: bool = False, + no_args_is_help: bool | None = None, + subcommand_metavar: str | None = None, + chain: bool = False, + result_callback: t.Callable[..., t.Any] | None = None, + **kwargs: t.Any, + ) -> None: + super().__init__(name, **kwargs) + + 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: cabc.MutableMapping[str, Command] = commands + + 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( + "A group in chain mode cannot have optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> 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 add_command(self, cmd: Command, name: str | None = 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_nested_chain(self, name, cmd, register=True) + self.commands[name] = cmd + + @t.overload + def command(self, __func: t.Callable[..., t.Any]) -> Command: ... + + @t.overload + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: ... + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command] | Command: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + func: t.Callable[..., t.Any] | None = None + + if args and callable(args[0]): + assert len(args) == 1 and not kwargs, ( + "Use 'command(**kwargs)(callable)' to provide arguments." + ) + (func,) = args + args = () + + if self.command_class and kwargs.get("cls") is None: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd: Command = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + @t.overload + def group(self, __func: t.Callable[..., t.Any]) -> Group: ... + + @t.overload + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Group]: ... + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Group] | Group: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + func: t.Callable[..., t.Any] | None = None + + if args and callable(args[0]): + assert len(args) == 1 and not kwargs, ( + "Use 'group(**kwargs)(callable)' to provide arguments." + ) + (func,) = args + args = () + + if self.group_class is not None and kwargs.get("cls") is None: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> Group: + cmd: Group = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + def 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: t.Any, /, *args: t.Any, **kwargs: t.Any) -> t.Any: + inner = old_callback(value, *args, **kwargs) + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv # type: ignore[return-value] + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> Command | None: + """Given a context and a command name, this returns a :class:`Command` + object if it exists or returns ``None``. + """ + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> list[str]: + """Returns a list of subcommand names in the order they should appear.""" + return sorted(self.commands) + + def collect_usage_pieces(self, ctx: Context) -> 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 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: list[str]) -> list[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + raise NoArgsIsHelpError(ctx) + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx._protected_args = rest + ctx.args = [] + elif rest: + ctx._protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx._protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with the group return value for regular + # groups, or an empty list for chained groups. + with ctx: + rv = super().invoke(ctx) + return _process_result([] if self.chain else rv) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx._protected_args, *ctx.args] + ctx.args = [] + ctx._protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: list[str] + ) -> tuple[str | None, Command | None, 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, args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def shell_complete(self, ctx: Context, incomplete: str) -> 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 _MultiCommand(Group, metaclass=_FakeSubclassCheck): + """ + .. deprecated:: 8.2 + Will be removed in Click 9.0. Use ``Group`` instead. + """ + + +class CommandCollection(Group): + """A :class:`Group` that looks up subcommands on other groups. If a command + is not found on this group, each registered source is checked in order. + Parameters on a source are not added to this group, and a source's callback + is not invoked when invoking its commands. In other words, this "flattens" + commands in many groups into this one group. + + :param name: The name of the group command. + :param sources: A list of :class:`Group` objects to look up commands from. + :param kwargs: Other arguments passed to :class:`Group`. + + .. versionchanged:: 8.2 + This is a subclass of ``Group``. Commands are looked up first on this + group, then each of its sources. + """ + + def __init__( + self, + name: str | None = None, + sources: list[Group] | None = None, + **kwargs: t.Any, + ) -> None: + super().__init__(name, **kwargs) + #: The list of registered groups. + self.sources: list[Group] = sources or [] + + def add_source(self, group: Group) -> None: + """Add a group as a source of commands.""" + self.sources.append(group) + + def get_command(self, ctx: Context, cmd_name: str) -> Command | None: + rv = super().get_command(ctx, cmd_name) + + if rv is not None: + return rv + + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_nested_chain(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> list[str]: + rv: set[str] = set(super().list_commands(ctx)) + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> cabc.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 latter 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: environment variable(s) that are used to provide a default value for + this parameter. This can be a string or a sequence of strings. If a sequence is + given, only the first non-empty environment variable is used for the parameter. + :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. + :param deprecated: If ``True`` or non-empty string, issues a message + indicating that the argument is deprecated and highlights + its deprecation in --help. The message can be customized + by using a string as the value. A deprecated parameter + cannot be required, a ValueError will be raised otherwise. + + .. versionchanged:: 8.2.0 + Introduction of ``deprecated``. + + .. versionchanged:: 8.2 + Adding duplicate parameter names to a :class:`~click.core.Command` will + result in a ``UserWarning`` being shown. + + .. versionchanged:: 8.2 + Adding duplicate parameter names to a :class:`~click.core.Command` will + result in a ``UserWarning`` being shown. + + .. 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: cabc.Sequence[str] | None = None, + type: types.ParamType | t.Any | None = None, + required: bool = False, + # XXX The default historically embed two concepts: + # - the declaration of a Parameter object carrying the default (handy to + # arbitrage the default value of coupled Parameters sharing the same + # self.name, like flag options), + # - and the actual value of the default. + # It is confusing and is the source of many issues discussed in: + # https://github.com/pallets/click/pull/3030 + # In the future, we might think of splitting it in two, not unlike + # Option.is_flag and Option.flag_value: we could have something like + # Parameter.is_default and Parameter.default_value. + default: t.Any | t.Callable[[], t.Any] | None = UNSET, + callback: t.Callable[[Context, Parameter, t.Any], t.Any] | None = None, + nargs: int | None = None, + multiple: bool = False, + metavar: str | None = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: str | cabc.Sequence[str] | None = None, + shell_complete: t.Callable[ + [Context, Parameter, str], list[CompletionItem] | list[str] + ] + | None = None, + deprecated: bool | str = False, + ) -> None: + self.name: str | None + self.opts: list[str] + self.secondary_opts: list[str] + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type: types.ParamType = 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: t.Any | t.Callable[[], t.Any] | None = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + self._custom_shell_complete = shell_complete + self.deprecated = deprecated + + 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}." + ) + + if required and deprecated: + raise ValueError( + f"The {self.param_type_name} '{self.human_readable_name}' " + "is deprecated and still required. A deprecated " + f"{self.param_type_name} cannot be required." + ) + + def to_info_dict(self) -> 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. + + .. versionchanged:: 8.3.0 + Returns ``None`` for the :attr:`default` if it was not set. + + .. 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, + # We explicitly hide the :attr:`UNSET` value to the user, as we choose to + # make it an implementation detail. And because ``to_info_dict`` has been + # designed for documentation purposes, we return ``None`` instead. + "default": self.default if self.default is not UNSET else None, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str | None, list[str], 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, ctx: Context) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(param=self, ctx=ctx) + + 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: t.Literal[True] = True + ) -> t.Any | None: ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Any | t.Callable[[], t.Any] | None: ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Any | t.Callable[[], t.Any] | None: + """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. + """ + name = self.name + value = ctx.lookup_default(name, call=False) if name is not None else None + + if value is None and not ctx._default_map_has(name): + 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: cabc.Mapping[str, t.Any] + ) -> tuple[t.Any, ParameterSource]: + """Returns the parameter value produced by the parser. + + If the parser did not produce a value from user input, the value is either + sourced from the environment variable, the default map, or the parameter's + default value. In that order of precedence. + + If no value is found, an internal sentinel value is returned. + + :meta private: + """ + # Collect from the parse the value passed by the user to the CLI. + value = opts.get(self.name, UNSET) # type: ignore + # If the value is set, it means it was sourced from the command line by the + # parser, otherwise it left unset by default. + source = ( + ParameterSource.COMMANDLINE + if value is not UNSET + else ParameterSource.DEFAULT + ) + + if value is UNSET: + envvar_value = self.value_from_envvar(ctx) + if envvar_value is not None: + value = envvar_value + source = ParameterSource.ENVIRONMENT + + if value is UNSET: + default_map_value = ctx.lookup_default(self.name) # type: ignore[arg-type] + if default_map_value is not None or ctx._default_map_has(self.name): + value = default_map_value + source = ParameterSource.DEFAULT_MAP + + if value is UNSET: + default_value = self.get_default(ctx) + if default_value is not UNSET: + value = default_value + 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 parameter's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + if self.multiple or self.nargs == -1: + return () + else: + return value + + def check_iter(value: t.Any) -> cabc.Iterator[t.Any]: + 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 + + # Define the conversion function based on nargs and type. + + if self.nargs == 1 or self.type.is_composite: + + def convert(value: t.Any) -> t.Any: + return self.type(value, param=self, ctx=ctx) + + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Any: # tuple[t.Any, ...] + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Any: # tuple[t.Any, ...] + 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: + """A value is considered missing if: + + - it is :attr:`UNSET`, + - or if it is an empty sequence while the parameter is suppose to have + non-single value (i.e. :attr:`nargs` is not ``1`` or :attr:`multiple` is + set). + + :meta private: + """ + if value is UNSET: + 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: + """Process the value of this parameter: + + 1. Type cast the value using :meth:`type_cast_value`. + 2. Check if the value is missing (see: :meth:`value_is_missing`), and raise + :exc:`MissingParameter` if it is required. + 3. If a :attr:`callback` is set, call it to have the value replaced by the + result of the callback. If the value was not set, the callback receive + ``None``. This keep the legacy behavior as it was before the introduction of + the :attr:`UNSET` sentinel. + + :meta private: + """ + # shelter `type_cast_value` from ever seeing an `UNSET` value by handling the + # cases in which `UNSET` gets special treatment explicitly at this layer + # + # Refs: + # https://github.com/pallets/click/issues/3069 + if value is UNSET: + if self.multiple or self.nargs == -1: + value = () + else: + 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: + # Legacy case: UNSET is not exposed directly to the callback, but converted + # to None. + if value is UNSET: + value = None + + # Search for parameters with UNSET values in the context. + unset_keys = {k: None for k, v in ctx.params.items() if v is UNSET} + # No UNSET values, call the callback as usual. + if not unset_keys: + value = self.callback(ctx, self, value) + + # Legacy case: provide a temporarily manipulated context to the callback + # to hide UNSET values as None. + # + # Refs: + # https://github.com/pallets/click/issues/3136 + # https://github.com/pallets/click/pull/3137 + else: + # Add another layer to the context stack to clearly hint that the + # context is temporarily modified. + with ctx: + # Update the context parameters to replace UNSET with None. + ctx.params.update(unset_keys) + # Feed these fake context parameters to the callback. + value = self.callback(ctx, self, value) + # Restore the UNSET values in the context parameters. + ctx.params.update( + { + k: UNSET + for k in unset_keys + # Only restore keys that are present and still None, in case + # the callback modified other parameters. + if k in ctx.params and ctx.params[k] is None + } + ) + + return value + + def resolve_envvar_value(self, ctx: Context) -> str | None: + """Returns the value found in the environment variable(s) attached to this + parameter. + + Environment variables values are `always returned as strings + `_. + + This method returns ``None`` if: + + - the :attr:`envvar` property is not set on the :class:`Parameter`, + - the environment variable is not found in the environment, + - the variable is found in the environment but its value is empty (i.e. the + environment variable is present but has an empty string). + + If :attr:`envvar` is setup with multiple environment variables, + then only the first non-empty value is returned. + + .. caution:: + + The raw value extracted from the environment is not normalized and is + returned as-is. Any normalization or reconciliation is performed later by + the :class:`Parameter`'s :attr:`type`. + + :meta private: + """ + if not self.envvar: + 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) + + # Return the first non-empty value of the list of environment variables. + if rv: + return rv + # Else, absence of value is interpreted as an environment variable that + # is not set, so proceed to the next one. + + return None + + def value_from_envvar(self, ctx: Context) -> str | cabc.Sequence[str] | None: + """Process the raw environment variable string for this parameter. + + Returns the string as-is or splits it into a sequence of strings if the + parameter is expecting multiple values (i.e. its :attr:`nargs` property is set + to a value other than ``1``). + + :meta private: + """ + rv = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + return self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: cabc.Mapping[str, t.Any], args: list[str] + ) -> tuple[t.Any, list[str]]: + """Process the value produced by the parser from user input. + + Always process the value through the Parameter's :attr:`type`, wherever it + comes from. + + If the parameter is deprecated, this method warn the user about it. But only if + the value has been explicitly set by the user (and as such, is not coming from + a default). + + :meta private: + """ + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + + ctx.set_parameter_source(self.name, source) # type: ignore + + # Display a deprecation warning if necessary. + if ( + self.deprecated + and value is not UNSET + and source < ParameterSource.DEFAULT_MAP + ): + extra_message = ( + f" {self.deprecated}" if isinstance(self.deprecated, str) else "" + ) + message = _( + "DeprecationWarning: The {param_type} {name!r} is deprecated." + "{extra_message}" + ).format( + param_type=self.param_type_name, + name=self.human_readable_name, + extra_message=extra_message, + ) + echo(style(message, fg="red"), err=True) + + # Process the value through the parameter's type. + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + # In resilient parsing mode, we do not want to fail the command if the + # value is incompatible with the parameter type, so we reset the value + # to UNSET, which will be interpreted as a missing value. + value = UNSET + + # Add parameter's value to the context. + if ( + self.expose_value + # We skip adding the value if it was previously set by another parameter + # targeting the same variable name. This prevents parameters competing for + # the same name to override each other. + and (self.name not in ctx.params or ctx.params[self.name] is UNSET) + ): + # Click is logically enforcing that the name is None if the parameter is + # not to be exposed. We still assert it here to please the type checker. + assert self.name is not None, ( + f"{self!r} parameter's name should not be None when exposing value." + ) + ctx.params[self.name] = value + + return value, args + + def get_help_record(self, ctx: Context) -> tuple[str, str] | None: + pass + + def get_usage_pieces(self, ctx: Context) -> 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) -> 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("list[CompletionItem]", results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: Show the default value for this option in its + help text. Values are not shown by default, unless + :attr:`Context.show_default` is ``True``. If this value is a + string, it shows that string in parentheses instead of the + actual value. This is particularly useful for dynamic options. + For single option boolean flags, the default remains hidden if + its value is ``False``. + :param show_envvar: Controls if an environment variable should be + shown on the help page and error messages. + 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. A deprecated option cannot be + prompted. + :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. + :param attrs: Other command arguments described in :class:`Parameter`. + + .. versionchanged:: 8.2 + ``envvar`` used with ``flag_value`` will always use the ``flag_value``, + previously it would use the value of the environment variable. + + .. versionchanged:: 8.1 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1 + The default of a single option boolean flag is not shown if the + default value is ``False``. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: cabc.Sequence[str] | None = None, + show_default: bool | str | None = None, + prompt: bool | str = False, + confirmation_prompt: bool | str = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: bool | None = None, + flag_value: t.Any = UNSET, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: types.ParamType | t.Any | None = None, + help: str | None = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + deprecated: bool | str = False, + **attrs: t.Any, + ) -> None: + if help: + help = inspect.cleandoc(help) + + super().__init__( + param_decls, type=type, multiple=multiple, deprecated=deprecated, **attrs + ) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: str | None = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + + if deprecated: + deprecated_message = ( + f"(DEPRECATED: {deprecated})" + if isinstance(deprecated, str) + else "(DEPRECATED)" + ) + help = help + deprecated_message if help is not None else deprecated_message + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # The _flag_needs_value property tells the parser that this option is a flag + # that cannot be used standalone and needs a value. With this information, the + # parser can determine whether to consider the next user-provided argument in + # the CLI as a value for this flag or as a new option. + # If prompt is enabled but not required, then it opens the possibility for the + # option to gets its value from the user. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + # Auto-detect if this is a flag or not. + if is_flag is None: + # Implicitly a flag because flag_value was set. + if flag_value is not UNSET: + is_flag = True + # Not a flag, but when used as a flag it shows a prompt. + elif self._flag_needs_value: + is_flag = False + # Implicitly a flag because secondary options names were given. + elif self.secondary_opts: + is_flag = True + + # The option is explicitly not a flag, but to determine whether or not it needs + # value, we need to check if `flag_value` or `default` was set. Either one is + # sufficient. + # Ref: https://github.com/pallets/click/issues/3084 + elif is_flag is False and not self._flag_needs_value: + self._flag_needs_value = flag_value is not UNSET or self.default is UNSET + + if is_flag: + # Set missing default for flags if not explicitly required or prompted. + if self.default is UNSET and not self.required and not self.prompt: + if multiple: + self.default = () + + # Auto-detect the type of the flag based on the flag_value. + if type is None: + # A flag without a flag_value is a boolean flag. + if flag_value is UNSET: + self.type: types.ParamType = types.BoolParamType() + # If the flag value is a boolean, use BoolParamType. + elif isinstance(flag_value, bool): + self.type = types.BoolParamType() + # Otherwise, guess the type from the flag value. + else: + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = bool(is_flag) + self.is_bool_flag: bool = bool( + is_flag and isinstance(self.type, types.BoolParamType) + ) + self.flag_value: t.Any = flag_value + + # Set boolean flag default to False if unset and not required. + if self.is_bool_flag: + if self.default is UNSET and not self.required: + self.default = False + + # The alignement of default to the flag_value is resolved lazily in + # get_default() to prevent callable flag_values (like classes) from + # being instantiated. Refs: + # https://github.com/pallets/click/issues/3121 + # https://github.com/pallets/click/issues/3024#issuecomment-3146199461 + # https://github.com/pallets/click/pull/3030/commits/06847da + + # Set the default flag_value if it is not set. + if self.flag_value is UNSET: + if self.is_flag: + self.flag_value = True + else: + self.flag_value = None + + # Counting. + self.count = count + if count: + if type is None: + self.type = types.IntRange(min=0) + if self.default is UNSET: + 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 deprecated and prompt: + raise ValueError("`deprecated` options cannot use `prompt`.") + + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + + 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) -> dict[str, t.Any]: + """ + .. versionchanged:: 8.3.0 + Returns ``None`` for the :attr:`flag_value` if it was not set. + """ + info_dict = super().to_info_dict() + info_dict.update( + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + # We explicitly hide the :attr:`UNSET` value to the user, as we choose to + # make it an implementation detail. And because ``to_info_dict`` has been + # designed for documentation purposes, we return ``None`` instead. + flag_value=self.flag_value if self.flag_value is not UNSET else None, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Any | t.Callable[[], t.Any] | None: + """Return the default value for this option. + + For non-boolean flag options, ``default=True`` is treated as a sentinel + meaning "activate this flag by default" and is resolved to + :attr:`flag_value`. For example, with ``--upper/--lower`` feature + switches where ``flag_value="upper"`` and ``default=True``, the default + resolves to ``"upper"``. + + .. caution:: + This substitution only applies to non-boolean flags + (:attr:`is_bool_flag` is ``False``). For boolean flags, ``True`` is + a legitimate Python value and ``default=True`` is returned as-is. + + .. versionchanged:: 8.3.3 + ``default=True`` is no longer substituted with ``flag_value`` for + boolean flags, fixing negative boolean flags like + ``flag_value=False, default=True``. + """ + value = super().get_default(ctx, call=False) + + # Resolve default=True to flag_value lazily (here instead of + # __init__) to prevent callable flag_values (like classes) from + # being instantiated by the callable check below. + if value is True and self.is_flag and not self.is_bool_flag: + value = self.flag_value + elif call and callable(value): + value = value() + + return value + + def get_error_hint(self, ctx: Context) -> str: + result = super().get_error_hint(ctx) + if self.show_envvar and self.envvar is not None: + result += f" (env var: '{self.envvar}')" + return result + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str | None, list[str], 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( + f"Could not determine name for option with declarations {decls!r}" + ) + + 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) -> tuple[str, str] | None: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: cabc.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(ctx=ctx)}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + + extra = self.get_help_extra(ctx) + extra_items = [] + if "envvars" in extra: + extra_items.append( + _("env var: {var}").format(var=", ".join(extra["envvars"])) + ) + if "default" in extra: + extra_items.append(_("default: {default}").format(default=extra["default"])) + if "range" in extra: + extra_items.append(extra["range"]) + if "required" in extra: + extra_items.append(_(extra["required"])) + + if extra_items: + extra_str = "; ".join(extra_items) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + def get_help_extra(self, ctx: Context) -> types.OptionHelpExtra: + extra: types.OptionHelpExtra = {} + + 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: + if isinstance(envvar, str): + extra["envvars"] = (envvar,) + else: + extra["envvars"] = tuple(str(d) for d in envvar) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default = False + show_default_is_str = False + + if self.show_default is not None: + if isinstance(self.show_default, str): + show_default_is_str = show_default = True + else: + show_default = self.show_default + elif ctx.show_default is not None: + show_default = ctx.show_default + + if show_default_is_str or ( + show_default and (default_value not in (None, UNSET)) + ): + 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 isinstance(default_value, enum.Enum): + default_string = default_value.name + 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 default_value else self.secondary_opts)[0] + )[1] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + elif isinstance(default_value, str) and default_value == "": + default_string = '""' + else: + default_string = str(default_value) + + if default_string: + extra["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["range"] = range_str + + if self.required: + extra["required"] = "required" + + return extra + + 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 lock in the value before + # attempting any user interaction. + default = self.get_default(ctx) + + # A boolean flag can use a simplified [y/n] confirmation prompt. + if self.is_bool_flag: + # If we have no boolean default, we force the user to explicitly provide + # one. + if default in (UNSET, None): + default = None + # Nothing prevent you to declare an option that is simultaneously: + # 1) auto-detected as a boolean flag, + # 2) allowed to prompt, and + # 3) still declare a non-boolean default. + # This forced casting into a boolean is necessary to align any non-boolean + # default to the prompt, which is going to be a [y/n]-style confirmation + # because the option is still a boolean flag. That way, instead of [y/n], + # we get [Y/n] or [y/N] depending on the truthy value of the default. + # Refs: https://github.com/pallets/click/pull/3030#discussion_r2289180249 + else: + default = bool(default) + return confirm(self.prompt, default) + + # If show_default is given, provide this to `prompt` as well, + # otherwise we use `prompt`'s default behavior + prompt_kwargs: t.Any = {} + if self.show_default is not None: + prompt_kwargs["show_default"] = self.show_default + + return prompt( + self.prompt, + # Use ``None`` to inform the prompt() function to reiterate until a valid + # value is provided by the user if we have no default. + default=None if default is UNSET else 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), + **prompt_kwargs, + ) + + def resolve_envvar_value(self, ctx: Context) -> str | None: + """:class:`Option` resolves its environment variable the same way as + :func:`Parameter.resolve_envvar_value`, but it also supports + :attr:`Context.auto_envvar_prefix`. If we could not find an environment from + the :attr:`envvar` property, we fallback on :attr:`Context.auto_envvar_prefix` + to build dynamiccaly the environment variable name using the + :python:`{ctx.auto_envvar_prefix}_{self.name.upper()}` template. + + :meta private: + """ + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + return rv + + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Any: + """For :class:`Option`, this method processes the raw environment variable + string the same way as :func:`Parameter.value_from_envvar` does. + + But in the case of non-boolean flags, the value is analyzed to determine if the + flag is activated or not, and returns a boolean of its activation, or the + :attr:`flag_value` if the latter is set. + + This method also takes care of repeated options (i.e. options with + :attr:`multiple` set to ``True``). + + :meta private: + """ + rv = self.resolve_envvar_value(ctx) + + # Absent environment variable or an empty string is interpreted as unset. + if rv is None: + return None + + # Non-boolean flags are more liberal in what they accept. But a flag being a + # flag, its envvar value still needs to be analyzed to determine if the flag is + # activated or not. + if self.is_flag and not self.is_bool_flag: + # If the flag_value is set and match the envvar value, return it + # directly. + if self.flag_value is not UNSET and rv == self.flag_value: + return self.flag_value + # Analyze the envvar value as a boolean to know if the flag is + # activated or not. + return types.BoolParamType.str_to_bool(rv) + + # Split the envvar value if it is allowed to be repeated. + value_depth = (self.nargs != 1) + bool(self.multiple) + if value_depth > 0: + multi_rv = self.type.split_envvar_value(rv) + if self.multiple and self.nargs != 1: + multi_rv = batch(multi_rv, self.nargs) # type: ignore[assignment] + + return multi_rv + + return rv + + def consume_value( + self, ctx: Context, opts: cabc.Mapping[str, Parameter] + ) -> tuple[t.Any, ParameterSource]: + """For :class:`Option`, the value can be collected from an interactive prompt + if the option is a flag that needs a value (and the :attr:`prompt` property is + set). + + Additionally, this method handles flag option that are activated without a + value, in which case the :attr:`flag_value` is returned. + + :meta private: + """ + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option is allowed to as a flag + # without a value. + if value is FLAG_NEEDS_VALUE: + # If the option allows for a prompt, we start an interaction with the user. + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + # Else the flag takes its flag_value as value. + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + # A flag which is activated always returns the flag value, unless the value + # comes from the explicitly sets default. + elif ( + self.is_flag + and value is True + and not self.is_bool_flag + and source < ParameterSource.DEFAULT_MAP + ): + value = self.flag_value + + # Re-interpret a multiple option which has been sent as-is by the parser. + # Here we replace each occurrence of value-less flags (marked by the + # FLAG_NEEDS_VALUE sentinel) with the flag_value. + elif ( + self.multiple + and value is not UNSET + and source < ParameterSource.DEFAULT_MAP + 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 for one to the user + # if prompting is enabled. + elif ( + (value is UNSET or source >= ParameterSource.DEFAULT_MAP) + 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 + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + # process_value has to be overridden on Options in order to capture + # `value == UNSET` cases before `type_cast_value()` gets called. + # + # Refs: + # https://github.com/pallets/click/issues/3069 + if self.is_flag and not self.required and self.is_bool_flag and value is UNSET: + value = False + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + # in the normal case, rely on Parameter.process_value + return super().process_value(ctx, value) + + +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 constructor of :class:`Parameter`. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: cabc.Sequence[str], + required: bool | None = None, + **attrs: t.Any, + ) -> None: + # Auto-detect the requirement status of the argument if not explicitly set. + if required is None: + # The argument gets automatically required if it has no explicit default + # value set and is setup to match at least one value. + if attrs.get("default", UNSET) is UNSET: + required = attrs.get("nargs", 1) > 0 + # If the argument has a default value, it is not required. + else: + required = False + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + super().__init__(param_decls, required=required, **attrs) + + @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, ctx: Context) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(param=self, ctx=ctx) + if not var: + var = self.name.upper() # type: ignore + if self.deprecated: + var += "!" + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str | None, list[str], list[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Argument is marked as exposed, but does not have a name.") + 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)}: {decls}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> list[str]: + return [self.make_metavar(ctx)] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar(ctx)}'" + + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) + + +def __getattr__(name: str) -> object: + import warnings + + if name == "BaseCommand": + warnings.warn( + "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Command' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _BaseCommand + + if name == "MultiCommand": + warnings.warn( + "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Group' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _MultiCommand + + raise AttributeError(name) diff --git a/venv/Lib/site-packages/click/decorators.py b/venv/Lib/site-packages/click/decorators.py new file mode 100644 index 0000000..21f4c34 --- /dev/null +++ b/venv/Lib/site-packages/click/decorators.py @@ -0,0 +1,551 @@ +from __future__ import annotations + +import inspect +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 + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") +T = t.TypeVar("T") +_AnyCallable = t.Callable[..., t.Any] +FC = t.TypeVar("FC", bound="_AnyCallable | Command") + + +def pass_context(f: t.Callable[te.Concatenate[Context, P], R]) -> t.Callable[P, R]: + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(new_func, f) + + +def pass_obj(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + """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: P.args, **kwargs: P.kwargs) -> R: + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + +def make_pass_decorator( + object_type: type[T], ensure: bool = False +) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]: + """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: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + ctx = get_current_context() + + obj: T | None + 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(new_func, f) + + return decorator + + +def pass_meta_key( + key: str, *, doc_description: str | None = None +) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]: + """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: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + if doc_description is None: + doc_description = f"the {key!r} key from :attr:`click.Context.meta`" + + decorator.__doc__ = ( + f"Decorator that passes {doc_description} as the first argument" + " to the decorated function." + ) + return decorator + + +CmdType = t.TypeVar("CmdType", bound=Command) + + +# variant: no call, directly as decorator for a function. +@t.overload +def command(name: _AnyCallable) -> Command: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) +@t.overload +def command( + name: str | None, + cls: type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) +@t.overload +def command( + name: None = None, + *, + cls: type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def command( + name: str | None = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Command]: ... + + +def command( + name: str | _AnyCallable | None = None, + cls: type[CmdType] | None = None, + **attrs: t.Any, +) -> Command | t.Callable[[_AnyCallable], Command | CmdType]: + 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, converted to + lowercase, with underscores ``_`` replaced by dashes ``-``, and the suffixes + ``_command``, ``_cmd``, ``_group``, and ``_grp`` are removed. For example, + ``init_data_command`` becomes ``init-data``. + + All keyword arguments are forwarded to the underlying command class. + For the ``params`` argument, any decorated params are appended to + the end of the list. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: The name of the command. Defaults to modifying the function's + name as described above. + :param cls: The command class to create. Defaults to :class:`Command`. + + .. versionchanged:: 8.2 + The suffixes ``_command``, ``_cmd``, ``_group``, and ``_grp`` are + removed when generating the name. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.1 + The ``params`` argument can be used. Decorated params are + appended to the end of the list. + """ + + func: t.Callable[[_AnyCallable], t.Any] | None = None + + if callable(name): + func = name + name = None + assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." + assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." + + if cls is None: + cls = t.cast("type[CmdType]", Command) + + def decorator(f: _AnyCallable) -> CmdType: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + attr_params = attrs.pop("params", None) + params = attr_params if attr_params is not None else [] + + try: + decorator_params = f.__click_params__ # type: ignore + except AttributeError: + pass + else: + del f.__click_params__ # type: ignore + params.extend(reversed(decorator_params)) + + if attrs.get("help") is None: + attrs["help"] = f.__doc__ + + if t.TYPE_CHECKING: + assert cls is not None + assert not callable(name) + + if name is not None: + cmd_name = name + else: + cmd_name = f.__name__.lower().replace("_", "-") + cmd_left, sep, suffix = cmd_name.rpartition("-") + + if sep and suffix in {"command", "cmd", "group", "grp"}: + cmd_name = cmd_left + + cmd = cls(name=cmd_name, callback=f, params=params, **attrs) + cmd.__doc__ = f.__doc__ + return cmd + + if func is not None: + return decorator(func) + + return decorator + + +GrpType = t.TypeVar("GrpType", bound=Group) + + +# variant: no call, directly as decorator for a function. +@t.overload +def group(name: _AnyCallable) -> Group: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) +@t.overload +def group( + name: str | None, + cls: type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) +@t.overload +def group( + name: None = None, + *, + cls: type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def group( + name: str | None = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Group]: ... + + +def group( + name: str | _AnyCallable | None = None, + cls: type[GrpType] | None = None, + **attrs: t.Any, +) -> Group | t.Callable[[_AnyCallable], Group | GrpType]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + """ + if cls is None: + cls = t.cast("type[GrpType]", Group) + + if callable(name): + return command(cls=cls, **attrs)(name) + + return command(name, cls, **attrs) + + +def _param_memo(f: t.Callable[..., t.Any], 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, cls: type[Argument] | None = None, **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. + + For the default argument class, refer to :class:`Argument` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Argument + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def option( + *param_decls: str, cls: type[Option] | None = None, **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. + + For the default option class, refer to :class:`Option` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Option + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **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: str | None = None, + *param_decls: str, + package_name: str | None = None, + prog_name: str | None = None, + message: str | None = 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``. + + 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: + import importlib.metadata + + try: + version = importlib.metadata.version(package_name) + except importlib.metadata.PackageNotFoundError: + 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( + 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]: + """Pre-configured ``--help`` option which immediately prints the help page + and exits the program. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def show_help(ctx: Context, param: Parameter, value: bool) -> None: + """Callback that print the help page on ```` and exits.""" + if value and not ctx.resilient_parsing: + 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.setdefault("callback", show_help) + + return option(*param_decls, **kwargs) diff --git a/venv/Lib/site-packages/click/exceptions.py b/venv/Lib/site-packages/click/exceptions.py new file mode 100644 index 0000000..4d782ee --- /dev/null +++ b/venv/Lib/site-packages/click/exceptions.py @@ -0,0 +1,308 @@ +from __future__ import annotations + +import collections.abc as cabc +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .globals import resolve_color_default +from .utils import echo +from .utils import format_filename + +if t.TYPE_CHECKING: + from .core import Command + from .core import Context + from .core import Parameter + + +def _join_param_hints(param_hint: cabc.Sequence[str] | str | None) -> str | None: + 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) + # The context will be removed by the time we print the message, so cache + # the color settings here to be used later on (in `show`) + self.show_color: bool | None = resolve_color_default() + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.IO[t.Any] | None = None) -> None: + if file is None: + file = get_text_stderr() + + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=self.show_color, + ) + + +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: Context | None = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd: Command | None = self.ctx.command if self.ctx else None + + def show(self, file: t.IO[t.Any] | None = 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: Context | None = None, + param: Parameter | None = None, + param_hint: cabc.Sequence[str] | str | None = 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: str | None = None, + ctx: Context | None = None, + param: Parameter | None = None, + param_hint: cabc.Sequence[str] | str | None = None, + param_type: str | None = 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: cabc.Sequence[str] | str | None = 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( + param=self.param, ctx=self.ctx + ) + 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: str | None = None, + possibilities: cabc.Sequence[str] | None = None, + ctx: Context | None = 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: Context | None = 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 NoArgsIsHelpError(UsageError): + def __init__(self, ctx: Context) -> None: + self.ctx: Context + super().__init__(ctx.get_help(), ctx=ctx) + + def show(self, file: t.IO[t.Any] | None = None) -> None: + echo(self.format_message(), file=file, err=True, color=self.ctx.color) + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: str | None = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename: str = format_filename(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: int = code diff --git a/venv/Lib/site-packages/click/formatting.py b/venv/Lib/site-packages/click/formatting.py new file mode 100644 index 0000000..0b64f83 --- /dev/null +++ b/venv/Lib/site-packages/click/formatting.py @@ -0,0 +1,301 @@ +from __future__ import annotations + +import collections.abc as cabc +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: int | None = None + + +def measure_table(rows: cabc.Iterable[tuple[str, str]]) -> tuple[int, ...]: + widths: 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: cabc.Iterable[tuple[str, str]], col_count: int +) -> cabc.Iterator[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: list[tuple[int, bool, str]] = [] + buf: 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: int | None = None, + max_width: int | None = None, + ) -> None: + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + import shutil + + 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: int = 0 + self.buffer: 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: str | None = 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: cabc.Sequence[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) -> cabc.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) -> cabc.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: cabc.Sequence[str]) -> tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = _split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/venv/Lib/site-packages/click/globals.py b/venv/Lib/site-packages/click/globals.py new file mode 100644 index 0000000..a2f9172 --- /dev/null +++ b/venv/Lib/site-packages/click/globals.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +import typing as t +from threading import local + +if t.TYPE_CHECKING: + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: t.Literal[False] = False) -> Context: ... + + +@t.overload +def get_current_context(silent: bool = ...) -> Context | None: ... + + +def get_current_context(silent: bool = False) -> Context | None: + """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: bool | None = None) -> bool | None: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/venv/Lib/site-packages/click/parser.py b/venv/Lib/site-packages/click/parser.py new file mode 100644 index 0000000..1ea1f71 --- /dev/null +++ b/venv/Lib/site-packages/click/parser.py @@ -0,0 +1,532 @@ +""" +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 +from __future__ import annotations + +import collections.abc as cabc +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from ._utils import FLAG_NEEDS_VALUE +from ._utils import UNSET +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + from ._utils import T_FLAG_NEEDS_VALUE + from ._utils import T_UNSET + 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") + + +def _unpack_args( + args: cabc.Sequence[str], nargs_spec: cabc.Sequence[int] +) -> tuple[cabc.Sequence[str | cabc.Sequence[str | None] | None], 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 ``UNSET``. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: list[str | tuple[str | T_UNSET, ...] | T_UNSET] = [] + spos: int | None = None + + def _fetch(c: deque[V]) -> V | T_UNSET: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return UNSET + + while nargs_spec: + nargs = _fetch(nargs_spec) + + if nargs is None: + continue + + if nargs == 1: + rv.append(_fetch(args)) # type: ignore[arg-type] + 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(UNSET) + + # 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) -> 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: Context | None) -> 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)}" + + +class _Option: + def __init__( + self, + obj: CoreOption, + opts: cabc.Sequence[str], + dest: str | None, + action: str | None = None, + nargs: int = 1, + const: t.Any | None = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes: set[str] = 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: t.Any, 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: str | None, nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: str | cabc.Sequence[str | None] | None | T_UNSET, + state: _ParsingState, + ) -> None: + if self.nargs > 1: + assert isinstance(value, cabc.Sequence) + holes = sum(1 for x in value if x is UNSET) + if holes == len(value): + value = UNSET + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + # We failed to collect any argument value so we consider the argument as unset. + if value == (): + value = UNSET + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class _ParsingState: + def __init__(self, rargs: list[str]) -> None: + self.opts: dict[str, t.Any] = {} + self.largs: list[str] = [] + self.rargs = rargs + self.order: 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. + + .. deprecated:: 8.2 + Will be removed in Click 9.0. + """ + + def __init__(self, ctx: Context | None = 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: bool = 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: bool = 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: dict[str, _Option] = {} + self._long_opt: dict[str, _Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: list[_Argument] = [] + + def add_option( + self, + obj: CoreOption, + opts: cabc.Sequence[str], + dest: str | None, + action: str | None = None, + nargs: int = 1, + const: t.Any | None = 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: str | None, 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: list[str] + ) -> tuple[dict[str, t.Any], list[str], 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: str | None, 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 = UNSET + + 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 = UNSET + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we recombine 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 + ) -> str | cabc.Sequence[str] | T_FLAG_NEEDS_VALUE: + nargs = option.nargs + + value: str | cabc.Sequence[str] | T_FLAG_NEEDS_VALUE + + 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) + + +def __getattr__(name: str) -> object: + import warnings + + if name in { + "OptionParser", + "Argument", + "Option", + "split_opt", + "normalize_opt", + "ParsingState", + }: + warnings.warn( + f"'parser.{name}' is deprecated and will be removed in Click 9.0." + " The old parser is available in 'optparse'.", + DeprecationWarning, + stacklevel=2, + ) + return globals()[f"_{name}"] + + if name == "split_arg_string": + from .shell_completion import split_arg_string + + warnings.warn( + "Importing 'parser.split_arg_string' is deprecated, it will only be" + " available in 'shell_completion' in Click 9.0.", + DeprecationWarning, + stacklevel=2, + ) + return split_arg_string + + raise AttributeError(name) diff --git a/venv/Lib/site-packages/click/py.typed b/venv/Lib/site-packages/click/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/click/shell_completion.py b/venv/Lib/site-packages/click/shell_completion.py new file mode 100644 index 0000000..8f1564c --- /dev/null +++ b/venv/Lib/site-packages/click/shell_completion.py @@ -0,0 +1,667 @@ +from __future__ import annotations + +import collections.abc as cabc +import os +import re +import typing as t +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 .core import ParameterSource +from .utils import echo + + +def shell_complete( + cli: Command, + ctx_args: cabc.MutableMapping[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: str | None = None, + **kwargs: t.Any, + ) -> None: + self.value: t.Any = value + self.type: str = type + self.help: str | None = 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; +""" + +# See ZshComplete.format_completion below, and issue #2703, before +# changing this script. +# +# (TL;DR: _describe is picky about the format, but this Zsh script snippet +# is already widely deployed. So freeze this script, and use clever-ish +# handling of colons in ZshComplet.format_completion.) +_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 +} + +if [[ $zsh_eval_context[-1] == loadautofunc ]]; then + # autoload from fpath, call function directly + %(complete_func)s "$@" +else + # eval/source/. command, register function for later + compdef %(complete_func)s %(prog_name)s +fi +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + + 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: Command, + ctx_args: cabc.MutableMapping[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("-", "_"), flags=re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> 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) -> tuple[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: list[str], incomplete: str) -> 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 + + @staticmethod + def _check_version() -> None: + import shutil + import subprocess + + bash_exe = shutil.which("bash") + + if bash_exe is None: + match = None + else: + output = subprocess.run( + [bash_exe, "--norc", "-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": + echo( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ), + err=True, + ) + else: + echo( + _("Couldn't detect Bash version, shell completion is not supported."), + err=True, + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> tuple[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) -> tuple[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: + help_ = item.help or "_" + # The zsh completion script uses `_describe` on items with help + # texts (which splits the item help from the item value at the + # first unescaped colon) and `compadd` on items without help + # text (which uses the item value as-is and does not support + # colon escaping). So escape colons in the item value if and + # only if the item help is not the sentinel "_" value, as used + # by the completion script. + # + # (The zsh completion script is potentially widely deployed, and + # thus harder to fix than this method.) + # + # See issue #1812 and issue #2703 for further context. + value = item.value.replace(":", r"\:") if help_ != "_" else item.value + return f"{item.type}\n{value}\n{help_}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name = "fish" + source_template = _SOURCE_FISH + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + if incomplete: + incomplete = split_arg_string(incomplete)[0] + 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}" + + +ShellCompleteType = t.TypeVar("ShellCompleteType", bound="type[ShellComplete]") + + +_available_shells: dict[str, type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: ShellCompleteType, name: str | None = None +) -> ShellCompleteType: + """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 + + return cls + + +def get_completion_class(shell: str) -> type[ShellComplete] | None: + """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 split_arg_string(string: str) -> 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. + + .. versionchanged:: 8.2 + Moved to ``shell_completion`` from ``parser``. + """ + 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 + + +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 + # Will be None if expose_value is False. + value = ctx.params.get(param.name) + return ( + param.nargs == -1 + or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE + or ( + param.nargs > 1 + and isinstance(value, (tuple, list)) + and len(value) < param.nargs + ) + ) + + +def _start_of_option(ctx: Context, value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + return c in ctx._opt_prefixes + + +def _is_incomplete_option(ctx: Context, args: list[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag or param.count: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(ctx, arg): + last_option = arg + break + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + args: 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 + with cli.make_context(prog_name, args.copy(), **ctx_args) as ctx: + args = ctx._protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, Group): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + with cmd.make_context( + name, args, parent=ctx, resilient_parsing=True + ) as sub_ctx: + ctx = sub_ctx + args = ctx._protected_args + ctx.args + else: + sub_ctx = ctx + + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + with cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) as sub_sub_ctx: + sub_ctx = sub_sub_ctx + 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: list[str], incomplete: str +) -> tuple[Command | Parameter, str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(ctx, incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(ctx, incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(ctx, args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/venv/Lib/site-packages/click/termui.py b/venv/Lib/site-packages/click/termui.py new file mode 100644 index 0000000..48f671b --- /dev/null +++ b/venv/Lib/site-packages/click/termui.py @@ -0,0 +1,891 @@ +from __future__ import annotations + +import collections.abc as cabc +import inspect +import io +import itertools +import sys +import typing as t +from contextlib import AbstractContextManager +from gettext import gettext as _ + +from ._compat import isatty +from ._compat import strip_ansi +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 | str = False, + default: t.Any | None = None, + show_choices: bool = True, + type: ParamType | None = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + if isinstance(show_default, str): + default = f"({show_default})" + 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 + + return default + + +def prompt( + text: str, + default: t.Any | None = None, + hide_input: bool = False, + confirmation_prompt: bool | str = False, + type: ParamType | t.Any | None = None, + value_proc: t.Callable[[str], t.Any] | None = None, + prompt_suffix: str = ": ", + show_default: bool | str = 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. + If this value is a string, it shows that string + in parentheses instead of the actual value. + :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): ". + + .. versionchanged:: 8.3.3 + ``show_default`` can be a string to show a custom value instead + of the actual default, matching the help text behavior. + + .. versionchanged:: 8.3.1 + A space is no longer appended to the prompt. + + .. 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[:-1], nl=False, err=err) + # Echo the last character to stdout to work around an issue where + # readline causes backspace to clear the whole line. + return f(text[-1:]) + 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) + continue + if not confirmation_prompt: + return result + while True: + value2 = prompt_func(confirmation_prompt) + is_empty = not value and not value2 + if value2 or is_empty: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: bool | None = 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.3.1 + A space is no longer appended to the prompt. + + .. 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[:-1], nl=False, err=err) + # Echo the last character to stdout to work around an issue where + # readline causes backspace to clear the whole line. + value = visible_prompt_func(prompt[-1:]).lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def echo_via_pager( + text_or_generator: cabc.Iterable[str] | t.Callable[[], cabc.Iterable[str]] | str, + color: bool | None = 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[[], cabc.Iterable[str]]", text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast("cabc.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) + + +@t.overload +def progressbar( + *, + length: int, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[int]: ... + + +@t.overload +def progressbar( + iterable: cabc.Iterable[V] | None = None, + length: int | None = None, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[V]: ... + + +def progressbar( + iterable: cabc.Iterable[V] | None = None, + length: int | None = None, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = 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 hidden: hide the progressbar. Defaults to ``False``. When no tty is + detected, it will only print the progressbar label. Setting this to + ``False`` also disables that. + :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. + + .. versionadded:: 8.2 + The ``hidden`` argument. + + .. 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 + The ``update_min_steps`` parameter. + + .. versionadded:: 4.0 + The ``color`` parameter and ``update`` method. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + hidden=hidden, + 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 + + # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor + echo("\033[2J\033[1;1H", nl=False) + + +def _interpret_color(color: int | 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: int | tuple[int, int, int] | str | None = None, + bg: int | tuple[int, int, int] | str | None = None, + bold: bool | None = None, + dim: bool | None = None, + underline: bool | None = None, + overline: bool | None = None, + italic: bool | None = None, + blink: bool | None = None, + reverse: bool | None = None, + strikethrough: bool | None = 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.Any | None = None, + file: t.IO[t.AnyStr] | None = None, + nl: bool = True, + err: bool = False, + color: bool | None = 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) + + +@t.overload +def edit( + text: bytes | bytearray, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = False, + extension: str = ".txt", +) -> bytes | None: ... + + +@t.overload +def edit( + text: str, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", +) -> str | None: ... + + +@t.overload +def edit( + text: None = None, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + filename: str | cabc.Iterable[str] | None = None, +) -> None: ... + + +def edit( + text: str | bytes | bytearray | None = None, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + filename: str | cabc.Iterable[str] | None = None, +) -> str | bytes | bytearray | None: + 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. If the editor supports + editing multiple files at once, a sequence of files may be + passed as well. Invoke `click.file` once per file instead + if multiple files cannot be managed at once or editing the + files serially is desired. + + .. versionchanged:: 8.2.0 + ``filename`` now accepts any ``Iterable[str]`` in addition to a ``str`` + if the ``editor`` supports editing multiple files at once. + + """ + 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) + + if isinstance(filename, str): + filename = (filename,) + + ed.edit_files(filenames=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.Callable[[bool], str] | None = 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() -> AbstractContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: str | None = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/venv/Lib/site-packages/click/testing.py b/venv/Lib/site-packages/click/testing.py new file mode 100644 index 0000000..04e7f1d --- /dev/null +++ b/venv/Lib/site-packages/click/testing.py @@ -0,0 +1,669 @@ +from __future__ import annotations + +import collections.abc as cabc +import contextlib +import io +import os +import pdb +import shlex +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import _compat +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from _typeshed import ReadableBuffer + + from .core import Command + + +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) -> list[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> cabc.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: EchoingStdin | None) -> cabc.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class BytesIOCopy(io.BytesIO): + """Patch ``io.BytesIO`` to let the written stream be copied to another. + + .. versionadded:: 8.2 + """ + + def __init__(self, copy_to: io.BytesIO) -> None: + super().__init__() + self.copy_to = copy_to + + def flush(self) -> None: + super().flush() + self.copy_to.flush() + + def write(self, b: ReadableBuffer) -> int: + self.copy_to.write(b) + return super().write(b) + + +class StreamMixer: + """Mixes `` and `` streams. + + The result is available in the ``output`` attribute. + + .. versionadded:: 8.2 + """ + + def __init__(self) -> None: + self.output: io.BytesIO = io.BytesIO() + self.stdout: io.BytesIO = BytesIOCopy(copy_to=self.output) + self.stderr: io.BytesIO = BytesIOCopy(copy_to=self.output) + + +class _NamedTextIOWrapper(io.TextIOWrapper): + """A :class:`~io.TextIOWrapper` with custom ``name`` and ``mode`` + that does not close its underlying buffer. + + An optional ``original_fd`` preserves the file descriptor of the + stream being replaced, so that C-level consumers that call + :meth:`fileno` (``faulthandler``, ``subprocess``, ...) still work. + Inspired by pytest's ``capsys``/``capfd`` split: see :doc:`/testing` + for details. + + .. versionchanged:: 8.3.3 + Added ``original_fd`` parameter and :meth:`fileno` override. + """ + + def __init__( + self, + buffer: t.BinaryIO, + name: str, + mode: str, + *, + original_fd: int = -1, + **kwargs: t.Any, + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + self._original_fd = original_fd + + def close(self) -> None: + """The buffer this object contains belongs to some other object, + so prevent the default ``__del__`` implementation from closing + that buffer. + + .. versionadded:: 8.3.2 + """ + + def fileno(self) -> int: + """Return the file descriptor of the original stream, if one was + provided at construction time. + + This allows C-level consumers (``faulthandler``, ``subprocess``, + signal handlers, ...) to obtain a valid fd without crashing, even + though the Python-level writes are redirected to an in-memory + buffer. + + .. versionadded:: 8.3.3 + """ + if self._original_fd >= 0: + return self._original_fd + return super().fileno() + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + +def make_input_stream( + input: str | bytes | t.IO[t.Any] | None, charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast("t.IO[t.Any]", 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(input) + + +class Result: + """Holds the captured result of an invoked CLI script. + + :param runner: The runner that created the result + :param stdout_bytes: The standard output as bytes. + :param stderr_bytes: The standard error as bytes. + :param output_bytes: A mix of ``stdout_bytes`` and ``stderr_bytes``, as the + user would see it in its terminal. + :param return_value: The value returned from the invoked command. + :param exit_code: The exit code as integer. + :param exception: The exception that happened if one did. + :param exc_info: Exception information (exception type, exception instance, + traceback type). + + .. versionchanged:: 8.2 + ``stderr_bytes`` no longer optional, ``output_bytes`` introduced and + ``mix_stderr`` has been removed. + + .. versionadded:: 8.0 + Added ``return_value``. + """ + + def __init__( + self, + runner: CliRunner, + stdout_bytes: bytes, + stderr_bytes: bytes, + output_bytes: bytes, + return_value: t.Any, + exit_code: int, + exception: BaseException | None, + exc_info: tuple[type[BaseException], BaseException, TracebackType] + | None = None, + ): + self.runner = runner + self.stdout_bytes = stdout_bytes + self.stderr_bytes = stderr_bytes + self.output_bytes = output_bytes + self.return_value = return_value + self.exit_code = exit_code + self.exception = exception + self.exc_info = exc_info + + @property + def output(self) -> str: + """The terminal output as unicode string, as the user would see it. + + .. versionchanged:: 8.2 + No longer a proxy for ``self.stdout``. Now has its own independent stream + that is mixing `` and ``, in the order they were written. + """ + return self.output_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @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. + + .. versionchanged:: 8.2 + No longer raise an exception, always returns the `` string. + """ + 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 `` writes + to ``. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param catch_exceptions: Whether to catch any exceptions other than + ``SystemExit`` when running :meth:`~CliRunner.invoke`. + + .. versionchanged:: 8.2 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 8.2 + ``mix_stderr`` parameter has been removed. + """ + + def __init__( + self, + charset: str = "utf-8", + env: cabc.Mapping[str, str | None] | None = None, + echo_stdin: bool = False, + catch_exceptions: bool = True, + ) -> None: + self.charset = charset + self.env: cabc.Mapping[str, str | None] = env or {} + self.echo_stdin = echo_stdin + self.catch_exceptions = catch_exceptions + + def get_default_prog_name(self, cli: Command) -> 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: cabc.Mapping[str, str | None] | None = None + ) -> cabc.Mapping[str, str | None]: + """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: str | bytes | t.IO[t.Any] | None = None, + env: cabc.Mapping[str, str | None] | None = None, + color: bool = False, + ) -> cabc.Iterator[tuple[io.BytesIO, io.BytesIO, io.BytesIO]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up `` 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. + + .. versionadded:: 8.2 + An additional output stream is returned, which is a mix of + `` and `` streams. + + .. versionchanged:: 8.2 + Always returns the `` stream. + + .. versionchanged:: 8.0 + `` 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) + + stream_mixer = StreamMixer() + + # Preserve the original file descriptors so that C-level + # consumers (faulthandler, subprocess, etc.) can still obtain a + # valid fd from the redirected streams. The original streams + # may themselves lack a fileno() (e.g. when CliRunner is used + # inside pytest's capsys), so we fall back to -1. + def _safe_fileno(stream: t.IO[t.Any]) -> int: + try: + return stream.fileno() + except (AttributeError, io.UnsupportedOperation): + return -1 + + old_stdout_fd = _safe_fileno(old_stdout) + old_stderr_fd = _safe_fileno(old_stderr) + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, stream_mixer.stdout) + ) + + 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( + stream_mixer.stdout, + encoding=self.charset, + name="", + mode="w", + original_fd=old_stdout_fd, + ) + + sys.stderr = _NamedTextIOWrapper( + stream_mixer.stderr, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + original_fd=old_stderr_fd, + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: str | None = None) -> str: + sys.stdout.write(prompt or "") + try: + val = next(text_input).rstrip("\r\n") + except StopIteration as e: + raise EOFError() from e + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: str | None = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + try: + return next(text_input).rstrip("\r\n") + except StopIteration as e: + raise EOFError() from e + + @_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.IO[t.Any] | None = None, color: bool | None = 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 + old__compat_should_strip_ansi = _compat.should_strip_ansi + old_pdb_init = pdb.Pdb.__init__ + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + _compat.should_strip_ansi = should_strip_ansi + + def _patched_pdb_init( + self: pdb.Pdb, + completekey: str = "tab", + stdin: t.IO[str] | None = None, + stdout: t.IO[str] | None = None, + **kwargs: t.Any, + ) -> None: + """Default ``pdb.Pdb`` to real terminal streams during + ``CliRunner`` isolation. + + Without this patch, ``pdb.Pdb.__init__`` inherits from + ``cmd.Cmd`` which falls back to ``sys.stdin``/``sys.stdout`` + when no explicit streams are provided. During isolation + those are ``BytesIO``-backed wrappers, so the debugger + reads from an empty buffer and writes to captured output, + making interactive debugging impossible. + + By defaulting to ``sys.__stdin__``/``sys.__stdout__`` (the + original terminal streams Python preserves regardless of + redirection), debuggers can interact with the user while + ``click.echo`` output is still captured normally. + + This covers ``pdb.set_trace()``, ``breakpoint()``, + ``pdb.post_mortem()``, and debuggers that subclass + ``pdb.Pdb`` (ipdb, pdbpp). Explicit ``stdin``/``stdout`` + arguments are honored and not overridden. Debuggers that + do not subclass ``pdb.Pdb`` (pudb, debugpy) are not + covered. + """ + if stdin is None: + stdin = sys.__stdin__ + if stdout is None: + stdout = sys.__stdout__ + old_pdb_init( + self, completekey=completekey, stdin=stdin, stdout=stdout, **kwargs + ) + + pdb.Pdb.__init__ = _patched_pdb_init # type: ignore[assignment] + + 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 (stream_mixer.stdout, stream_mixer.stderr, stream_mixer.output) + 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 + _compat.should_strip_ansi = old__compat_should_strip_ansi + formatting.FORCED_WIDTH = old_forced_width + pdb.Pdb.__init__ = old_pdb_init # type: ignore[method-assign] + + def invoke( + self, + cli: Command, + args: str | cabc.Sequence[str] | None = None, + input: str | bytes | t.IO[t.Any] | None = None, + env: cabc.Mapping[str, str | None] | None = None, + catch_exceptions: bool | None = None, + 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``. If :data:`None`, the value + from :class:`CliRunner` is used. + :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. + + .. versionadded:: 8.2 + The result object has the ``output_bytes`` attribute with + the mix of ``stdout_bytes`` and ``stderr_bytes``, as the user would + see it in its terminal. + + .. versionchanged:: 8.2 + The result object always returns the ``stderr_bytes`` stream. + + .. 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 + if catch_exceptions is None: + catch_exceptions = self.catch_exceptions + + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: BaseException | None = 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("int | t.Any | None", 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() + sys.stderr.flush() + stdout = outstreams[0].getvalue() + stderr = outstreams[1].getvalue() + output = outstreams[2].getvalue() + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + output_bytes=output, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: str | os.PathLike[str] | None = None + ) -> cabc.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) + os.chdir(dt) + + try: + yield dt + finally: + os.chdir(cwd) + + if temp_dir is None: + import shutil + + try: + shutil.rmtree(dt) + except OSError: + pass diff --git a/venv/Lib/site-packages/click/types.py b/venv/Lib/site-packages/click/types.py new file mode 100644 index 0000000..e71c1c2 --- /dev/null +++ b/venv/Lib/site-packages/click/types.py @@ -0,0 +1,1209 @@ +from __future__ import annotations + +import collections.abc as cabc +import enum +import os +import stat +import sys +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 open_stream +from .exceptions import BadParameter +from .utils import format_filename +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 + +ParamTypeValue = t.TypeVar("ParamTypeValue") + + +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[str | None] = None + + def to_info_dict(self) -> dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + + # Custom subclasses might not remember to set a name. + if hasattr(self, "name"): + name = self.name + else: + name = param_type + + return {"param_type": param_type, "name": name} + + def __call__( + self, + value: t.Any, + param: Parameter | None = None, + ctx: Context | None = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: Parameter, ctx: Context | None) -> str | None: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> 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) -> cabc.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: Parameter | None = None, + ctx: Context | None = 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 + ) -> 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: str = func.__name__ + self.func = func + + def to_info_dict(self) -> 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: Parameter | None, ctx: Context | None + ) -> 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: Parameter | None, ctx: Context | None + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = sys.getfilesystemencoding() + 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, t.Generic[ParamTypeValue]): + """The choice type allows a value to be checked against a fixed set + of supported values. + + You may pass any iterable value which will be converted to a tuple + and thus will only be iterated once. + + The resulting value will always be one of the originally passed choices. + See :meth:`normalize_choice` for more info on the mapping of strings + to choices. See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + + .. versionchanged:: 8.2.0 + Non-``str`` ``choices`` are now supported. It can additionally be any + iterable. Before you were not recommended to pass anything but a list or + tuple. + + .. versionadded:: 8.2.0 + Choice normalization can be overridden via :meth:`normalize_choice`. + """ + + name = "choice" + + def __init__( + self, choices: cabc.Iterable[ParamTypeValue], case_sensitive: bool = True + ) -> None: + self.choices: cabc.Sequence[ParamTypeValue] = tuple(choices) + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> 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 _normalized_mapping( + self, ctx: Context | None = None + ) -> cabc.Mapping[ParamTypeValue, str]: + """ + Returns mapping where keys are the original choices and the values are + the normalized values that are accepted via the command line. + + This is a simple wrapper around :meth:`normalize_choice`, use that + instead which is supported. + """ + return { + choice: self.normalize_choice( + choice=choice, + ctx=ctx, + ) + for choice in self.choices + } + + def normalize_choice(self, choice: ParamTypeValue, ctx: Context | None) -> str: + """ + Normalize a choice value, used to map a passed string to a choice. + Each choice must have a unique normalized value. + + By default uses :meth:`Context.token_normalize_func` and if not case + sensitive, convert it to a casefolded value. + + .. versionadded:: 8.2.0 + """ + normed_value = choice.name if isinstance(choice, enum.Enum) else str(choice) + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(normed_value) + + if not self.case_sensitive: + normed_value = normed_value.casefold() + + return normed_value + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + if param.param_type_name == "option" and not param.show_choices: # type: ignore + choice_metavars = [ + convert_type(type(choice)).name.upper() for choice in self.choices + ] + choices_str = "|".join([*dict.fromkeys(choice_metavars)]) + else: + choices_str = "|".join( + [str(i) for i in self._normalized_mapping(ctx=ctx).values()] + ) + + # 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, ctx: Context | None) -> str: + """ + Message shown when no choice is passed. + + .. versionchanged:: 8.2.0 Added ``ctx`` argument. + """ + return _("Choose from:\n\t{choices}").format( + choices=",\n\t".join(self._normalized_mapping(ctx=ctx).values()) + ) + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> ParamTypeValue: + """ + For a given value from the parser, normalize it and find its + matching normalized value in the list of choices. Then return the + matched "original" choice. + """ + normed_value = self.normalize_choice(choice=value, ctx=ctx) + normalized_mapping = self._normalized_mapping(ctx=ctx) + + try: + return next( + original + for original, normalized in normalized_mapping.items() + if normalized == normed_value + ) + except StopIteration: + self.fail( + self.get_invalid_choice_message(value=value, ctx=ctx), + param=param, + ctx=ctx, + ) + + def get_invalid_choice_message(self, value: t.Any, ctx: Context | None) -> str: + """Get the error message when the given choice is invalid. + + :param value: The invalid value. + + .. versionadded:: 8.2 + """ + choices_str = ", ".join(map(repr, self._normalized_mapping(ctx=ctx).values())) + return 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) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> 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: cabc.Sequence[str] | None = None): + self.formats: cabc.Sequence[str] = formats or [ + "%Y-%m-%d", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%d %H:%M:%S", + ] + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> datetime | None: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> 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[type[t.Any]] + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> 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: float | None = None, + max: float | None = 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) -> 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: Parameter | None, ctx: Context | None + ) -> 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: t.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: t.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: float | None = None, + max: float | None = 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: t.Literal[1, -1], open: bool) -> float: + if not open: + return bound + + # Could use 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" + + bool_states: dict[str, bool] = { + "1": True, + "0": False, + "yes": True, + "no": False, + "true": True, + "false": False, + "on": True, + "off": False, + "t": True, + "f": False, + "y": True, + "n": False, + # Absence of value is considered False. + "": False, + } + """A mapping of string values to boolean states. + + Mapping is inspired by :py:attr:`configparser.ConfigParser.BOOLEAN_STATES` + and extends it. + + .. caution:: + String values are lower-cased, as the ``str_to_bool`` comparison function + below is case-insensitive. + + .. warning:: + The mapping is not exhaustive, and does not cover all possible boolean strings + representations. It will remains as it is to avoid endless bikeshedding. + + Future work my be considered to make this mapping user-configurable from public + API. + """ + + @staticmethod + def str_to_bool(value: str | bool) -> bool | None: + """Convert a string to a boolean value. + + If the value is already a boolean, it is returned as-is. If the value is a + string, it is stripped of whitespaces and lower-cased, then checked against + the known boolean states pre-defined in the `BoolParamType.bool_states` mapping + above. + + Returns `None` if the value does not match any known boolean state. + """ + if isinstance(value, bool): + return value + return BoolParamType.bool_states.get(value.strip().lower()) + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> bool: + normalized = self.str_to_bool(value) + if normalized is None: + self.fail( + _( + "{value!r} is not a valid boolean. Recognized values: {states}" + ).format(value=value, states=", ".join(sorted(self.bool_states))), + param, + ctx, + ) + return normalized + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> 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. + + 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. + + .. versionchanged:: 2.0 + Added the ``atomic`` parameter. + """ + + name = "filename" + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + lazy: bool | None = 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) -> 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: str | os.PathLike[str]) -> bool: + if self.lazy is not None: + return self.lazy + if os.fspath(value) == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, + value: str | os.PathLike[str] | t.IO[t.Any], + param: Parameter | None, + ctx: Context | None, + ) -> t.IO[t.Any]: + if _is_file_like(value): + return value + + value = t.cast("str | os.PathLike[str]", value) + + try: + lazy = self.resolve_lazy_flag(value) + + if lazy: + lf = LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + if ctx is not None: + ctx.call_on_close(lf.close_intelligently) + + return t.cast("t.IO[t.Any]", lf) + + 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: + self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> 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")] + + +def _is_file_like(value: t.Any) -> te.TypeGuard[t.IO[t.Any]]: + return hasattr(value, "read") or hasattr(value, "write") + + +class Path(ParamType): + """The ``Path`` type is similar to the :class:`File` type, but + returns the filename instead of an open file. Various checks can be + enabled to validate the type of file and permissions. + + :param exists: The file or directory needs to exist for the value to + be valid. If this is not set to ``True``, and the file does not + exist, then all further checks are silently skipped. + :param file_okay: Allow a file as a value. + :param dir_okay: Allow a directory as a value. + :param readable: if true, a readable check is performed. + :param writable: if true, a writable check is performed. + :param executable: if true, an executable check is performed. + :param resolve_path: Make the value absolute and resolve any + symlinks. A ``~`` is not expanded, as this is supposed to be + done by the shell only. + :param allow_dash: Allow a single dash as a value, which indicates + a standard stream (but does not open it). Use + :func:`~click.open_file` to handle opening this value. + :param path_type: Convert the incoming path value to this type. If + ``None``, keep Python's default, which is ``str``. Useful to + convert to :class:`pathlib.Path`. + + .. versionchanged:: 8.1 + Added the ``executable`` parameter. + + .. versionchanged:: 8.0 + Allow passing ``path_type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter: t.ClassVar[str] = 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: type[t.Any] | None = None, + executable: bool = False, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.readable = readable + self.writable = writable + self.executable = executable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name: str = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> 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, value: str | os.PathLike[str] + ) -> str | bytes | os.PathLike[str]: + if self.type is not None and not isinstance(value, self.type): + if self.type is str: + return os.fsdecode(value) + elif self.type is bytes: + return os.fsencode(value) + else: + return t.cast("os.PathLike[str]", self.type(value)) + + return value + + def convert( + self, + value: str | os.PathLike[str], + param: Parameter | None, + ctx: Context | None, + ) -> str | bytes | os.PathLike[str]: + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + rv = os.path.realpath(rv) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=format_filename(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=format_filename(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=format_filename(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=format_filename(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=format_filename(value) + ), + param, + ctx, + ) + + if self.executable and not os.access(value, os.X_OK): + self.fail( + _("{name} {filename!r} is not executable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> 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: cabc.Sequence[type[t.Any] | ParamType]) -> None: + self.types: cabc.Sequence[ParamType] = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> 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: Parameter | None, ctx: Context | None + ) -> 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, strict=False) + ) + + +def convert_type(ty: t.Any | None, default: t.Any | None = 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() + + +class OptionHelpExtra(t.TypedDict, total=False): + envvars: tuple[str, ...] + default: str + range: str + required: str diff --git a/venv/Lib/site-packages/click/utils.py b/venv/Lib/site-packages/click/utils.py new file mode 100644 index 0000000..89cae3b --- /dev/null +++ b/venv/Lib/site-packages/click/utils.py @@ -0,0 +1,630 @@ +from __future__ import annotations + +import collections.abc as cabc +import os +import re +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType +from types import TracebackType + +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 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 + + P = te.ParamSpec("P") + +R = t.TypeVar("R") + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: t.Callable[P, R]) -> t.Callable[P, R | None]: + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | None: + try: + return func(*args, **kwargs) + except Exception: + pass + return None + + return update_wrapper(wrapper, func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(sys.getfilesystemencoding()) + 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. + + :meta private: + """ + # 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 | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + atomic: bool = False, + ): + self.name: str = os.fspath(filename) + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.IO[t.Any] | None + self.should_close: bool + + if self.name == "-": + 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[t.Any]: + """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: + 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: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.close_intelligently() + + def __iter__(self) -> cabc.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO[t.Any]) -> None: + self._file: t.IO[t.Any] = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> KeepOpenFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> cabc.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Any | None = None, + file: t.IO[t.Any] | None = None, + nl: bool = True, + err: bool = False, + color: bool | None = 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() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + return + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, (str, bytes, bytearray)): + out: str | bytes | bytearray | None = 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, color) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: t.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: t.Literal["stdin", "stdout", "stderr"], + encoding: str | None = None, + errors: str | None = "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 | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO[t.Any]: + """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 or Path 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[t.Any]", 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[t.Any]", KeepOpenFile(f)) + + return f + + +def format_filename( + filename: str | bytes | os.PathLike[str] | os.PathLike[bytes], + shorten: bool = False, +) -> str: + """Format a filename as a string for display. Ensures the filename can be + displayed by replacing any invalid bytes or surrogate escapes in the name + with the replacement character ``�``. + + Invalid bytes or surrogate escapes will raise an error when written to a + stream with ``errors="strict"``. This will typically happen with ``stdout`` + when the locale is something like ``en_GB.UTF-8``. + + Many scenarios *are* safe to write surrogates though, due to PEP 538 and + PEP 540, including: + + - Writing to ``stderr``, which uses ``errors="backslashreplace"``. + - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens + stdout and stderr with ``errors="surrogateescape"``. + - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. + - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. + Python opens stdout and stderr with ``errors="surrogateescape"``. + + :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) + else: + filename = os.fspath(filename) + + if isinstance(filename, bytes): + filename = filename.decode(sys.getfilesystemencoding(), "replace") + else: + filename = filename.encode("utf-8", "surrogateescape").decode( + "utf-8", "replace" + ) + + return 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 effect 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[t.Any]) -> 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: str | None = None, _main: ModuleType | None = None +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if _main is None: + _main = sys.modules["__main__"] + + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + # It is set to "" inside a Shiv or PEX zipapp. + if getattr(_main, "__package__", None) in {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: cabc.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> list[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This is intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionchanged:: 8.1 + Invalid glob patterns are treated as empty expansions rather + than raising an error. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + try: + matches = glob(arg, recursive=glob_recursive) + except re.error: + matches = [] + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER b/venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA b/venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA new file mode 100644 index 0000000..a1b5c57 --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA @@ -0,0 +1,441 @@ +Metadata-Version: 2.1 +Name: colorama +Version: 0.4.6 +Summary: Cross-platform colored terminal text. +Project-URL: Homepage, https://github.com/tartley/colorama +Author-email: Jonathan Hartley +License-File: LICENSE.txt +Keywords: ansi,color,colour,crossplatform,terminal,text,windows,xplatform +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Terminals +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7 +Description-Content-Type: text/x-rst + +.. image:: https://img.shields.io/pypi/v/colorama.svg + :target: https://pypi.org/project/colorama/ + :alt: Latest Version + +.. image:: https://img.shields.io/pypi/pyversions/colorama.svg + :target: https://pypi.org/project/colorama/ + :alt: Supported Python versions + +.. image:: https://github.com/tartley/colorama/actions/workflows/test.yml/badge.svg + :target: https://github.com/tartley/colorama/actions/workflows/test.yml + :alt: Build Status + +Colorama +======== + +Makes ANSI escape character sequences (for producing colored terminal text and +cursor positioning) work under MS Windows. + +.. |donate| image:: https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif + :target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD + :alt: Donate with Paypal + +`PyPI for releases `_ | +`Github for source `_ | +`Colorama for enterprise on Tidelift `_ + +If you find Colorama useful, please |donate| to the authors. Thank you! + +Installation +------------ + +Tested on CPython 2.7, 3.7, 3.8, 3.9 and 3.10 and Pypy 2.7 and 3.8. + +No requirements other than the standard library. + +.. code-block:: bash + + pip install colorama + # or + conda install -c anaconda colorama + +Description +----------- + +ANSI escape character sequences have long been used to produce colored terminal +text and cursor positioning on Unix and Macs. Colorama makes this work on +Windows, too, by wrapping ``stdout``, stripping ANSI sequences it finds (which +would appear as gobbledygook in the output), and converting them into the +appropriate win32 calls to modify the state of the terminal. On other platforms, +Colorama does nothing. + +This has the upshot of providing a simple cross-platform API for printing +colored terminal text from Python, and has the happy side-effect that existing +applications or libraries which use ANSI sequences to produce colored output on +Linux or Macs can now also work on Windows, simply by calling +``colorama.just_fix_windows_console()`` (since v0.4.6) or ``colorama.init()`` +(all versions, but may have other side-effects – see below). + +An alternative approach is to install ``ansi.sys`` on Windows machines, which +provides the same behaviour for all applications running in terminals. Colorama +is intended for situations where that isn't easy (e.g., maybe your app doesn't +have an installer.) + +Demo scripts in the source code repository print some colored text using +ANSI sequences. Compare their output under Gnome-terminal's built in ANSI +handling, versus on Windows Command-Prompt using Colorama: + +.. image:: https://github.com/tartley/colorama/raw/master/screenshots/ubuntu-demo.png + :width: 661 + :height: 357 + :alt: ANSI sequences on Ubuntu under gnome-terminal. + +.. image:: https://github.com/tartley/colorama/raw/master/screenshots/windows-demo.png + :width: 668 + :height: 325 + :alt: Same ANSI sequences on Windows, using Colorama. + +These screenshots show that, on Windows, Colorama does not support ANSI 'dim +text'; it looks the same as 'normal text'. + +Usage +----- + +Initialisation +.............. + +If the only thing you want from Colorama is to get ANSI escapes to work on +Windows, then run: + +.. code-block:: python + + from colorama import just_fix_windows_console + just_fix_windows_console() + +If you're on a recent version of Windows 10 or better, and your stdout/stderr +are pointing to a Windows console, then this will flip the magic configuration +switch to enable Windows' built-in ANSI support. + +If you're on an older version of Windows, and your stdout/stderr are pointing to +a Windows console, then this will wrap ``sys.stdout`` and/or ``sys.stderr`` in a +magic file object that intercepts ANSI escape sequences and issues the +appropriate Win32 calls to emulate them. + +In all other circumstances, it does nothing whatsoever. Basically the idea is +that this makes Windows act like Unix with respect to ANSI escape handling. + +It's safe to call this function multiple times. It's safe to call this function +on non-Windows platforms, but it won't do anything. It's safe to call this +function when one or both of your stdout/stderr are redirected to a file – it +won't do anything to those streams. + +Alternatively, you can use the older interface with more features (but also more +potential footguns): + +.. code-block:: python + + from colorama import init + init() + +This does the same thing as ``just_fix_windows_console``, except for the +following differences: + +- It's not safe to call ``init`` multiple times; you can end up with multiple + layers of wrapping and broken ANSI support. + +- Colorama will apply a heuristic to guess whether stdout/stderr support ANSI, + and if it thinks they don't, then it will wrap ``sys.stdout`` and + ``sys.stderr`` in a magic file object that strips out ANSI escape sequences + before printing them. This happens on all platforms, and can be convenient if + you want to write your code to emit ANSI escape sequences unconditionally, and + let Colorama decide whether they should actually be output. But note that + Colorama's heuristic is not particularly clever. + +- ``init`` also accepts explicit keyword args to enable/disable various + functionality – see below. + +To stop using Colorama before your program exits, simply call ``deinit()``. +This will restore ``stdout`` and ``stderr`` to their original values, so that +Colorama is disabled. To resume using Colorama again, call ``reinit()``; it is +cheaper than calling ``init()`` again (but does the same thing). + +Most users should depend on ``colorama >= 0.4.6``, and use +``just_fix_windows_console``. The old ``init`` interface will be supported +indefinitely for backwards compatibility, but we don't plan to fix any issues +with it, also for backwards compatibility. + +Colored Output +.............. + +Cross-platform printing of colored text can then be done using Colorama's +constant shorthand for ANSI escape sequences. These are deliberately +rudimentary, see below. + +.. code-block:: python + + from colorama import Fore, Back, Style + print(Fore.RED + 'some red text') + print(Back.GREEN + 'and with a green background') + print(Style.DIM + 'and in dim text') + print(Style.RESET_ALL) + print('back to normal now') + +...or simply by manually printing ANSI sequences from your own code: + +.. code-block:: python + + print('\033[31m' + 'some red text') + print('\033[39m') # and reset to default color + +...or, Colorama can be used in conjunction with existing ANSI libraries +such as the venerable `Termcolor `_ +the fabulous `Blessings `_, +or the incredible `_Rich `_. + +If you wish Colorama's Fore, Back and Style constants were more capable, +then consider using one of the above highly capable libraries to generate +colors, etc, and use Colorama just for its primary purpose: to convert +those ANSI sequences to also work on Windows: + +SIMILARLY, do not send PRs adding the generation of new ANSI types to Colorama. +We are only interested in converting ANSI codes to win32 API calls, not +shortcuts like the above to generate ANSI characters. + +.. code-block:: python + + from colorama import just_fix_windows_console + from termcolor import colored + + # use Colorama to make Termcolor work on Windows too + just_fix_windows_console() + + # then use Termcolor for all colored text output + print(colored('Hello, World!', 'green', 'on_red')) + +Available formatting constants are:: + + Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Style: DIM, NORMAL, BRIGHT, RESET_ALL + +``Style.RESET_ALL`` resets foreground, background, and brightness. Colorama will +perform this reset automatically on program exit. + +These are fairly well supported, but not part of the standard:: + + Fore: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX + Back: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX + +Cursor Positioning +.................. + +ANSI codes to reposition the cursor are supported. See ``demos/demo06.py`` for +an example of how to generate them. + +Init Keyword Args +................. + +``init()`` accepts some ``**kwargs`` to override default behaviour. + +init(autoreset=False): + If you find yourself repeatedly sending reset sequences to turn off color + changes at the end of every print, then ``init(autoreset=True)`` will + automate that: + + .. code-block:: python + + from colorama import init + init(autoreset=True) + print(Fore.RED + 'some red text') + print('automatically back to default color again') + +init(strip=None): + Pass ``True`` or ``False`` to override whether ANSI codes should be + stripped from the output. The default behaviour is to strip if on Windows + or if output is redirected (not a tty). + +init(convert=None): + Pass ``True`` or ``False`` to override whether to convert ANSI codes in the + output into win32 calls. The default behaviour is to convert if on Windows + and output is to a tty (terminal). + +init(wrap=True): + On Windows, Colorama works by replacing ``sys.stdout`` and ``sys.stderr`` + with proxy objects, which override the ``.write()`` method to do their work. + If this wrapping causes you problems, then this can be disabled by passing + ``init(wrap=False)``. The default behaviour is to wrap if ``autoreset`` or + ``strip`` or ``convert`` are True. + + When wrapping is disabled, colored printing on non-Windows platforms will + continue to work as normal. To do cross-platform colored output, you can + use Colorama's ``AnsiToWin32`` proxy directly: + + .. code-block:: python + + import sys + from colorama import init, AnsiToWin32 + init(wrap=False) + stream = AnsiToWin32(sys.stderr).stream + + # Python 2 + print >>stream, Fore.BLUE + 'blue text on stderr' + + # Python 3 + print(Fore.BLUE + 'blue text on stderr', file=stream) + +Recognised ANSI Sequences +......................... + +ANSI sequences generally take the form:: + + ESC [ ; ... + +Where ```` is an integer, and ```` is a single letter. Zero or +more params are passed to a ````. If no params are passed, it is +generally synonymous with passing a single zero. No spaces exist in the +sequence; they have been inserted here simply to read more easily. + +The only ANSI sequences that Colorama converts into win32 calls are:: + + ESC [ 0 m # reset all (colors and brightness) + ESC [ 1 m # bright + ESC [ 2 m # dim (looks same as normal brightness) + ESC [ 22 m # normal brightness + + # FOREGROUND: + ESC [ 30 m # black + ESC [ 31 m # red + ESC [ 32 m # green + ESC [ 33 m # yellow + ESC [ 34 m # blue + ESC [ 35 m # magenta + ESC [ 36 m # cyan + ESC [ 37 m # white + ESC [ 39 m # reset + + # BACKGROUND + ESC [ 40 m # black + ESC [ 41 m # red + ESC [ 42 m # green + ESC [ 43 m # yellow + ESC [ 44 m # blue + ESC [ 45 m # magenta + ESC [ 46 m # cyan + ESC [ 47 m # white + ESC [ 49 m # reset + + # cursor positioning + ESC [ y;x H # position cursor at x across, y down + ESC [ y;x f # position cursor at x across, y down + ESC [ n A # move cursor n lines up + ESC [ n B # move cursor n lines down + ESC [ n C # move cursor n characters forward + ESC [ n D # move cursor n characters backward + + # clear the screen + ESC [ mode J # clear the screen + + # clear the line + ESC [ mode K # clear the line + +Multiple numeric params to the ``'m'`` command can be combined into a single +sequence:: + + ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background + +All other ANSI sequences of the form ``ESC [ ; ... `` +are silently stripped from the output on Windows. + +Any other form of ANSI sequence, such as single-character codes or alternative +initial characters, are not recognised or stripped. It would be cool to add +them though. Let me know if it would be useful for you, via the Issues on +GitHub. + +Status & Known Problems +----------------------- + +I've personally only tested it on Windows XP (CMD, Console2), Ubuntu +(gnome-terminal, xterm), and OS X. + +Some valid ANSI sequences aren't recognised. + +If you're hacking on the code, see `README-hacking.md`_. ESPECIALLY, see the +explanation there of why we do not want PRs that allow Colorama to generate new +types of ANSI codes. + +See outstanding issues and wish-list: +https://github.com/tartley/colorama/issues + +If anything doesn't work for you, or doesn't do what you expected or hoped for, +I'd love to hear about it on that issues list, would be delighted by patches, +and would be happy to grant commit access to anyone who submits a working patch +or two. + +.. _README-hacking.md: README-hacking.md + +License +------- + +Copyright Jonathan Hartley & Arnon Yaari, 2013-2020. BSD 3-Clause license; see +LICENSE file. + +Professional support +-------------------- + +.. |tideliftlogo| image:: https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png + :alt: Tidelift + :target: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - Professional support for colorama is available as part of the + `Tidelift Subscription`_. + Tidelift gives software development teams a single source for purchasing + and maintaining their software, with professional grade assurances from + the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme + +Thanks +------ + +See the CHANGELOG for more thanks! + +* Marc Schlaich (schlamar) for a ``setup.py`` fix for Python2.5. +* Marc Abramowitz, reported & fixed a crash on exit with closed ``stdout``, + providing a solution to issue #7's setuptools/distutils debate, + and other fixes. +* User 'eryksun', for guidance on correctly instantiating ``ctypes.windll``. +* Matthew McCormick for politely pointing out a longstanding crash on non-Win. +* Ben Hoyt, for a magnificent fix under 64-bit Windows. +* Jesse at Empty Square for submitting a fix for examples in the README. +* User 'jamessp', an observant documentation fix for cursor positioning. +* User 'vaal1239', Dave Mckee & Lackner Kristof for a tiny but much-needed Win7 + fix. +* Julien Stuyck, for wisely suggesting Python3 compatible updates to README. +* Daniel Griffith for multiple fabulous patches. +* Oscar Lesta for a valuable fix to stop ANSI chars being sent to non-tty + output. +* Roger Binns, for many suggestions, valuable feedback, & bug reports. +* Tim Golden for thought and much appreciated feedback on the initial idea. +* User 'Zearin' for updates to the README file. +* John Szakmeister for adding support for light colors +* Charles Merriam for adding documentation to demos +* Jurko for a fix on 64-bit Windows CPython2.5 w/o ctypes +* Florian Bruhin for a fix when stdout or stderr are None +* Thomas Weininger for fixing ValueError on Windows +* Remi Rampin for better Github integration and fixes to the README file +* Simeon Visser for closing a file handle using 'with' and updating classifiers + to include Python 3.3 and 3.4 +* Andy Neff for fixing RESET of LIGHT_EX colors. +* Jonathan Hartley for the initial idea and implementation. diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD b/venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD new file mode 100644 index 0000000..8c5f12d --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD @@ -0,0 +1,31 @@ +colorama-0.4.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +colorama-0.4.6.dist-info/METADATA,sha256=e67SnrUMOym9sz_4TjF3vxvAV4T3aF7NyqRHHH3YEMw,17158 +colorama-0.4.6.dist-info/RECORD,, +colorama-0.4.6.dist-info/WHEEL,sha256=cdcF4Fbd0FPtw2EMIOwH-3rSOTUdTCeOSXRMD1iLUb8,105 +colorama-0.4.6.dist-info/licenses/LICENSE.txt,sha256=ysNcAmhuXQSlpxQL-zs25zrtSWZW6JEQLkKIhteTAxg,1491 +colorama/__init__.py,sha256=wePQA4U20tKgYARySLEC047ucNX-g8pRLpYBuiHlLb8,266 +colorama/__pycache__/__init__.cpython-310.pyc,, +colorama/__pycache__/ansi.cpython-310.pyc,, +colorama/__pycache__/ansitowin32.cpython-310.pyc,, +colorama/__pycache__/initialise.cpython-310.pyc,, +colorama/__pycache__/win32.cpython-310.pyc,, +colorama/__pycache__/winterm.cpython-310.pyc,, +colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522 +colorama/ansitowin32.py,sha256=vPNYa3OZbxjbuFyaVo0Tmhmy1FZ1lKMWCnT7odXpItk,11128 +colorama/initialise.py,sha256=-hIny86ClXo39ixh5iSCfUIa2f_h_bgKRDW7gqs-KLU,3325 +colorama/tests/__init__.py,sha256=MkgPAEzGQd-Rq0w0PZXSX2LadRWhUECcisJY8lSrm4Q,75 +colorama/tests/__pycache__/__init__.cpython-310.pyc,, +colorama/tests/__pycache__/ansi_test.cpython-310.pyc,, +colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc,, +colorama/tests/__pycache__/initialise_test.cpython-310.pyc,, +colorama/tests/__pycache__/isatty_test.cpython-310.pyc,, +colorama/tests/__pycache__/utils.cpython-310.pyc,, +colorama/tests/__pycache__/winterm_test.cpython-310.pyc,, +colorama/tests/ansi_test.py,sha256=FeViDrUINIZcr505PAxvU4AjXz1asEiALs9GXMhwRaE,2839 +colorama/tests/ansitowin32_test.py,sha256=RN7AIhMJ5EqDsYaCjVo-o4u8JzDD4ukJbmevWKS70rY,10678 +colorama/tests/initialise_test.py,sha256=BbPy-XfyHwJ6zKozuQOvNvQZzsx9vdb_0bYXn7hsBTc,6741 +colorama/tests/isatty_test.py,sha256=Pg26LRpv0yQDB5Ac-sxgVXG7hsA1NYvapFgApZfYzZg,1866 +colorama/tests/utils.py,sha256=1IIRylG39z5-dzq09R_ngufxyPZxgldNbrxKxUGwGKE,1079 +colorama/tests/winterm_test.py,sha256=qoWFPEjym5gm2RuMwpf3pOis3a5r_PJZFCzK254JL8A,3709 +colorama/win32.py,sha256=YQOKwMTwtGBbsY4dL5HYTvwTeP9wIQra5MvPNddpxZs,6181 +colorama/winterm.py,sha256=XCQFDHjPi6AHYNdZwy0tA02H-Jh48Jp-HvCjeLeLp3U,7134 diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL b/venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL new file mode 100644 index 0000000..d79189f --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.11.1 +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any diff --git a/venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt b/venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..3105888 --- /dev/null +++ b/venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2010 Jonathan Hartley +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holders, nor those of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/colorama/__init__.py b/venv/Lib/site-packages/colorama/__init__.py new file mode 100644 index 0000000..383101c --- /dev/null +++ b/venv/Lib/site-packages/colorama/__init__.py @@ -0,0 +1,7 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.4.6' + diff --git a/venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a48ecbd76f999ec56aab54337b1f9e8e49a1a481 GIT binary patch literal 446 zcmYk2OG^VW5P&zk+3j|>_1uGhU=LPl#e;}g3!)$*R1m^KSa+kO=24RE%YWow%GHxM zuX-{=5gf=jnMY*GVXk=Vl7{WH+39|*g*KI} zXv;RO8_busrO$jr*IYGvZE3BQ)q)k+;VW&J!f%|u70e~mrLo!+$mJ7d`GbDs zYCL+2kA}y?le=P%WYCOPf?H|KSt`(*4f!Tvpz|VAYTHYHyXLW1K$xW^0qEbGsTrssOZ~0T{E3t!xpT2rww*33| eA*C=QrNhP6rDsw%OTo@zXBLLpM+4MDY4{7ft$4@) literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-310.pyc b/venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2061bc9fb83c68f8f1867a3eac9293c0be22d264 GIT binary patch literal 2972 zcma)8TW=Fb6yDig-_}<`NT8*yd&g~Y+DdIDsA!zTgcRdMaiWwVbh~(lWWlk+>;_5{ zB&6^|=p%n=U;DIgm3S&rrJip#PC_mcYt835b9T<0Ip;ewJ17<%gPy9ARc$#{7QJ`LC)(}NedSQr? z%YD6J<%Ke+Hrk!JYIogJUN@Ag+8qcQ3ByRDw#eU9=!ZS2-A#ZvWkk6^hOX9>NmO|NdNEcww(KxU3R)eNUlnNhio!v8J!ZYo z?MY9$L03uHAE6hM1xDY^_DyYD>3N9qA-A2xaL@0{bNgn;V3yij4?LZu7GP0nP=y#& zAyg++6_&6lxptImP!^`i|KP11W4CE)I?z8-C!`?nvKiy<=rgv%UNXV{V!`>)+%b08 zuKCOiE%ZWr$J}MR7Be2%V#pr1mWnLltH=pLPldq~KU|M8Ycll0P(>Na0y;X`H7l|M z*}EUveQ!fXsmES#>sVy7uIu;x&~@JtLuf0&BkA_@8or8-E?P&!YOw3zyUvf6Lh)rj5DYuBv3%={U#r$=LGEzEZ=leqx7 zzR0#qhk{6|c8w_{uV(7Mq@PtE(~$ZE@G0Ocz&C(8;9I~CfP;h`(EKw}py{H@8dOv! zekD_3v`g)+2c(>&_IiO}pY9;KNfft;+&100DI#>5XGMnRM3y@u$MeGB1(D}PQQ##} zx56~n?N6b_>zcmg~rs3b}e(180o1+LX(c(FscDK=L zo#-^ zBz4+!JhWMEqgsfol%go5H^|e|`dB>4#VGr8auHrbB?l-dVnqtf$XZ#;p OSY<4oPO~)Kvi}2>!9pJZ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-310.pyc b/venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96d0a6a78627f4d69e9ac09d5af221c39b70b108 GIT binary patch literal 8484 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/__pycache__/win32.cpython-310.pyc b/venv/Lib/site-packages/colorama/__pycache__/win32.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ae41cccb43a6f5217d73926ea78eec5d8777ff2 GIT binary patch literal 4439 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 F{{W6r6vqGn literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/ansi.py b/venv/Lib/site-packages/colorama/ansi.py new file mode 100644 index 0000000..11ec695 --- /dev/null +++ b/venv/Lib/site-packages/colorama/ansi.py @@ -0,0 +1,102 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' +OSC = '\033]' +BEL = '\a' + + +def code_to_chars(code): + return CSI + str(code) + 'm' + +def set_title(title): + return OSC + '2;' + title + BEL + +def clear_screen(mode=2): + return CSI + str(mode) + 'J' + +def clear_line(mode=2): + return CSI + str(mode) + 'K' + + +class AnsiCodes(object): + def __init__(self): + # the subclasses declare class attributes which are numbers. + # Upon instantiation we define instance attributes, which are the same + # as the class attributes but wrapped with the ANSI escape sequence + for name in dir(self): + if not name.startswith('_'): + value = getattr(self, name) + setattr(self, name, code_to_chars(value)) + + +class AnsiCursor(object): + def UP(self, n=1): + return CSI + str(n) + 'A' + def DOWN(self, n=1): + return CSI + str(n) + 'B' + def FORWARD(self, n=1): + return CSI + str(n) + 'C' + def BACK(self, n=1): + return CSI + str(n) + 'D' + def POS(self, x=1, y=1): + return CSI + str(y) + ';' + str(x) + 'H' + + +class AnsiFore(AnsiCodes): + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 90 + LIGHTRED_EX = 91 + LIGHTGREEN_EX = 92 + LIGHTYELLOW_EX = 93 + LIGHTBLUE_EX = 94 + LIGHTMAGENTA_EX = 95 + LIGHTCYAN_EX = 96 + LIGHTWHITE_EX = 97 + + +class AnsiBack(AnsiCodes): + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 100 + LIGHTRED_EX = 101 + LIGHTGREEN_EX = 102 + LIGHTYELLOW_EX = 103 + LIGHTBLUE_EX = 104 + LIGHTMAGENTA_EX = 105 + LIGHTCYAN_EX = 106 + LIGHTWHITE_EX = 107 + + +class AnsiStyle(AnsiCodes): + BRIGHT = 1 + DIM = 2 + NORMAL = 22 + RESET_ALL = 0 + +Fore = AnsiFore() +Back = AnsiBack() +Style = AnsiStyle() +Cursor = AnsiCursor() diff --git a/venv/Lib/site-packages/colorama/ansitowin32.py b/venv/Lib/site-packages/colorama/ansitowin32.py new file mode 100644 index 0000000..abf209e --- /dev/null +++ b/venv/Lib/site-packages/colorama/ansitowin32.py @@ -0,0 +1,277 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import re +import sys +import os + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL +from .winterm import enable_vt_processing, WinTerm, WinColor, WinStyle +from .win32 import windll, winapi_test + + +winterm = None +if windll is not None: + winterm = WinTerm() + + +class StreamWrapper(object): + ''' + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()', which is delegated to our + Converter instance. + ''' + def __init__(self, wrapped, converter): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + self.__convertor = converter + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def __enter__(self, *args, **kwargs): + # special method lookup bypasses __getattr__/__getattribute__, see + # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit + # thus, contextlib magic methods are not proxied via __getattr__ + return self.__wrapped.__enter__(*args, **kwargs) + + def __exit__(self, *args, **kwargs): + return self.__wrapped.__exit__(*args, **kwargs) + + def __setstate__(self, state): + self.__dict__ = state + + def __getstate__(self): + return self.__dict__ + + def write(self, text): + self.__convertor.write(text) + + def isatty(self): + stream = self.__wrapped + if 'PYCHARM_HOSTED' in os.environ: + if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__): + return True + try: + stream_isatty = stream.isatty + except AttributeError: + return False + else: + return stream_isatty() + + @property + def closed(self): + stream = self.__wrapped + try: + return stream.closed + # AttributeError in the case that the stream doesn't support being closed + # ValueError for the case that the stream has already been detached when atexit runs + except (AttributeError, ValueError): + return True + + +class AnsiToWin32(object): + ''' + Implements a 'write()' method which, on Windows, will strip ANSI character + sequences from the text, and if outputting to a tty, will convert them into + win32 function calls. + ''' + ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer + ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command + + def __init__(self, wrapped, convert=None, strip=None, autoreset=False): + # The wrapped stream (normally sys.stdout or sys.stderr) + self.wrapped = wrapped + + # should we reset colors to defaults after every .write() + self.autoreset = autoreset + + # create the proxy wrapping our output stream + self.stream = StreamWrapper(wrapped, self) + + on_windows = os.name == 'nt' + # We test if the WinAPI works, because even if we are on Windows + # we may be using a terminal that doesn't support the WinAPI + # (e.g. Cygwin Terminal). In this case it's up to the terminal + # to support the ANSI codes. + conversion_supported = on_windows and winapi_test() + try: + fd = wrapped.fileno() + except Exception: + fd = -1 + system_has_native_ansi = not on_windows or enable_vt_processing(fd) + have_tty = not self.stream.closed and self.stream.isatty() + need_conversion = conversion_supported and not system_has_native_ansi + + # should we strip ANSI sequences from our output? + if strip is None: + strip = need_conversion or not have_tty + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = need_conversion and have_tty + self.convert = convert + + # dict of ansi codes to win32 functions and parameters + self.win32_calls = self.get_win32_calls() + + # are we wrapping stderr? + self.on_stderr = self.wrapped is sys.stderr + + def should_wrap(self): + ''' + True if this class is actually needed. If false, then the output + stream will not be affected, nor will win32 calls be issued, so + wrapping stdout is not actually required. This will generally be + False on non-Windows platforms, unless optional functionality like + autoreset has been requested using kwargs to init() + ''' + return self.convert or self.strip or self.autoreset + + def get_win32_calls(self): + if self.convert and winterm: + return { + AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), + AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), + AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), + AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), + AnsiFore.RED: (winterm.fore, WinColor.RED), + AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), + AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), + AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), + AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), + AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), + AnsiFore.WHITE: (winterm.fore, WinColor.GREY), + AnsiFore.RESET: (winterm.fore, ), + AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), + AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), + AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), + AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), + AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), + AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), + AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), + AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), + AnsiBack.BLACK: (winterm.back, WinColor.BLACK), + AnsiBack.RED: (winterm.back, WinColor.RED), + AnsiBack.GREEN: (winterm.back, WinColor.GREEN), + AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), + AnsiBack.BLUE: (winterm.back, WinColor.BLUE), + AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), + AnsiBack.CYAN: (winterm.back, WinColor.CYAN), + AnsiBack.WHITE: (winterm.back, WinColor.GREY), + AnsiBack.RESET: (winterm.back, ), + AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), + AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), + AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), + AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), + AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), + AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), + AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), + AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), + } + return dict() + + def write(self, text): + if self.strip or self.convert: + self.write_and_convert(text) + else: + self.wrapped.write(text) + self.wrapped.flush() + if self.autoreset: + self.reset_all() + + + def reset_all(self): + if self.convert: + self.call_win32('m', (0,)) + elif not self.strip and not self.stream.closed: + self.wrapped.write(Style.RESET_ALL) + + + def write_and_convert(self, text): + ''' + Write the given text to our wrapped stream, stripping any ANSI + sequences from the text, and optionally converting them into win32 + calls. + ''' + cursor = 0 + text = self.convert_osc(text) + for match in self.ANSI_CSI_RE.finditer(text): + start, end = match.span() + self.write_plain_text(text, cursor, start) + self.convert_ansi(*match.groups()) + cursor = end + self.write_plain_text(text, cursor, len(text)) + + + def write_plain_text(self, text, start, end): + if start < end: + self.wrapped.write(text[start:end]) + self.wrapped.flush() + + + def convert_ansi(self, paramstring, command): + if self.convert: + params = self.extract_params(command, paramstring) + self.call_win32(command, params) + + + def extract_params(self, command, paramstring): + if command in 'Hf': + params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) + while len(params) < 2: + # defaults: + params = params + (1,) + else: + params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) + if len(params) == 0: + # defaults: + if command in 'JKm': + params = (0,) + elif command in 'ABCD': + params = (1,) + + return params + + + def call_win32(self, command, params): + if command == 'm': + for param in params: + if param in self.win32_calls: + func_args = self.win32_calls[param] + func = func_args[0] + args = func_args[1:] + kwargs = dict(on_stderr=self.on_stderr) + func(*args, **kwargs) + elif command in 'J': + winterm.erase_screen(params[0], on_stderr=self.on_stderr) + elif command in 'K': + winterm.erase_line(params[0], on_stderr=self.on_stderr) + elif command in 'Hf': # cursor position - absolute + winterm.set_cursor_position(params, on_stderr=self.on_stderr) + elif command in 'ABCD': # cursor position - relative + n = params[0] + # A - up, B - down, C - forward, D - back + x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] + winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) + + + def convert_osc(self, text): + for match in self.ANSI_OSC_RE.finditer(text): + start, end = match.span() + text = text[:start] + text[end:] + paramstring, command = match.groups() + if command == BEL: + if paramstring.count(";") == 1: + params = paramstring.split(";") + # 0 - change title and icon (we will only change title) + # 1 - change icon (we don't support this) + # 2 - change title + if params[0] in '02': + winterm.set_title(params[1]) + return text + + + def flush(self): + self.wrapped.flush() diff --git a/venv/Lib/site-packages/colorama/initialise.py b/venv/Lib/site-packages/colorama/initialise.py new file mode 100644 index 0000000..d5fd4b7 --- /dev/null +++ b/venv/Lib/site-packages/colorama/initialise.py @@ -0,0 +1,121 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import atexit +import contextlib +import sys + +from .ansitowin32 import AnsiToWin32 + + +def _wipe_internal_state_for_tests(): + global orig_stdout, orig_stderr + orig_stdout = None + orig_stderr = None + + global wrapped_stdout, wrapped_stderr + wrapped_stdout = None + wrapped_stderr = None + + global atexit_done + atexit_done = False + + global fixed_windows_console + fixed_windows_console = False + + try: + # no-op if it wasn't registered + atexit.unregister(reset_all) + except AttributeError: + # python 2: no atexit.unregister. Oh well, we did our best. + pass + + +def reset_all(): + if AnsiToWin32 is not None: # Issue #74: objects might become None at exit + AnsiToWin32(orig_stdout).reset_all() + + +def init(autoreset=False, convert=None, strip=None, wrap=True): + + if not wrap and any([autoreset, convert, strip]): + raise ValueError('wrap=False conflicts with any other arg=True') + + global wrapped_stdout, wrapped_stderr + global orig_stdout, orig_stderr + + orig_stdout = sys.stdout + orig_stderr = sys.stderr + + if sys.stdout is None: + wrapped_stdout = None + else: + sys.stdout = wrapped_stdout = \ + wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + if sys.stderr is None: + wrapped_stderr = None + else: + sys.stderr = wrapped_stderr = \ + wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + + global atexit_done + if not atexit_done: + atexit.register(reset_all) + atexit_done = True + + +def deinit(): + if orig_stdout is not None: + sys.stdout = orig_stdout + if orig_stderr is not None: + sys.stderr = orig_stderr + + +def just_fix_windows_console(): + global fixed_windows_console + + if sys.platform != "win32": + return + if fixed_windows_console: + return + if wrapped_stdout is not None or wrapped_stderr is not None: + # Someone already ran init() and it did stuff, so we won't second-guess them + return + + # On newer versions of Windows, AnsiToWin32.__init__ will implicitly enable the + # native ANSI support in the console as a side-effect. We only need to actually + # replace sys.stdout/stderr if we're in the old-style conversion mode. + new_stdout = AnsiToWin32(sys.stdout, convert=None, strip=None, autoreset=False) + if new_stdout.convert: + sys.stdout = new_stdout + new_stderr = AnsiToWin32(sys.stderr, convert=None, strip=None, autoreset=False) + if new_stderr.convert: + sys.stderr = new_stderr + + fixed_windows_console = True + +@contextlib.contextmanager +def colorama_text(*args, **kwargs): + init(*args, **kwargs) + try: + yield + finally: + deinit() + + +def reinit(): + if wrapped_stdout is not None: + sys.stdout = wrapped_stdout + if wrapped_stderr is not None: + sys.stderr = wrapped_stderr + + +def wrap_stream(stream, convert, strip, autoreset, wrap): + if wrap: + wrapper = AnsiToWin32(stream, + convert=convert, strip=strip, autoreset=autoreset) + if wrapper.should_wrap(): + stream = wrapper.stream + return stream + + +# Use this for initial setup as well, to reduce code duplication +_wipe_internal_state_for_tests() diff --git a/venv/Lib/site-packages/colorama/tests/__init__.py b/venv/Lib/site-packages/colorama/tests/__init__.py new file mode 100644 index 0000000..8c5661e --- /dev/null +++ b/venv/Lib/site-packages/colorama/tests/__init__.py @@ -0,0 +1 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b75589540b12ed7612724a379e5e42d00bfc1c3 GIT binary patch literal 166 zcmd1j<>g`kg2x|zW_p8Y5C<7B0yz#qT+9L_QW%06G#UL?G8BP?5yUS~N2{2CqWrAX z2BVvedk?7@y3fnBvTmRNaEaRBq+ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/ansi_test.cpython-310.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/ansi_test.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e8c56dfe4b7accb13d6f5cd3c9018878d2e6d1f9 GIT binary patch literal 2258 zcma)7TXWk)6xQly`5MP5DUfo%3TVLel!I4+Yo6RLPdqk2Whu4S3krk!MX zOMeJF<}d9lPkm$P6THAVy9rj4bOw3+o&9!o&hEMBY+9|BGGDsx(#{_Z* z{@DQ5g0_H1~o@!Vg*)W2X;~j3UbaGIY}`n5-rw-rJxL0396eK zwQ1q0MhmfiVg$8#hB}XQwi+8J8fcMS`FD%YU_Y(n>cZ*98DEdGSeQvP7(dft-Zg}| z7WE$q^V^ig!rJ9Wqu3>)+!|+tZn{4hudejbK>k6+JMhmufLIF%)dHOosz22N16=@V zQj6LkEn1)sNShXE31oqmcQv;n9Jv7;s87Ji`GaE&)H?jLj{$zse$_bP`p`Hb-26=^ z(;4f0hHAUoDe1U&P>ZrGX1tSf|L4PKBusuZiG`6JWx~!lO%FM9z^EiKW2Z!zSv>mb zRAaL+{r7jRFMB_*bQt$})@yC`(_|8j2kE%?I37RlwFeJ+*?`A4ClIgbVVw2)=_qAU z5-oA?BU_3fWFZb0Cr84{V!k)2p&#f_!wR4{r<>DpJF|N|{It)1Y9qQZwG|@wb z<>gwUtQ-kbHh9P?FvqF@avEGx)3`00K|sG0H1I8l#}V5|pNy{olLxa&i5$=Vcd+yI zOJ_UJ%JV-#Q)OicIiF@^Xiz=JOlstqMRU0u+SJOe6{wwK4lU$Zkvch6qQx95(^8IA zXgSBKw31^rTFtQ;TFbFIoyoCTTF^x@{=G1E{0(7k?)ZL3 z*!TT*``$i$&h0&46t-KNey7_K=KB3sM_BuJx4OQtcKltxE2{0S&AVM`BJ_V0Wl6yl z6165xBCW|aWg2Q}`I!beUdR>hMLRDoVRjvipC!~&8Szv`Je3hoWyDh%@l-}Ul@U)R)PscD3kS=$ zKwnBQASGCk5==-5Hlze2Qi2sJF|q7bI0&;4&}sHM!UDn#gf|f0M0g9~ZG?9aFgWZz zg!d6XK==^hBLr-l%tg4W1IU15$Dam#3E%w`;6gZGeCws*%oaiQ|91rWHJP1bbKyWm zdj$lL<;1mAKnh?lFgCWf1G{r?XS>x7?6n=N*ee~H!wQnu*3=^(EM)khV7Wk<+<2i( zF1*}v9pQxGI7;F$6s0gsQhGSTaU~4#8c{234(zdcgijDYL%4}B?Xhx%jg}PwusLT4 zj^^l&?O4uyWnQk}es~3kp<92EAE9K(Xc->~G>X16QrdNoq+oWzd-;{{?&%YO%|H1$O literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f9a0d2be8f1b7a4e9872ec88493ac544b0886531 GIT binary patch literal 11569 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/tests/__pycache__/initialise_test.cpython-310.pyc b/venv/Lib/site-packages/colorama/tests/__pycache__/initialise_test.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07de04236dde36606a4fdc32487bb3306308ae60 GIT binary patch literal 6866 zcmbtZ&2QYs73Yv#irQVRSF&tNj$Q zxf#w{_Es(e$UxIWkxNfK#Yg=kJ@?E@4+RS7R=~Y9E#m&(aJkw~1!z~`BWK=xyzlpB z6i-bRHT?eekADSsPU20C-k*)eWjx8B0l3Cls5$i3L)Oq8y;!*oohwTtLA!a~Dx ztj2^h!L++tG;M0W{kX<7`cwGH1N^62#I`kV@jVWiUQE@7bBhC@@ zO>jG`Hm05F#*8z=G+)~~>dXQjbB=$g@kw5Ms_~+)?;6etv`f55Iq< z-9&oQcQ<3cZDCxzIl~T z(|r1K$~|LeI{2e}Hp4qp{1`uu6)XG%pTqkIpXVp>uJV`oDZHoo%ltInGg#$}+Hql* z@mKg_XZ}(2Q_rjXHR^#*OZ;`T&+<1^`xt-owpOd9_H_uD^wd6)JWiGp$>Ww>2$F>; zmJ8m3l#lBq!~93zq^m1i%KUC?du$atta?e}3%MH0OpSbxrDo!Xn|qo# zh8f@7T>fDFrii!vx=hxWuh-*7(~E*QT7T$A57$2qHrA6s`tLTq`UCHtpRCv8Fcx0J zJEv57jw~YZ!XWXLj+dH`(?a6QJIz@vo)B?mqg~0_T2iR-JRHY(sgVIoCUw`4CSZYU zw>2>h)Se|uc%`LS1ovQFB%RcNq*ANX=wf^!R43lzDCQ_;GeDc9w)8!5HQtWq(RxnS zW-*HuS$n!mYd&I#+yX|E$diu&NaT`j>C)irnZfe5DGOZhv@JQIM%b`f6$b1q3}a?CJigGGmyH(fypLvd`f8JDDKm%b z2@FEK1oFix0*Hp1I8ESXfV7y6S`#feX^;dF9674{f&%17X-;JmIwnR7B~^ebtFj8y znfaok>&$-9KHe2>--PiJehsU9mxPt%jMet3e#w!`^hl4eJ<{vP)~wXdB{Nnmqc|5c zAn+{lYbt(cS$lqf#{<`EznnYIQqXmqCE>#Fxyk0oJ1n4yb8oqX%HihB9Wwfhq1V{p-lex`4mqc(^%#7P{iZgO5I^!ZLf?HT z-KL1^NE<6p$QRIG$~dre%n6NRrcr^hs#GSb+;WSpbIqOPyvZ^G~{~# zeJqk;3N?Fxw^wsYuf~27DGqN$*BY%5xtezvJ&(y5qeh3(cZL`}WcrXl=gL&H)}W9= zGfsj>;s-?JsPKGx6p^cOu81FZ=~F=)ig@Et`VMA}B~1l$*csCES985~CXFrg8ElGd z4&Md(9$|N92JKE|tePb%#3NaqQ_e#S$nkr^j0(J?q%b!uh3lI%`4aI@#cUD$myHTr z{CE46LnM&R8q53h%UG)8V<`P{i7Mh{ zN#SlF@AoKS>0%hyy)e0ik1XM!>hD+vJ=gk|41%NDT0lb^xJ2;@2r)bPD6N8u$(dhjl@3b>BUlTt8 zEnRM^hD6$!LlAU6U|7oA@)7{Xdu@yq{7f|1ku9HXZ{up+M zj7h#E(S8}*=;^~8)E(WmCi}Ev_RJ{uVM>FT5&H6m^i}TF*FO37fYSEK!uILpva(O{ zLRlm9*4l3$M_B!d~B(v}~p&Rpsi7)HL;y>F9 zqxEyJQ2W(U4uZ1cQ6N#~-h9}- zk}YK6Y!M5j7TKxK9b7I@T}|fcbWS}h2uXfNLQa`5w>m@(it3aSl_ESJLUtQIzJX&< zM8s0^we&Kxk$|hL{d&J_#>`x;9Zzl7rOOZ3O^Yt5<1LibZd!I-avoJ|S>}z^KT71PEF)p_FZF z6}`x8(=OPyWtLeP2RW-iZ}gy#poQYF%&Z9%A=4EmXnq|}-;Y?ij7Ke+-{WMt%rUsZ zP28H8et{RVyBnN_)!hy5NGx7N>MEu4zrc0VO7N(YVY`tzkwa;d&JBv&IeaM75OlmB zS$c@|y=iC(9v` zQm|HB(h*C%Nnk*o{n`8r8bL>h!?w&5bsv+YJgQs`JjwgmS87{~vj>KllX~YIW}pw3 z9i50rEnIC-i`+B2qI-hec2)cAH^0}U%?rQNHknd3u0FoHuj=Yaa_}8>vZK|W=(Nfc zqkoSb6`{Y>#777ZYM&i-nG#K)U8EUV`@2@?pN&@U?j?2iG?IoLqAJhP#*U(Z`0S15 zk&P4RAA4i@=ch>yH5*4BRb2FGlkAlql@G~7DabuR<+h`_kn@hVg*z~=<88XejwTDP zrA82rVFXnUG=f4RjbIotq4q1_;7#d$O(l(*EhtYQHij`0u%P|YvhZa~MD9Z>#8PWB zK$edOP9+GI>VAl_LywVVH)xyQ1D`J8z63I%tV8j@RuR!cCZR9fr(HYD%o8~hdr&{- zd(`xmHSsR?=#w9L0zDKM)dkF#c}mELr9<@|b4rmXgNMFLw<;N;Tj~A@BQoFk|LDx! zr&d-HOl@3jtMiMXL?K=$pd{IU(0PfNrQ5oFL}xmbV|{DmB7@XsA-tP(Tc_u(3dZF)!dBU- zzv^F@FWMJI?y`pMm&VetTAL6ziB>vUh+6~{(<+%M8GJ^417S>QX$^>NJSyYb8my{7 z2VOz_luYS=8LnG9spRh1EmY=2h9JKZ6u4A2b*6H}66O6&?#d@h*Bi}PNOcl%%ucy* zaQ&w4Je}e}!Jm~HN~`KFy3mqAm}I51I_i&7Go?mFaVCWke5UrrtQfdN2AwPd*ld>R Z`Yci`-Y?3=EObDQRW+)M45vV8%k+LGHwUXtoP3zQ`Wo9-Z zs~{n5-t!ld$NU9;ieGuk8&aM?KsaX{cfDp4TXSa4T+jK=H^)vI4U53@`yYP>-&LSS zrTA!|Jb}U)2#wMe)1$gmC2bEx~*@+GcLQ@3XYF7W1H!YzOhf&(0`L(bGzQzxT>zQED;2AG$r? zd9k0)=Iw+9FN4?(v9p?O*6BP?>sfOni9ze(PWgjv70Od6ya_^(1At_t9McoBMNTPL z%XH{ZjexsC?6+xZaB1Z!VJ#T=YklpD?j}oi{GQ<5wP(E~>br4}#NAy#-tDdjFSOc{_u75w^KLH*6Xr(lssIf5YPPL|%7gxXY6jeK9XP0p79lQ8%5Ff_gEH~p*b^xH zYY;xMsYh(Zqn-j0Q89{8YZyJK^~*>ZS)A;OSM%!7Z+Z={33b(**&^*$YCV%qVss{8 z!m~^$2-Eon2th{V7kWUBsZb8+h@MbN1VsC&MFGHK?>4}x!8<~30i>T2etQpQS!7U$N~aqn7Y%{OicMqwN-&0+6A+pfe(jKL~fRFfycAQ&aL zd=F+1=gOvJ6DF)dLgq;Gbx;uLqgy$uLfz3av@?`J*E4+z&dbwrHlz#eZP;9Tl&XB6 zBda~1Fympg!=|v`FA*9_OdX0!c`;{vRsntXJ}-k z5)>T14|I~mI+~HLt3u0ZR&+FO0i70$ZWc$8T4f#PKPt{2(@&T5%dkPS#sIN7RPnMG zNH?8WShiOjwD8#VdzVvci5eyh zpfJQ$=%cZ2fL_VyvZ1cu(=GtqjAPS zICr#T{e)bCZU`j9GxiELCrMX$JRqQPSkFWvsUrvXzR>(94f+b;_!4{_K3|R^4 z5~{aR;N6*u>|z<&;?IKj2`uBdl}YF1`z~GidwA~}Kqr=Ow5`;1oY;+g$4M>6i4t!R zV!Q4*KMmY48)3K5x-D})N5Z@1-PN$QdYn{>-Oyg1rh!DGZ3WT)SxOEd~FQ#WwEDnUMRUEncxbCjg zo;ib!{hq(W|I)tZsc(gQN?zKTmCwGICM#w%AG>n9={(K;uhmGLtkxuCnE4SL(^?TiXf zbk4~cjXaQj(Y101A>qbva6^ysn0?vg+0F=X`Ida-H2DCF$>g zS`E{>sAWyo1nu)tt zG0x5S2y6lE0kET``E6$@O6$L>Z?l61|0%4&b@CHrsJ+)VEE*DKqrL)mN;Em3$8-Y! z82bMlx;mkaJb+gALuOd5jDuARnfwga7^jx`{tej1trgDc;m!z!ow&@UF7>JW9Mtz< zaP*@QX!%)#i~3O8epPTa$4Xs%im<)E!QX-Jj8js14~Sa%6P_&mcK^@2Ht%g>dvm)EGnXz4_Q7bG&j^{*0ugHuCMtsILguB&b z27X#zI+Q;&Vzy~;n-{`zRPicN)vHD|uf~i8gFC$V(%?m5?pj`5OmO$9Dd&Z?i*?wl zF3nP(_GBzKt;}r+mCgl9WOfvE;;gKmb~f(Ep-`%6X7*Cjewr0Fg0%glZ_*UZc+iRG zk}#3FwVG~)A}i8ZOUS5=WsiO~b#75ne-Q}7W8CmeVR5$0xXG=T#xC=0Q4mhIxaV?v zpYg)3nIC~9rGci)OIou$rdj3{Evbx2s=THp)iFt(PiRSPuzG`^(f#_MKgrMP{sf;| zHJZ~|WpFq!wSBIQqX$uNb+Z$vbY#>wuD3zX@WY`|=BTLK1gWuWyf8W2F?}jKtZVvK zYSWmtW9$`ntgge&eUll3UXfczdZ2}5+@Q@Amiq~<*DnGb0KBYOk_D==%H~X(%Fd%+ zDpYoHL8Nm@tddZywxtm9Y;S#C$i;X)$*NMMJsJDYgRmzg+&HTQN(q_HKkEfyR`Ayo zDX4lBkog^7!N>=8W-Ae{d!>k+f;hQx-E+~zpJq0=I56ZzTI|22nIG5g$)qdVsal&^ zY$wr15O0kb}OS2wmY7mdZZlTOg$ ziek-hOozG5VRiaVvh69_4cxDV2}9+~qII?#QatN*q)?YghbNn=&1nAcgVjKzw$z{0 z(3WCkDdy)0t1%yTvUk{CVV~j$)=0@=p>sHnk%7b|iTSpKq%Jq7jPr)pDRs+luZr1+ zIw{F>O>K2+Sz^=u20^o;?IowEEvEtJ0A~rZ(qobO0a-&yxP|9DoWFDD?gOv5G_x?j z+?tUkJvzIxxNy65V9E=a`4IqC%i&{cWmRxU{sYS@D5%Q>L^{##um*F@Nh-EBfc@IF z(VAlJ>+K824KZBg^!V7Ol~0ckNOmm0z)eDm62hWJcgNZ*?Nd%8p36FdhB1R)F^`#n z%;g2JKIj#?74D#~xh*dDo3w-e8~XZSSt7P1JgwEN>bR2NU7i6<0`hoK#C1E5uVcpW zO5|>^L>T6FaoW@rT%O15lc-*S{!AENBLxM8p?|xYaPoDtZo=26*f_<*$zzT^OhK=1 z(Tcfar;O+%=NvdS+p{V0utDNsUF%31q9jFWNs|hq4(x#wBGa^*#oP(MRQ0H;_(E6Fq{>1CVkI-N{Bvvul_pP(Dd=Z((A7(EXOc14HvGE2lGb?rnzd z#;f|zBuzfXRP~Ul%cnCnTwuA`$Xwr#gGl&(R`&fU;k^*;s_)~WkV_VCovqj2xKl*>&@cvaMI NrmTkL_R7qD^FQa|xu5_5 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/colorama/tests/ansi_test.py b/venv/Lib/site-packages/colorama/tests/ansi_test.py new file mode 100644 index 0000000..0a20c80 --- /dev/null +++ b/venv/Lib/site-packages/colorama/tests/ansi_test.py @@ -0,0 +1,76 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansi import Back, Fore, Style +from ..ansitowin32 import AnsiToWin32 + +stdout_orig = sys.stdout +stderr_orig = sys.stderr + + +class AnsiTest(TestCase): + + def setUp(self): + # sanity check: stdout should be a file or StringIO object. + # It will only be AnsiToWin32 if init() has previously wrapped it + self.assertNotEqual(type(sys.stdout), AnsiToWin32) + self.assertNotEqual(type(sys.stderr), AnsiToWin32) + + def tearDown(self): + sys.stdout = stdout_orig + sys.stderr = stderr_orig + + + def testForeAttributes(self): + self.assertEqual(Fore.BLACK, '\033[30m') + self.assertEqual(Fore.RED, '\033[31m') + self.assertEqual(Fore.GREEN, '\033[32m') + self.assertEqual(Fore.YELLOW, '\033[33m') + self.assertEqual(Fore.BLUE, '\033[34m') + self.assertEqual(Fore.MAGENTA, '\033[35m') + self.assertEqual(Fore.CYAN, '\033[36m') + self.assertEqual(Fore.WHITE, '\033[37m') + self.assertEqual(Fore.RESET, '\033[39m') + + # Check the light, extended versions. + self.assertEqual(Fore.LIGHTBLACK_EX, '\033[90m') + self.assertEqual(Fore.LIGHTRED_EX, '\033[91m') + self.assertEqual(Fore.LIGHTGREEN_EX, '\033[92m') + self.assertEqual(Fore.LIGHTYELLOW_EX, '\033[93m') + self.assertEqual(Fore.LIGHTBLUE_EX, '\033[94m') + self.assertEqual(Fore.LIGHTMAGENTA_EX, '\033[95m') + self.assertEqual(Fore.LIGHTCYAN_EX, '\033[96m') + self.assertEqual(Fore.LIGHTWHITE_EX, '\033[97m') + + + def testBackAttributes(self): + self.assertEqual(Back.BLACK, '\033[40m') + self.assertEqual(Back.RED, '\033[41m') + self.assertEqual(Back.GREEN, '\033[42m') + self.assertEqual(Back.YELLOW, '\033[43m') + self.assertEqual(Back.BLUE, '\033[44m') + self.assertEqual(Back.MAGENTA, '\033[45m') + self.assertEqual(Back.CYAN, '\033[46m') + self.assertEqual(Back.WHITE, '\033[47m') + self.assertEqual(Back.RESET, '\033[49m') + + # Check the light, extended versions. + self.assertEqual(Back.LIGHTBLACK_EX, '\033[100m') + self.assertEqual(Back.LIGHTRED_EX, '\033[101m') + self.assertEqual(Back.LIGHTGREEN_EX, '\033[102m') + self.assertEqual(Back.LIGHTYELLOW_EX, '\033[103m') + self.assertEqual(Back.LIGHTBLUE_EX, '\033[104m') + self.assertEqual(Back.LIGHTMAGENTA_EX, '\033[105m') + self.assertEqual(Back.LIGHTCYAN_EX, '\033[106m') + self.assertEqual(Back.LIGHTWHITE_EX, '\033[107m') + + + def testStyleAttributes(self): + self.assertEqual(Style.DIM, '\033[2m') + self.assertEqual(Style.NORMAL, '\033[22m') + self.assertEqual(Style.BRIGHT, '\033[1m') + + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/colorama/tests/ansitowin32_test.py b/venv/Lib/site-packages/colorama/tests/ansitowin32_test.py new file mode 100644 index 0000000..91ca551 --- /dev/null +++ b/venv/Lib/site-packages/colorama/tests/ansitowin32_test.py @@ -0,0 +1,294 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from io import StringIO, TextIOWrapper +from unittest import TestCase, main +try: + from contextlib import ExitStack +except ImportError: + # python 2 + from contextlib2 import ExitStack + +try: + from unittest.mock import MagicMock, Mock, patch +except ImportError: + from mock import MagicMock, Mock, patch + +from ..ansitowin32 import AnsiToWin32, StreamWrapper +from ..win32 import ENABLE_VIRTUAL_TERMINAL_PROCESSING +from .utils import osname + + +class StreamWrapperTest(TestCase): + + def testIsAProxy(self): + mockStream = Mock() + wrapper = StreamWrapper(mockStream, None) + self.assertTrue( wrapper.random_attr is mockStream.random_attr ) + + def testDelegatesWrite(self): + mockStream = Mock() + mockConverter = Mock() + wrapper = StreamWrapper(mockStream, mockConverter) + wrapper.write('hello') + self.assertTrue(mockConverter.write.call_args, (('hello',), {})) + + def testDelegatesContext(self): + mockConverter = Mock() + s = StringIO() + with StreamWrapper(s, mockConverter) as fp: + fp.write(u'hello') + self.assertTrue(s.closed) + + def testProxyNoContextManager(self): + mockStream = MagicMock() + mockStream.__enter__.side_effect = AttributeError() + mockConverter = Mock() + with self.assertRaises(AttributeError) as excinfo: + with StreamWrapper(mockStream, mockConverter) as wrapper: + wrapper.write('hello') + + def test_closed_shouldnt_raise_on_closed_stream(self): + stream = StringIO() + stream.close() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + + def test_closed_shouldnt_raise_on_detached_stream(self): + stream = TextIOWrapper(StringIO()) + stream.detach() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + +class AnsiToWin32Test(TestCase): + + def testInit(self): + mockStdout = Mock() + auto = Mock() + stream = AnsiToWin32(mockStdout, autoreset=auto) + self.assertEqual(stream.wrapped, mockStdout) + self.assertEqual(stream.autoreset, auto) + + @patch('colorama.ansitowin32.winterm', None) + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + def testStripIsTrueOnWindows(self): + with osname('nt'): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + self.assertTrue(stream.strip) + + def testStripIsFalseOffWindows(self): + with osname('posix'): + mockStdout = Mock(closed=False) + stream = AnsiToWin32(mockStdout) + self.assertFalse(stream.strip) + + def testWriteStripsAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = True + + stream.write('abc') + + self.assertFalse(stream.wrapped.write.called) + self.assertEqual(stream.write_and_convert.call_args, (('abc',), {})) + + def testWriteDoesNotStripAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = False + stream.convert = False + + stream.write('abc') + + self.assertFalse(stream.write_and_convert.called) + self.assertEqual(stream.wrapped.write.call_args, (('abc',), {})) + + def assert_autoresets(self, convert, autoreset=True): + stream = AnsiToWin32(Mock()) + stream.convert = convert + stream.reset_all = Mock() + stream.autoreset = autoreset + stream.winterm = Mock() + + stream.write('abc') + + self.assertEqual(stream.reset_all.called, autoreset) + + def testWriteAutoresets(self): + self.assert_autoresets(convert=True) + self.assert_autoresets(convert=False) + self.assert_autoresets(convert=True, autoreset=False) + self.assert_autoresets(convert=False, autoreset=False) + + def testWriteAndConvertWritesPlainText(self): + stream = AnsiToWin32(Mock()) + stream.write_and_convert( 'abc' ) + self.assertEqual( stream.wrapped.write.call_args, (('abc',), {}) ) + + def testWriteAndConvertStripsAllValidAnsi(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + data = [ + 'abc\033[mdef', + 'abc\033[0mdef', + 'abc\033[2mdef', + 'abc\033[02mdef', + 'abc\033[002mdef', + 'abc\033[40mdef', + 'abc\033[040mdef', + 'abc\033[0;1mdef', + 'abc\033[40;50mdef', + 'abc\033[50;30;40mdef', + 'abc\033[Adef', + 'abc\033[0Gdef', + 'abc\033[1;20;128Hdef', + ] + for datum in data: + stream.wrapped.write.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( + [args[0] for args in stream.wrapped.write.call_args_list], + [ ('abc',), ('def',) ] + ) + + def testWriteAndConvertSkipsEmptySnippets(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + stream.write_and_convert( '\033[40m\033[41m' ) + self.assertFalse( stream.wrapped.write.called ) + + def testWriteAndConvertCallsWin32WithParamsAndCommand(self): + stream = AnsiToWin32(Mock()) + stream.convert = True + stream.call_win32 = Mock() + stream.extract_params = Mock(return_value='params') + data = { + 'abc\033[adef': ('a', 'params'), + 'abc\033[;;bdef': ('b', 'params'), + 'abc\033[0cdef': ('c', 'params'), + 'abc\033[;;0;;Gdef': ('G', 'params'), + 'abc\033[1;20;128Hdef': ('H', 'params'), + } + for datum, expected in data.items(): + stream.call_win32.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( stream.call_win32.call_args[0], expected ) + + def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + converter = AnsiToWin32(stream) + stream.close() + + converter.reset_all() + + def test_wrap_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + stream.close() + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(stream) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def test_wrap_shouldnt_raise_on_missing_closed_attr(self): + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(object()) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def testExtractParams(self): + stream = AnsiToWin32(Mock()) + data = { + '': (0,), + ';;': (0,), + '2': (2,), + ';;002;;': (2,), + '0;1': (0, 1), + ';;003;;456;;': (3, 456), + '11;22;33;44;55': (11, 22, 33, 44, 55), + } + for datum, expected in data.items(): + self.assertEqual(stream.extract_params('m', datum), expected) + + def testCallWin32UsesLookup(self): + listener = Mock() + stream = AnsiToWin32(listener) + stream.win32_calls = { + 1: (lambda *_, **__: listener(11),), + 2: (lambda *_, **__: listener(22),), + 3: (lambda *_, **__: listener(33),), + } + stream.call_win32('m', (3, 1, 99, 2)) + self.assertEqual( + [a[0][0] for a in listener.call_args_list], + [33, 11, 22] ) + + def test_osc_codes(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout, convert=True) + with patch('colorama.ansitowin32.winterm') as winterm: + data = [ + '\033]0\x07', # missing arguments + '\033]0;foo\x08', # wrong OSC command + '\033]0;colorama_test_title\x07', # should work + '\033]1;colorama_test_title\x07', # wrong set command + '\033]2;colorama_test_title\x07', # should work + '\033]' + ';' * 64 + '\x08', # see issue #247 + ] + for code in data: + stream.write(code) + self.assertEqual(winterm.set_title.call_count, 2) + + def test_native_windows_ansi(self): + with ExitStack() as stack: + def p(a, b): + stack.enter_context(patch(a, b, create=True)) + # Pretend to be on Windows + p("colorama.ansitowin32.os.name", "nt") + p("colorama.ansitowin32.winapi_test", lambda: True) + p("colorama.win32.winapi_test", lambda: True) + p("colorama.winterm.win32.windll", "non-None") + p("colorama.winterm.get_osfhandle", lambda _: 1234) + + # Pretend that our mock stream has native ANSI support + p( + "colorama.winterm.win32.GetConsoleMode", + lambda _: ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = True + stdout.fileno.return_value = 1 + + # Our fake console says it has native vt support, so AnsiToWin32 should + # enable that support and do nothing else. + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertFalse(stream.strip) + self.assertFalse(stream.convert) + self.assertFalse(stream.should_wrap()) + + # Now let's pretend we're on an old Windows console, that doesn't have + # native ANSI support. + p("colorama.winterm.win32.GetConsoleMode", lambda _: 0) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertTrue(stream.strip) + self.assertTrue(stream.convert) + self.assertTrue(stream.should_wrap()) + + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/colorama/tests/initialise_test.py b/venv/Lib/site-packages/colorama/tests/initialise_test.py new file mode 100644 index 0000000..89f9b07 --- /dev/null +++ b/venv/Lib/site-packages/colorama/tests/initialise_test.py @@ -0,0 +1,189 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import patch, Mock +except ImportError: + from mock import patch, Mock + +from ..ansitowin32 import StreamWrapper +from ..initialise import init, just_fix_windows_console, _wipe_internal_state_for_tests +from .utils import osname, replace_by + +orig_stdout = sys.stdout +orig_stderr = sys.stderr + + +class InitTest(TestCase): + + @skipUnless(sys.stdout.isatty(), "sys.stdout is not a tty") + def setUp(self): + # sanity check + self.assertNotWrapped() + + def tearDown(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def assertWrapped(self): + self.assertIsNot(sys.stdout, orig_stdout, 'stdout should be wrapped') + self.assertIsNot(sys.stderr, orig_stderr, 'stderr should be wrapped') + self.assertTrue(isinstance(sys.stdout, StreamWrapper), + 'bad stdout wrapper') + self.assertTrue(isinstance(sys.stderr, StreamWrapper), + 'bad stderr wrapper') + + def assertNotWrapped(self): + self.assertIs(sys.stdout, orig_stdout, 'stdout should not be wrapped') + self.assertIs(sys.stderr, orig_stderr, 'stderr should not be wrapped') + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + @patch('colorama.ansitowin32.enable_vt_processing', lambda *_: False) + def testInitWrapsOnWindows(self, _): + with osname("nt"): + init() + self.assertWrapped() + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: False) + def testInitDoesntWrapOnEmulatedWindows(self, _): + with osname("nt"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapOnNonWindows(self): + with osname("posix"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapIfNone(self): + with replace_by(None): + init() + # We can't use assertNotWrapped here because replace_by(None) + # changes stdout/stderr already. + self.assertIsNone(sys.stdout) + self.assertIsNone(sys.stderr) + + def testInitAutoresetOnWrapsOnAllPlatforms(self): + with osname("posix"): + init(autoreset=True) + self.assertWrapped() + + def testInitWrapOffDoesntWrapOnWindows(self): + with osname("nt"): + init(wrap=False) + self.assertNotWrapped() + + def testInitWrapOffIncompatibleWithAutoresetOn(self): + self.assertRaises(ValueError, lambda: init(autoreset=True, wrap=False)) + + @patch('colorama.win32.SetConsoleTextAttribute') + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetPassedOn(self, mockATW32, _): + with osname("nt"): + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 2) + self.assertEqual(mockATW32.call_args_list[1][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[0][1]['autoreset'], True) + + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetChangeable(self, mockATW32): + with osname("nt"): + init() + + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 4) + self.assertEqual(mockATW32.call_args_list[2][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[3][1]['autoreset'], True) + + init() + self.assertEqual(len(mockATW32.call_args_list), 6) + self.assertEqual( + mockATW32.call_args_list[4][1]['autoreset'], False) + self.assertEqual( + mockATW32.call_args_list[5][1]['autoreset'], False) + + + @patch('colorama.initialise.atexit.register') + def testAtexitRegisteredOnlyOnce(self, mockRegister): + init() + self.assertTrue(mockRegister.called) + mockRegister.reset_mock() + init() + self.assertFalse(mockRegister.called) + + +class JustFixWindowsConsoleTest(TestCase): + def _reset(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def tearDown(self): + self._reset() + + @patch("colorama.ansitowin32.winapi_test", lambda: True) + def testJustFixWindowsConsole(self): + if sys.platform != "win32": + # just_fix_windows_console should be a no-op + just_fix_windows_console() + self.assertIs(sys.stdout, orig_stdout) + self.assertIs(sys.stderr, orig_stderr) + else: + def fake_std(): + # Emulate stdout=not a tty, stderr=tty + # to check that we handle both cases correctly + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = False + stdout.fileno.return_value = 1 + sys.stdout = stdout + + stderr = Mock() + stderr.closed = False + stderr.isatty.return_value = True + stderr.fileno.return_value = 2 + sys.stderr = stderr + + for native_ansi in [False, True]: + with patch( + 'colorama.ansitowin32.enable_vt_processing', + lambda *_: native_ansi + ): + self._reset() + fake_std() + + # Regular single-call test + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + if native_ansi: + self.assertIs(sys.stderr, prev_stderr) + else: + self.assertIsNot(sys.stderr, prev_stderr) + + # second call without resetting is always a no-op + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + self.assertIs(sys.stderr, prev_stderr) + + self._reset() + fake_std() + + # If init() runs first, just_fix_windows_console should be a no-op + init() + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(prev_stdout, sys.stdout) + self.assertIs(prev_stderr, sys.stderr) + + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/colorama/tests/isatty_test.py b/venv/Lib/site-packages/colorama/tests/isatty_test.py new file mode 100644 index 0000000..0f84e4b --- /dev/null +++ b/venv/Lib/site-packages/colorama/tests/isatty_test.py @@ -0,0 +1,57 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansitowin32 import StreamWrapper, AnsiToWin32 +from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY + + +def is_a_tty(stream): + return StreamWrapper(stream, None).isatty() + +class IsattyTest(TestCase): + + def test_TTY(self): + tty = StreamTTY() + self.assertTrue(is_a_tty(tty)) + with pycharm(): + self.assertTrue(is_a_tty(tty)) + + def test_nonTTY(self): + non_tty = StreamNonTTY() + self.assertFalse(is_a_tty(non_tty)) + with pycharm(): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharm(self): + with pycharm(): + self.assertTrue(is_a_tty(sys.stderr)) + self.assertTrue(is_a_tty(sys.stdout)) + + def test_withPycharmTTYOverride(self): + tty = StreamTTY() + with pycharm(), replace_by(tty): + self.assertTrue(is_a_tty(tty)) + + def test_withPycharmNonTTYOverride(self): + non_tty = StreamNonTTY() + with pycharm(), replace_by(non_tty): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharmNoneOverride(self): + with pycharm(): + with replace_by(None), replace_original_by(None): + self.assertFalse(is_a_tty(None)) + self.assertFalse(is_a_tty(StreamNonTTY())) + self.assertTrue(is_a_tty(StreamTTY())) + + def test_withPycharmStreamWrapped(self): + with pycharm(): + self.assertTrue(AnsiToWin32(StreamTTY()).stream.isatty()) + self.assertFalse(AnsiToWin32(StreamNonTTY()).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stdout).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stderr).stream.isatty()) + + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/colorama/tests/utils.py b/venv/Lib/site-packages/colorama/tests/utils.py new file mode 100644 index 0000000..472fafb --- /dev/null +++ b/venv/Lib/site-packages/colorama/tests/utils.py @@ -0,0 +1,49 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from contextlib import contextmanager +from io import StringIO +import sys +import os + + +class StreamTTY(StringIO): + def isatty(self): + return True + +class StreamNonTTY(StringIO): + def isatty(self): + return False + +@contextmanager +def osname(name): + orig = os.name + os.name = name + yield + os.name = orig + +@contextmanager +def replace_by(stream): + orig_stdout = sys.stdout + orig_stderr = sys.stderr + sys.stdout = stream + sys.stderr = stream + yield + sys.stdout = orig_stdout + sys.stderr = orig_stderr + +@contextmanager +def replace_original_by(stream): + orig_stdout = sys.__stdout__ + orig_stderr = sys.__stderr__ + sys.__stdout__ = stream + sys.__stderr__ = stream + yield + sys.__stdout__ = orig_stdout + sys.__stderr__ = orig_stderr + +@contextmanager +def pycharm(): + os.environ["PYCHARM_HOSTED"] = "1" + non_tty = StreamNonTTY() + with replace_by(non_tty), replace_original_by(non_tty): + yield + del os.environ["PYCHARM_HOSTED"] diff --git a/venv/Lib/site-packages/colorama/tests/winterm_test.py b/venv/Lib/site-packages/colorama/tests/winterm_test.py new file mode 100644 index 0000000..d0955f9 --- /dev/null +++ b/venv/Lib/site-packages/colorama/tests/winterm_test.py @@ -0,0 +1,131 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import Mock, patch +except ImportError: + from mock import Mock, patch + +from ..winterm import WinColor, WinStyle, WinTerm + + +class WinTermTest(TestCase): + + @patch('colorama.winterm.win32') + def testInit(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 7 + 6 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + self.assertEqual(term._fore, 7) + self.assertEqual(term._back, 6) + self.assertEqual(term._style, 8) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testGetAttrs(self): + term = WinTerm() + + term._fore = 0 + term._back = 0 + term._style = 0 + self.assertEqual(term.get_attrs(), 0) + + term._fore = WinColor.YELLOW + self.assertEqual(term.get_attrs(), WinColor.YELLOW) + + term._back = WinColor.MAGENTA + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16) + + term._style = WinStyle.BRIGHT + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16 + WinStyle.BRIGHT) + + @patch('colorama.winterm.win32') + def testResetAll(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 1 + 2 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + + term.set_console = Mock() + term._fore = -1 + term._back = -1 + term._style = -1 + + term.reset_all() + + self.assertEqual(term._fore, 1) + self.assertEqual(term._back, 2) + self.assertEqual(term._style, 8) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testFore(self): + term = WinTerm() + term.set_console = Mock() + term._fore = 0 + + term.fore(5) + + self.assertEqual(term._fore, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testBack(self): + term = WinTerm() + term.set_console = Mock() + term._back = 0 + + term.back(5) + + self.assertEqual(term._back, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testStyle(self): + term = WinTerm() + term.set_console = Mock() + term._style = 0 + + term.style(22) + + self.assertEqual(term._style, 22) + self.assertEqual(term.set_console.called, True) + + @patch('colorama.winterm.win32') + def testSetConsole(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console() + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDOUT, term.get_attrs()), {}) + ) + + @patch('colorama.winterm.win32') + def testSetConsoleOnStderr(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console(on_stderr=True) + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDERR, term.get_attrs()), {}) + ) + + +if __name__ == '__main__': + main() diff --git a/venv/Lib/site-packages/colorama/win32.py b/venv/Lib/site-packages/colorama/win32.py new file mode 100644 index 0000000..841b0e2 --- /dev/null +++ b/venv/Lib/site-packages/colorama/win32.py @@ -0,0 +1,180 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + +try: + import ctypes + from ctypes import LibraryLoader + windll = LibraryLoader(ctypes.WinDLL) + from ctypes import wintypes +except (AttributeError, ImportError): + windll = None + SetConsoleTextAttribute = lambda *_: None + winapi_test = lambda *_: None +else: + from ctypes import byref, Structure, c_char, POINTER + + COORD = wintypes._COORD + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", wintypes.WORD), + ("srWindow", wintypes.SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + def __str__(self): + return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( + self.dwSize.Y, self.dwSize.X + , self.dwCursorPosition.Y, self.dwCursorPosition.X + , self.wAttributes + , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right + , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X + ) + + _GetStdHandle = windll.kernel32.GetStdHandle + _GetStdHandle.argtypes = [ + wintypes.DWORD, + ] + _GetStdHandle.restype = wintypes.HANDLE + + _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo + _GetConsoleScreenBufferInfo.argtypes = [ + wintypes.HANDLE, + POINTER(CONSOLE_SCREEN_BUFFER_INFO), + ] + _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + + _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute + _SetConsoleTextAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + ] + _SetConsoleTextAttribute.restype = wintypes.BOOL + + _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition + _SetConsoleCursorPosition.argtypes = [ + wintypes.HANDLE, + COORD, + ] + _SetConsoleCursorPosition.restype = wintypes.BOOL + + _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA + _FillConsoleOutputCharacterA.argtypes = [ + wintypes.HANDLE, + c_char, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputCharacterA.restype = wintypes.BOOL + + _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute + _FillConsoleOutputAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputAttribute.restype = wintypes.BOOL + + _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW + _SetConsoleTitleW.argtypes = [ + wintypes.LPCWSTR + ] + _SetConsoleTitleW.restype = wintypes.BOOL + + _GetConsoleMode = windll.kernel32.GetConsoleMode + _GetConsoleMode.argtypes = [ + wintypes.HANDLE, + POINTER(wintypes.DWORD) + ] + _GetConsoleMode.restype = wintypes.BOOL + + _SetConsoleMode = windll.kernel32.SetConsoleMode + _SetConsoleMode.argtypes = [ + wintypes.HANDLE, + wintypes.DWORD + ] + _SetConsoleMode.restype = wintypes.BOOL + + def _winapi_test(handle): + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return bool(success) + + def winapi_test(): + return any(_winapi_test(h) for h in + (_GetStdHandle(STDOUT), _GetStdHandle(STDERR))) + + def GetConsoleScreenBufferInfo(stream_id=STDOUT): + handle = _GetStdHandle(stream_id) + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = _GetStdHandle(stream_id) + return _SetConsoleTextAttribute(handle, attrs) + + def SetConsoleCursorPosition(stream_id, position, adjust=True): + position = COORD(*position) + # If the position is out of range, do nothing. + if position.Y <= 0 or position.X <= 0: + return + # Adjust for Windows' SetConsoleCursorPosition: + # 1. being 0-based, while ANSI is 1-based. + # 2. expecting (x,y), while ANSI uses (y,x). + adjusted_position = COORD(position.Y - 1, position.X - 1) + if adjust: + # Adjust for viewport's scroll position + sr = GetConsoleScreenBufferInfo(STDOUT).srWindow + adjusted_position.Y += sr.Top + adjusted_position.X += sr.Left + # Resume normal processing + handle = _GetStdHandle(stream_id) + return _SetConsoleCursorPosition(handle, adjusted_position) + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = _GetStdHandle(stream_id) + char = c_char(char.encode()) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + success = _FillConsoleOutputCharacterA( + handle, char, length, start, byref(num_written)) + return num_written.value + + def FillConsoleOutputAttribute(stream_id, attr, length, start): + ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' + handle = _GetStdHandle(stream_id) + attribute = wintypes.WORD(attr) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + return _FillConsoleOutputAttribute( + handle, attribute, length, start, byref(num_written)) + + def SetConsoleTitle(title): + return _SetConsoleTitleW(title) + + def GetConsoleMode(handle): + mode = wintypes.DWORD() + success = _GetConsoleMode(handle, byref(mode)) + if not success: + raise ctypes.WinError() + return mode.value + + def SetConsoleMode(handle, mode): + success = _SetConsoleMode(handle, mode) + if not success: + raise ctypes.WinError() diff --git a/venv/Lib/site-packages/colorama/winterm.py b/venv/Lib/site-packages/colorama/winterm.py new file mode 100644 index 0000000..aad867e --- /dev/null +++ b/venv/Lib/site-packages/colorama/winterm.py @@ -0,0 +1,195 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +try: + from msvcrt import get_osfhandle +except ImportError: + def get_osfhandle(_): + raise OSError("This isn't windows!") + + +from . import win32 + +# from wincon.h +class WinColor(object): + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 + MAGENTA = 5 + YELLOW = 6 + GREY = 7 + +# from wincon.h +class WinStyle(object): + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + BRIGHT_BACKGROUND = 0x80 # dim text, bright background + +class WinTerm(object): + + def __init__(self): + self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes + self.set_attrs(self._default) + self._default_fore = self._fore + self._default_back = self._back + self._default_style = self._style + # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style. + # So that LIGHT_EX colors and BRIGHT style do not clobber each other, + # we track them separately, since LIGHT_EX is overwritten by Fore/Back + # and BRIGHT is overwritten by Style codes. + self._light = 0 + + def get_attrs(self): + return self._fore + self._back * 16 + (self._style | self._light) + + def set_attrs(self, value): + self._fore = value & 7 + self._back = (value >> 4) & 7 + self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + self._light = 0 + + def fore(self, fore=None, light=False, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + # Emulate LIGHT_EX with BRIGHT Style + if light: + self._light |= WinStyle.BRIGHT + else: + self._light &= ~WinStyle.BRIGHT + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, light=False, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style + if light: + self._light |= WinStyle.BRIGHT_BACKGROUND + else: + self._light &= ~WinStyle.BRIGHT_BACKGROUND + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + + def get_position(self, handle): + position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition + # Because Windows coordinates are 0-based, + # and win32.SetConsoleCursorPosition expects 1-based. + position.X += 1 + position.Y += 1 + return position + + def set_cursor_position(self, position=None, on_stderr=False): + if position is None: + # I'm not currently tracking the position, so there is no default. + # position = self.get_position() + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleCursorPosition(handle, position) + + def cursor_adjust(self, x, y, on_stderr=False): + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + position = self.get_position(handle) + adjusted_position = (position.Y + y, position.X + x) + win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) + + def erase_screen(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the screen. + # 1 should clear from the cursor to the beginning of the screen. + # 2 should clear the entire screen, and move cursor to (1,1) + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + # get the number of character cells in the current buffer + cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y + # get number of character cells before current cursor position + cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = cells_in_screen - cells_before_cursor + elif mode == 1: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_before_cursor + elif mode == 2: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_in_screen + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + if mode == 2: + # put the cursor where needed + win32.SetConsoleCursorPosition(handle, (1, 1)) + + def erase_line(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the line. + # 1 should clear from the cursor to the beginning of the line. + # 2 should clear the entire line. + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X + elif mode == 1: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwCursorPosition.X + elif mode == 2: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwSize.X + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + + def set_title(self, title): + win32.SetConsoleTitle(title) + + +def enable_vt_processing(fd): + if win32.windll is None or not win32.winapi_test(): + return False + + try: + handle = get_osfhandle(fd) + mode = win32.GetConsoleMode(handle) + win32.SetConsoleMode( + handle, + mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + + mode = win32.GetConsoleMode(handle) + if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING: + return True + # Can get TypeError in testsuite where 'fd' is a Mock() + except (OSError, TypeError): + return False diff --git a/venv/Lib/site-packages/flask-3.1.3.dist-info/INSTALLER b/venv/Lib/site-packages/flask-3.1.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/flask-3.1.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/flask-3.1.3.dist-info/METADATA b/venv/Lib/site-packages/flask-3.1.3.dist-info/METADATA new file mode 100644 index 0000000..9d8623c --- /dev/null +++ b/venv/Lib/site-packages/flask-3.1.3.dist-info/METADATA @@ -0,0 +1,91 @@ +Metadata-Version: 2.4 +Name: Flask +Version: 3.1.3 +Summary: A simple framework for building complex web applications. +Maintainer-email: Pallets +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +License-Expression: BSD-3-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Flask +Classifier: Intended Audience :: Developers +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 +Classifier: Typing :: Typed +License-File: LICENSE.txt +Requires-Dist: blinker>=1.9.0 +Requires-Dist: click>=8.1.3 +Requires-Dist: importlib-metadata>=3.6.0; python_version < '3.10' +Requires-Dist: itsdangerous>=2.2.0 +Requires-Dist: jinja2>=3.1.2 +Requires-Dist: markupsafe>=2.1.1 +Requires-Dist: werkzeug>=3.1.0 +Requires-Dist: asgiref>=3.2 ; extra == "async" +Requires-Dist: python-dotenv ; extra == "dotenv" +Project-URL: Changes, https://flask.palletsprojects.com/page/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://flask.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/flask/ +Provides-Extra: async +Provides-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/ + +## A Simple Example + +```python +# save this as app.py +from flask import Flask + +app = Flask(__name__) + +@app.route("/") +def hello(): + return "Hello, World!" +``` + +``` +$ flask run + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) +``` + +## 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 + +## Contributing + +See our [detailed contributing documentation][contrib] for many ways to +contribute, including reporting issues, requesting features, asking or answering +questions, and making PRs. + +[contrib]: https://palletsprojects.com/contributing/ + diff --git a/venv/Lib/site-packages/flask-3.1.3.dist-info/RECORD b/venv/Lib/site-packages/flask-3.1.3.dist-info/RECORD new file mode 100644 index 0000000..55bf03f --- /dev/null +++ b/venv/Lib/site-packages/flask-3.1.3.dist-info/RECORD @@ -0,0 +1,58 @@ +../../Scripts/flask.exe,sha256=XKcvacNn9jIjcO7b45I_46IF7OTCTFzm6p1b_yQJ224,108393 +flask-3.1.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +flask-3.1.3.dist-info/METADATA,sha256=qmdg7W9UVwRHTXBzPkpjp_FIHjdpc-3IlqE9AqciTHw,3167 +flask-3.1.3.dist-info/RECORD,, +flask-3.1.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask-3.1.3.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +flask-3.1.3.dist-info/entry_points.txt,sha256=bBP7hTOS5fz9zLtC7sPofBZAlMkEvBxu7KqS6l5lvc4,40 +flask-3.1.3.dist-info/licenses/LICENSE.txt,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +flask/__init__.py,sha256=mHvJN9Swtl1RDtjCqCIYyIniK_SZ_l_hqUynOzgpJ9o,2701 +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__/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=k7tW8LHRSldUi6zKsFKK7Axa_WL4zu1e2wPNthIsu7o,61719 +flask/blueprints.py,sha256=p5QE2lY18GItbdr_RKRpZ8Do17g0PvQGIgZkSUDhX2k,4541 +flask/cli.py,sha256=Pfh72-BxlvoH0QHCDOc1HvXG7Kq5Xetf3zzNz2kNSHk,37184 +flask/config.py,sha256=PiqF0DPam6HW0FH4CH1hpXTBe30NSzjPEOwrz1b6kt0,13219 +flask/ctx.py,sha256=oMe0TRsScW0qdaIqavVsk8P9qiEvAY5VHn1FAgkX8nk,15521 +flask/debughelpers.py,sha256=PGIDhStW_efRjpaa3zHIpo-htStJOR41Ip3OJWPYBwo,6080 +flask/globals.py,sha256=XdQZmStBmPIs8t93tjx6pO7Bm3gobAaONWkFcUHaGas,1713 +flask/helpers.py,sha256=rJZge7_J288J1UQv5-kNf4oEaw332PP8NTW0QRIBbXE,23517 +flask/json/__init__.py,sha256=hLNR898paqoefdeAhraa5wyJy-bmRB2k2dV4EgVy2Z8,5602 +flask/json/__pycache__/__init__.cpython-310.pyc,, +flask/json/__pycache__/provider.cpython-310.pyc,, +flask/json/__pycache__/tag.cpython-310.pyc,, +flask/json/provider.py,sha256=5imEzY5HjV2HoUVrQbJLqXCzMNpZXfD0Y1XqdLV2XBA,7672 +flask/json/tag.py,sha256=DhaNwuIOhdt2R74oOC9Y4Z8ZprxFYiRb5dUP5byyINw,9281 +flask/logging.py,sha256=8sM3WMTubi1cBb2c_lPkWpN0J8dMAqrgKRYLLi1dCVI,2377 +flask/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask/sansio/README.md,sha256=-0X1tECnilmz1cogx-YhNw5d7guK7GKrq_DEV2OzlU0,228 +flask/sansio/__pycache__/app.cpython-310.pyc,, +flask/sansio/__pycache__/blueprints.cpython-310.pyc,, +flask/sansio/__pycache__/scaffold.cpython-310.pyc,, +flask/sansio/app.py,sha256=whGURQDkN0jmhS4CHO7DQ96GGlZS0kETkKkAkoRjl4U,38106 +flask/sansio/blueprints.py,sha256=Tqe-7EkZ-tbWchm8iDoCfD848f0_3nLv6NNjeIPvHwM,24637 +flask/sansio/scaffold.py,sha256=wSASXYdFRWJmqcL0Xq-T7N-PDVUSiFGvjO9kPZg58bk,30371 +flask/sessions.py,sha256=eywRqmytTmYnX_EC78-YBGJoTc5XD_lRphQG5LbN1d0,14969 +flask/signals.py,sha256=V7lMUww7CqgJ2ThUBn1PiatZtQanOyt7OZpu2GZI-34,750 +flask/templating.py,sha256=vbIkwYAxsSEfDxQID1gKRvBQQcGWEuWYCnH0XK3EqOI,7678 +flask/testing.py,sha256=zzC7XxhBWOP9H697IV_4SG7Lg3Lzb5PWiyEP93_KQXE,10117 +flask/typing.py,sha256=L-L5t2jKgS0aOmVhioQ_ylqcgiVFnA6yxO-RLNhq-GU,3293 +flask/views.py,sha256=xzJx6oJqGElThtEghZN7ZQGMw5TDFyuRxUkecwRuAoA,6962 +flask/wrappers.py,sha256=jUkv4mVek2Iq4hwxd4RvqrIMb69Bv0PElDgWLmd5ORo,9406 diff --git a/venv/Lib/site-packages/flask-3.1.3.dist-info/REQUESTED b/venv/Lib/site-packages/flask-3.1.3.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/flask-3.1.3.dist-info/WHEEL b/venv/Lib/site-packages/flask-3.1.3.dist-info/WHEEL new file mode 100644 index 0000000..d8b9936 --- /dev/null +++ b/venv/Lib/site-packages/flask-3.1.3.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/Lib/site-packages/flask-3.1.3.dist-info/entry_points.txt b/venv/Lib/site-packages/flask-3.1.3.dist-info/entry_points.txt new file mode 100644 index 0000000..eec6733 --- /dev/null +++ b/venv/Lib/site-packages/flask-3.1.3.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +flask=flask.cli:main + diff --git a/venv/Lib/site-packages/flask-3.1.3.dist-info/licenses/LICENSE.txt b/venv/Lib/site-packages/flask-3.1.3.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/venv/Lib/site-packages/flask-3.1.3.dist-info/licenses/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/flask/__init__.py b/venv/Lib/site-packages/flask/__init__.py new file mode 100644 index 0000000..1fdc50c --- /dev/null +++ b/venv/Lib/site-packages/flask/__init__.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +import typing as t + +from . import json as json +from .app import Flask as Flask +from .blueprints import Blueprint as Blueprint +from .config import Config as Config +from .ctx import after_this_request as after_this_request +from .ctx import copy_current_request_context as copy_current_request_context +from .ctx import has_app_context as has_app_context +from .ctx import has_request_context as has_request_context +from .globals import current_app as current_app +from .globals import g as g +from .globals import request as request +from .globals import session as session +from .helpers import abort as abort +from .helpers import flash as flash +from .helpers import get_flashed_messages as get_flashed_messages +from .helpers import get_template_attribute as get_template_attribute +from .helpers import make_response as make_response +from .helpers import redirect as redirect +from .helpers import send_file as send_file +from .helpers import send_from_directory as send_from_directory +from .helpers import stream_with_context as stream_with_context +from .helpers import url_for as url_for +from .json import jsonify as jsonify +from .signals import appcontext_popped as appcontext_popped +from .signals import appcontext_pushed as appcontext_pushed +from .signals import appcontext_tearing_down as appcontext_tearing_down +from .signals import before_render_template as before_render_template +from .signals import got_request_exception as got_request_exception +from .signals import message_flashed as message_flashed +from .signals import request_finished as request_finished +from .signals import request_started as request_started +from .signals import request_tearing_down as request_tearing_down +from .signals import template_rendered as template_rendered +from .templating import render_template as render_template +from .templating import render_template_string as render_template_string +from .templating import stream_template as stream_template +from .templating import stream_template_string as stream_template_string +from .wrappers import Request as Request +from .wrappers import Response as Response + +if not t.TYPE_CHECKING: + + def __getattr__(name: str) -> t.Any: + if name == "__version__": + import importlib.metadata + import warnings + + warnings.warn( + "The '__version__' attribute is deprecated and will be removed in" + " Flask 3.2. Use feature detection or" + " 'importlib.metadata.version(\"flask\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("flask") + + raise AttributeError(name) diff --git a/venv/Lib/site-packages/flask/__main__.py b/venv/Lib/site-packages/flask/__main__.py new file mode 100644 index 0000000..4e28416 --- /dev/null +++ b/venv/Lib/site-packages/flask/__main__.py @@ -0,0 +1,3 @@ +from .cli import main + +main() diff --git a/venv/Lib/site-packages/flask/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/flask/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d05a6eb435834c93d71fb0702eedbca4f9fa5c3 GIT binary patch literal 2301 zcmb`ITW=dh6vubhm-RJHoW!{&o2%m_b=szYR4UXE3RIw~YKwqTq|w?twl|r*Ff&dX zl)eD3ec}tiBVTD>5f6Ril~$@aXJWTjc*R!!{_THeXU?2CGl|OOf&rhu{{F{*3G^pE zjDAw^aNjUYX;_Bg8J@|Fz$B)LY;lXEyp)&b=^#Tg;HA6_&jvQJHD`G)$dkNgn-_v2 zDQeE~Qcxyk&3WK4GN!ozTp<9<&FjFsWLNVB@E+OIyvc6_P14l71-wu8HP?Y}lAD^hfp3vpnj8Ffa6k?;?*QK+ zcT9t&PCk%ty<6VCck`Dt`OveT!frn`8n<6zZ8XfJ=!RjWT;)fheDun^GSeqA3ZXIc zId|nLK>kzSXR+`@1rOPyDD3!MpsL$ZOizh zj_sA6E2$gDBi1BZgAqpT;&2e?4KJEFr~Q5+TQVsjaBwqjD-t+(rUN1O0H(T3QT@O? z8bC+4%OvY()%)4&(Wlx(kBRv&! zVHdj8i_US`wHAYcG3e04jTkHoTBf_v``okVZ5HDeLqlbdi(ziS8Xso9qV`al?VYs$H|*cebCrtF+vc!Dc1t z&<$9Uf|ru4V5%>|*BRA33@_SP(!&qlff@Ka!h7%@z$M&+rQ<4ebbW5n7}dYcZ>fW2&=a(2cz(dXo} zVJFr94wdB2T@k|8$Rv%FR6iR;4by*n)s*Ch6DRgy=49+}c$yvv0rj2|yD)K*e>a0T zfagC9aTk7G-#@&2{8&UMuqt_c_^=%Xu^Yk!$7d`&JO0{l9ZO%a8!?28NB3ASYoAi6 zQAKGpzQF9mVaD`Qx(`FjDUh6r-?U|!R?0H7Z%TH~61dlmv<^q?0gD?bO>{p>3Y2#G zm}A&O%v6$97cs0pF;!xSG&m#$M^IqpkV0#C6H1ZQo#7C}Z76JfNVU~-kpmKW6#S1Y zir8RxdDL>b6eYCEC}SuU-PmJ1hB6Z4XicD0Q6^DvO=23QhBAXPi!z5Y4?=9|cS9U} z0j))pC6r~96%+?$6=e-&9c2TA6b45QmMb>V-9o9OY-4-wT)@&Xwg!4TAW8Yz_m3aY xqc0yEef99s7dp3kM@Qsi?3cS2L|&h>Pw-&K??6gM&MH|Y^Z)y1tuZV8_FqOzfM5Us literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/__main__.cpython-310.pyc b/venv/Lib/site-packages/flask/__pycache__/__main__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6af57acddc5dc2040e57e8b9ce7502b88cd8b690 GIT binary patch literal 197 zcmd1j<>g`kf~OyTX37ES#~=Px# literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/app.cpython-310.pyc b/venv/Lib/site-packages/flask/__pycache__/app.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ceccc72f06d1e7a2662cd8c8ad2e2d49dbf5d4e6 GIT binary patch literal 50865 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/blueprints.cpython-310.pyc b/venv/Lib/site-packages/flask/__pycache__/blueprints.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0352c5367dbf0fc766910a464a3eef596d3a795e GIT binary patch literal 4152 zcmcInOLH5?5#C)az$FOqp=Tu9@kEv*qatl2InDz~sSL@KqKZPPL^+kLnOY2X2Es>-KabEb~|5&0u~P37QQE=g5*ak^)*3y@NB3rG%oW_r4NdcJ-jZMT~S zeE$BAe}{ivGmO9CWd38r^vqMThE} zSrsNcf{F8#%P|`-YeR>9@U<~C*%@~BRU7jk(Aj-w%^e`M#)wxV#2d2-MJ7%5BM`p`M6kSP?5Y zK&%+zEDq;zIFG{x9KMIcB^bUzT=+pmrS$!8jN`k#?$#5L?f|mnR_|eu#$%cQQnz+_ zvb*&t9Bj!@@f%|rJg3`SZVln&^B)gN^htNTFWv*Ws8qfSL!)h(Z~oZ-a@LmZgEJLU z-STF+GYlh+NH}f+oTu*`SRktWfIrcv#y#}wO(2x91NgjTytF13vob?0VZ zE+~6q9o1hOhvud+!C0)(1O$FAIU|F8(j8_=(CrtI1;7+hzxR6&*8R`c@Ap1`^sG-X{wq0XUyvrla61z^ ztB`b9%J@pZk2{w``Yw5f%1XG;#H4Ub#BjfVo$QUmU_=y;BB}2`5b2)e0v(e{t&WA5 zz-RAe%VMBIvv>HmlBqZ zqcDIVMuB)&3D_VpSEH1XFwsl*5rvfj8qIS8U6<@~A;H{WM3ZgKx?OVHy;YnVZR?2RzQG<2^FLr7YNTu=~-hvuKmiE(62%-7~i2ts8a z)l~!b7hb~}2I-$`It+VI@zC|VA>Shyh6pNz7(=K5;&vgnz;<~O@?ENwfQX&16aefV zfs715LEK~tay!%!jWfZwLwGp>Vj)tiC($6&s;MF;WtR1%k3oELt0+vcF@wD1Y7Phg zM*z=&^~o%e9NfNk!V}j>oJmCn5Gq{F{8;j!J6&i27N&UUINsWbO26FKbvJK4&7fI` z`MMCP5I=%<77H-srpoPrLUnB85>}acAd2g_dJ2ZC8iJ*GP>-F3Mqs1mm`&RD_NL=;hrJG5y}an0*meyaRmzQ zN8m7|6-+vWCQQ!;Fnhz85(}pd+6(cXSW`+4C~@#|=s0)>T}Su}b;6#)cwH>vT)3U2 zm_JS*8-xiJ`^8OXA^EnF^RavDg!~>B3GMlQzqAT&tDgv)kJ;t!=o(-OaSMP8v7in* zPp5#BP+Q>|=wXO=yiSqhN`JfhOvesO0ue8Ya3a%x#p> zsTZ~7V>l$RbJfSiFOfPG+->Bqur~q@9of#RW3F0`)ijr_gNw7sKEd6&P&BMMmFTXu zN-AO%b?N@~3{tv@)oZ$w)oZ$))s*gOFCw3AZ1oy;w?b2wk$MM)j;*WZ-0FvOt6I0_ z#ZuR?wCBWaq#dC>p*gfWG=(mmll4>NT*Mw7yI0@Oo4yYi%AoT4dCT{|gr29^5%_IZ zx5-6f#U57Nfgx{Cd+`7|FX-}Tz9z_mMO}Y;!fWa7+A*y9$?kgfpUtbycdU!&zSEqY z3uwkLgRdxHhJcJ^h%O9yL+y_N`NGAC105&FY_Gcw@qOgNY?&`?N9lk@vS?2hMm&PA zAyQWYZ%xt!nku(STT*NERyOr|_2yy`BzY@SZW+)aR#5GOy#4GCPuBf=zh1xhn}-_@ vJZDcpenSoT1asHdj=<~Z&aVoe-ofw65}&JOIF@6!%oa2<#5Clnxq%Iy&mO$9VN|fwahGb zi(T-{fFQ;T#S!UbezOWnyb?^b)1Wxd(x&&n*NzSZJ#G;lB!9XQ`(F7K9m+2)?5 zz2&`fo@?H`bW8abInOt5UAnD&8_o;vK=WNoPT7(3V)OQ;edT>}K8W)>%6Ay4`nJV) ztM8E3+*!U8-$Tv2md46sQqne@?=SC{^I@Eim&Yad_U3z*-dldJk$N=cj<`E6rQ99$ z(ZvH7)8z^G7u?ouf#VS975}tNX*$MbbbrfTa2vS)5#(5O&mzZJ zl=?XGJmLPf+jN)kYzog>ZX3_qtP#(i#PB za6gt@cj`}EOu6f5;|o!WZ)RE2`n%kZUrE=e@E+gdzIY`g`7gSrdkMMIa`g#ZeQR>{ z5^`Nmu3mP{`jaU8llA=K$;DHjHr;P~HSK7*SR;4>Qsf_YJ@BTdc{<-?| z?!P%}ey6GS*PpwX!pt#G$=~?zzfOKLZiEBXR;%q-{YJaxP2qZIW!bIz^~$QME-%*= z?i3n+eQCb#`}*pMw!6}-Pp>W4@od|Ps;Vy4@#s{0Mb&t#pjKKHud%e;ZVjIDtI9uF_m=S%(yr5|9zA||dAZrB$*VCNZyjx&Z>V-_3FW_LgqdT_ zs&}@=p6LCsArF!Jk`32WWy4K@;~M2Odv81)4o$UD^wA5o`ZBvMxbI}O;niKIG3QiU z4w@a;>eQrjo!PoG*IsG4PFp$h_HJov+zNA*N(-G=sf2|}Wl6df=fz6p+)A|>J;_$^ zmbFUdwUpY8*2rP=fy0x}pHS_^dd>HqKYYB_URtiU(30oRqdT5|yfOQ{hcTO2uGY?0 z=j-0{=s54}-L+<;w7jNvpqM-H=Rvfja;cmlcS2h?KxJ?@v{l_-Q7tusiyFHl{@6Gf z!e5%*^Bz2L4G1I%r5%Q{B@^yV*TRo!qn35kKoPNAA-pHy?c$ zUIp8J(QpS)^TT~LTYi3{(6O!>>ZM>?$GVv6Y0FH4bQjn<{BUsf1wV#@qA~w?RkyaX1(Cx(@R{9 zmL!VG0%<9dp+_35$Fy0JT#DL;+K$7x8KzN2d7!7DslE|5k%}|`GBLcpIGIm%QfKn$ zTa2lUq}ee(WuC)S5Msp4Dm1+KO;EmBx+gTwOKqBD_n?rlaH!evd{XLzAHcQ8YWAjr z+xe~)z9*-9=}@y>t2Vs{aT2FiB){OEN5ErhJ|@}HO4DzEw>UC)dppEKf5tsK7UafC z&Qo6984LDJaxRUrXT9ZmtuePI{Z?y(&D4CS-Kv++>_Sb|$@kbhxuxn^ISWRTdgrP& zzpd6#-r!hYMF+ZS`maID&B;%#GUxb9`iO5!Hw2i%#=7$a5>L8W~=gC-*<9R9l5NRrt1bx5Af&&}f6t z%bVGX#8b|*E@5-(7AbG3>em+Po={*VdV|oYYf>SJDxsIbKEfEseea_<6s=skXcWwx zVP4y974cm(O#HJ9>zZZQ*Q{%~ET5Z(jbj0KhnUtd&0)hbt+!0me#^XOX3RHoA2;7L zH_h$6c~yx`H0Cx({n8Pqf8_Y|vaW)U6$3(OgV}F<2`pHF`Srg5jo5VL~lt)(xG;8<4x!DLh+Ejc0<_k1W({ zXPwoBI#_`cqJoqJ^N5E*&vzGhW@eHBnVA9Cs8v@y%`eDBgdFEIJ%MVX$Bx&A++JXj z5hIDoN!W-mAYmSLj&rFX=ab76_s-m;GmV0ihWHCrPHfrbL}wa&q8a!><#U?O0DhINwE9|!DjKSRlnem zo9b>3o9WMLA`&@CB2w?gfmGv>l#4kNq%Z_hu=Dm;a&MTqf+Z=!9Ok@`D602Wjjf44 zWSsny;r*E&dAAOwE-3mORAa-!fTvySiizP&pH8)gAyMo#m^u6hiI9Q3FkSmqO1JEb z=4#5%b<)V2hg>|^NiP=Gt$9eFMrz$|JrX?|z_TTuFR~V-^p1)AV@Y|I^rFFM%o~i* z%(|4q^`J=7j9YXE<$6f2ZM11O+O%!0nY3w0+9V}KZR%_1i$;+3hatzUi{^S3`S&3I z_O>rn(@<#m8P8L%4XGQ96m$r>WCGVj8>@T^KUC*O$@*NYs^zS=)=m}YO zr=(B0yF@yVMpE~t;1xZQBI=tL5v)~C2U54{oUf_|XSzc%LB86ZVkL)cKug{pa&J3e zqC(4{oJ5KrU|vo1@+>LWZBWoO*R~4d(5i#!LV#M3;q_p+%kweKAi9Dv2Xl%lC3t89 zGN#+aWa@Epm!^XE9E*4}8ZcLf`V6IFX$9H?RL`oTwQ+gBNhdT)*Ksfk{8Qv$qc{

=hcxV#+r*_?^CLe3xh^uNly>)nTMpALij9 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/config.cpython-310.pyc b/venv/Lib/site-packages/flask/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08da4fc81fd76d1899d7b60e043068cd557a1776 GIT binary patch literal 13390 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 D@phbG literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/ctx.cpython-310.pyc b/venv/Lib/site-packages/flask/__pycache__/ctx.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9dfe7274106e7f0770dca1022654fb7c29ad3113 GIT binary patch literal 15314 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 Wuq(Op_s+gh&XsYrs#2bR^?v}+GQUs& literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/debughelpers.cpython-310.pyc b/venv/Lib/site-packages/flask/__pycache__/debughelpers.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d84a0712c04be9bb04f22f8c432bcd5305fbc017 GIT binary patch literal 6576 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/helpers.cpython-310.pyc b/venv/Lib/site-packages/flask/__pycache__/helpers.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39ce274fc6da22056982e973b405c90f47358ca1 GIT binary patch literal 22112 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 h4QCDiF9H7LOL(Wt=L;9!IB|Y|z>!z# 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 L#T>^qUETX{t~=Gh literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/testing.cpython-310.pyc b/venv/Lib/site-packages/flask/__pycache__/testing.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0730e11f57a1911e3dc6fc7df4bc07737d3f3cc7 GIT binary patch literal 9819 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/typing.cpython-310.pyc b/venv/Lib/site-packages/flask/__pycache__/typing.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc63091e78f341f1155c7a3edd8ccbf0347f85c5 GIT binary patch literal 1872 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/__pycache__/wrappers.cpython-310.pyc b/venv/Lib/site-packages/flask/__pycache__/wrappers.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..489a02a319325ff483a4f30022bb41e7a37ceb26 GIT binary patch literal 8375 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/app.py b/venv/Lib/site-packages/flask/app.py new file mode 100644 index 0000000..cc326db --- /dev/null +++ b/venv/Lib/site-packages/flask/app.py @@ -0,0 +1,1536 @@ +from __future__ import annotations + +import collections.abc as cabc +import os +import sys +import typing as t +import weakref +from datetime import timedelta +from inspect import iscoroutinefunction +from itertools import chain +from types import TracebackType +from urllib.parse import quote as _url_quote + +import click +from werkzeug.datastructures import Headers +from werkzeug.datastructures import ImmutableDict +from werkzeug.exceptions import BadRequestKeyError +from werkzeug.exceptions import HTTPException +from werkzeug.exceptions import InternalServerError +from werkzeug.routing import BuildError +from werkzeug.routing import MapAdapter +from werkzeug.routing import RequestRedirect +from werkzeug.routing import RoutingException +from werkzeug.routing import Rule +from werkzeug.serving import is_running_from_reloader +from werkzeug.wrappers import Response as BaseResponse +from werkzeug.wsgi import get_host + +from . import cli +from . import typing as ft +from .ctx import AppContext +from .ctx import RequestContext +from .globals import _cv_app +from .globals import _cv_request +from .globals import current_app +from .globals import g +from .globals import request +from .globals import request_ctx +from .globals import session +from .helpers import get_debug_flag +from .helpers import get_flashed_messages +from .helpers import get_load_dotenv +from .helpers import send_from_directory +from .sansio.app import App +from .sansio.scaffold import _sentinel +from .sessions import SecureCookieSessionInterface +from .sessions import SessionInterface +from .signals import appcontext_tearing_down +from .signals import got_request_exception +from .signals import request_finished +from .signals import request_started +from .signals import request_tearing_down +from .templating import Environment +from .wrappers import Request +from .wrappers import Response + +if t.TYPE_CHECKING: # pragma: no cover + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIEnvironment + + from .testing import FlaskClient + from .testing import FlaskCliRunner + from .typing import HeadersValue + +T_shell_context_processor = t.TypeVar( + "T_shell_context_processor", bound=ft.ShellContextProcessorCallable +) +T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) +T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable) +T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable) +T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable) + + +def _make_timedelta(value: timedelta | int | None) -> timedelta | None: + if value is None or isinstance(value, timedelta): + return value + + return timedelta(seconds=value) + + +class Flask(App): + """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. + """ + + default_config = ImmutableDict( + { + "DEBUG": None, + "TESTING": False, + "PROPAGATE_EXCEPTIONS": None, + "SECRET_KEY": None, + "SECRET_KEY_FALLBACKS": None, + "PERMANENT_SESSION_LIFETIME": timedelta(days=31), + "USE_X_SENDFILE": False, + "TRUSTED_HOSTS": None, + "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_PARTITIONED": False, + "SESSION_COOKIE_SAMESITE": None, + "SESSION_REFRESH_EACH_REQUEST": True, + "MAX_CONTENT_LENGTH": None, + "MAX_FORM_MEMORY_SIZE": 500_000, + "MAX_FORM_PARTS": 1_000, + "SEND_FILE_MAX_AGE_DEFAULT": None, + "TRAP_BAD_REQUEST_ERRORS": None, + "TRAP_HTTP_EXCEPTIONS": False, + "EXPLAIN_TEMPLATE_LOADING": False, + "PREFERRED_URL_SCHEME": "http", + "TEMPLATES_AUTO_RELOAD": None, + "MAX_COOKIE_SIZE": 4093, + "PROVIDE_AUTOMATIC_OPTIONS": True, + } + ) + + #: The class that is used for request objects. See :class:`~flask.Request` + #: for more information. + request_class: type[Request] = Request + + #: The class that is used for response objects. See + #: :class:`~flask.Response` for more information. + response_class: type[Response] = Response + + #: the session interface to use. By default an instance of + #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here. + #: + #: .. versionadded:: 0.8 + session_interface: SessionInterface = SecureCookieSessionInterface() + + def __init__( + self, + import_name: str, + static_url_path: str | None = None, + static_folder: str | os.PathLike[str] | None = "static", + static_host: str | None = None, + host_matching: bool = False, + subdomain_matching: bool = False, + template_folder: str | os.PathLike[str] | None = "templates", + instance_path: str | None = None, + instance_relative_config: bool = False, + root_path: str | None = None, + ): + super().__init__( + import_name=import_name, + static_url_path=static_url_path, + static_folder=static_folder, + static_host=static_host, + host_matching=host_matching, + subdomain_matching=subdomain_matching, + template_folder=template_folder, + instance_path=instance_path, + instance_relative_config=instance_relative_config, + root_path=root_path, + ) + + #: The Click command group for registering CLI commands for this + #: object. The commands are available from the ``flask`` command + #: once the application has been discovered and blueprints have + #: been registered. + self.cli = cli.AppGroup() + + # 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 + + # 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 + ) + + def get_send_file_max_age(self, filename: str | None) -> int | None: + """Used by :func:`send_file` to determine the ``max_age`` cache + value for a given file path if it wasn't passed. + + By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from + the configuration of :data:`~flask.current_app`. This defaults + to ``None``, which tells the browser to use conditional requests + instead of a timed cache, which is usually preferable. + + Note this is a duplicate of the same method in the Flask + class. + + .. versionchanged:: 2.0 + The default configuration is ``None`` instead of 12 hours. + + .. versionadded:: 0.9 + """ + value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"] + + if value is None: + return None + + if isinstance(value, timedelta): + return int(value.total_seconds()) + + return value # type: ignore[no-any-return] + + def send_static_file(self, filename: str) -> Response: + """The view function used to serve files from + :attr:`static_folder`. A route is automatically registered for + this view at :attr:`static_url_path` if :attr:`static_folder` is + set. + + Note this is a duplicate of the same method in the Flask + class. + + .. versionadded:: 0.5 + + """ + if not self.has_static_folder: + raise RuntimeError("'static_folder' must be set to serve static_files.") + + # send_file only knows to call get_send_file_max_age on the app, + # call it here so it works for blueprints too. + max_age = self.get_send_file_max_age(filename) + return send_from_directory( + t.cast(str, self.static_folder), filename, max_age=max_age + ) + + def open_resource( + self, resource: str, mode: str = "rb", encoding: str | None = None + ) -> t.IO[t.AnyStr]: + """Open a resource file relative to :attr:`root_path` for reading. + + For example, if the file ``schema.sql`` is next to the file + ``app.py`` where the ``Flask`` app is defined, it can be opened + with: + + .. code-block:: python + + with app.open_resource("schema.sql") as f: + conn.executescript(f.read()) + + :param resource: Path to the resource relative to :attr:`root_path`. + :param mode: Open the file in this mode. Only reading is supported, + valid values are ``"r"`` (or ``"rt"``) and ``"rb"``. + :param encoding: Open the file with this encoding when opening in text + mode. This is ignored when opening in binary mode. + + .. versionchanged:: 3.1 + Added the ``encoding`` parameter. + """ + if mode not in {"r", "rt", "rb"}: + raise ValueError("Resources can only be opened for reading.") + + path = os.path.join(self.root_path, resource) + + if mode == "rb": + return open(path, mode) # pyright: ignore + + return open(path, mode, encoding=encoding) + + def open_instance_resource( + self, resource: str, mode: str = "rb", encoding: str | None = "utf-8" + ) -> t.IO[t.AnyStr]: + """Open a resource file relative to the application's instance folder + :attr:`instance_path`. Unlike :meth:`open_resource`, files in the + instance folder can be opened for writing. + + :param resource: Path to the resource relative to :attr:`instance_path`. + :param mode: Open the file in this mode. + :param encoding: Open the file with this encoding when opening in text + mode. This is ignored when opening in binary mode. + + .. versionchanged:: 3.1 + Added the ``encoding`` parameter. + """ + path = os.path.join(self.instance_path, resource) + + if "b" in mode: + return open(path, mode) + + return open(path, mode, encoding=encoding) + + def create_jinja_environment(self) -> Environment: + """Create the Jinja environment based on :attr:`jinja_options` + and the various Jinja-related methods of the app. Changing + :attr:`jinja_options` after this will have no effect. Also adds + Flask-related globals and filters to the environment. + + .. versionchanged:: 0.11 + ``Environment.auto_reload`` set in accordance with + ``TEMPLATES_AUTO_RELOAD`` configuration option. + + .. versionadded:: 0.5 + """ + options = dict(self.jinja_options) + + if "autoescape" not in options: + options["autoescape"] = self.select_jinja_autoescape + + if "auto_reload" not in options: + auto_reload = self.config["TEMPLATES_AUTO_RELOAD"] + + if auto_reload is None: + auto_reload = self.debug + + options["auto_reload"] = auto_reload + + rv = self.jinja_environment(self, **options) + rv.globals.update( + url_for=self.url_for, + get_flashed_messages=get_flashed_messages, + config=self.config, + # request, session and g are normally added with the + # context processor for efficiency reasons but for imported + # templates we also want the proxies in there. + request=request, + session=session, + g=g, + ) + rv.policies["json.dumps_function"] = self.json.dumps + return rv + + def create_url_adapter(self, request: Request | None) -> MapAdapter | None: + """Creates a URL adapter for the given request. The URL adapter + is created at a point where the request context is not yet set + up so the request is passed explicitly. + + .. versionchanged:: 3.1 + If :data:`SERVER_NAME` is set, it does not restrict requests to + only that domain, for both ``subdomain_matching`` and + ``host_matching``. + + .. versionchanged:: 1.0 + :data:`SERVER_NAME` no longer implicitly enables subdomain + matching. Use :attr:`subdomain_matching` instead. + + .. versionchanged:: 0.9 + This can be called outside a request when the URL adapter is created + for an application context. + + .. versionadded:: 0.6 + """ + if request is not None: + if (trusted_hosts := self.config["TRUSTED_HOSTS"]) is not None: + request.trusted_hosts = trusted_hosts + + # Check trusted_hosts here until bind_to_environ does. + request.host = get_host(request.environ, request.trusted_hosts) # pyright: ignore + subdomain = None + server_name = self.config["SERVER_NAME"] + + if self.url_map.host_matching: + # Don't pass SERVER_NAME, otherwise it's used and the actual + # host is ignored, which breaks host matching. + server_name = None + elif not self.subdomain_matching: + # Werkzeug doesn't implement subdomain matching yet. Until then, + # disable it by forcing the current subdomain to the default, or + # the empty string. + subdomain = self.url_map.default_subdomain or "" + + return self.url_map.bind_to_environ( + request.environ, server_name=server_name, subdomain=subdomain + ) + + # Need at least SERVER_NAME to match/build outside a request. + 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 raise_routing_exception(self, request: Request) -> t.NoReturn: + """Intercept routing exceptions and possibly do something else. + + In debug mode, intercept a routing redirect and replace it with + an error if the body will be discarded. + + With modern Werkzeug this shouldn't occur, since it now uses a + 308 status which tells the browser to resend the method and + body. + + .. versionchanged:: 2.1 + Don't intercept 307 and 308 redirects. + + :meta private: + :internal: + """ + if ( + not self.debug + or not isinstance(request.routing_exception, RequestRedirect) + or request.routing_exception.code in {307, 308} + or request.method in {"GET", "HEAD", "OPTIONS"} + ): + raise request.routing_exception # type: ignore[misc] + + from .debughelpers import FormDataRoutingRedirect + + raise FormDataRoutingRedirect(request) + + def update_template_context(self, context: dict[str, t.Any]) -> None: + """Update the template context with some commonly used variables. + This injects request, session, config and g into the template + context as well as everything template context processors want + to inject. Note that the as of Flask 0.6, the original values + in the context will not be overridden if a context processor + decides to return a value with the same key. + + :param context: the context as a dictionary that is updated in place + to add extra variables. + """ + names: t.Iterable[str | None] = (None,) + + # A template may be rendered outside a request context. + if request: + names = chain(names, reversed(request.blueprints)) + + # The values passed to render_template take precedence. Keep a + # copy to re-apply after all context functions. + orig_ctx = context.copy() + + for name in names: + if name in self.template_context_processors: + for func in self.template_context_processors[name]: + context.update(self.ensure_sync(func)()) + + context.update(orig_ctx) + + def make_shell_context(self) -> dict[str, t.Any]: + """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 + + def run( + self, + host: str | None = None, + port: int | None = None, + debug: bool | None = None, + load_dotenv: bool = True, + **options: t.Any, + ) -> None: + """Runs the application on a local development server. + + Do not use ``run()`` in a production setting. It is not intended to + meet security and performance requirements for a production server. + Instead, see :doc:`/deploying/index` for WSGI server recommendations. + + If the :attr:`debug` flag is set the server will automatically reload + for code changes and show a debugger in case an exception happened. + + If you want to run the application in debug mode, but disable the + code execution on the interactive debugger, you can pass + ``use_evalex=False`` as parameter. This will keep the debugger's + traceback screen active, but disable code execution. + + It is not recommended to use this function for development with + automatic reloading as this is badly supported. Instead you should + be using the :command:`flask` command line script's ``run`` support. + + .. admonition:: Keep in Mind + + Flask will suppress any server error with a generic error page + unless it is in debug mode. As such to enable just the + interactive debugger without the code reloading, you have to + invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``. + Setting ``use_debugger`` to ``True`` without being in debug mode + won't catch any exceptions because there won't be any to + catch. + + :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to + have the server available externally as well. Defaults to + ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable + if present. + :param port: the port of the webserver. Defaults to ``5000`` or the + port defined in the ``SERVER_NAME`` config variable if present. + :param debug: if given, enable or disable debug mode. See + :attr:`debug`. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param options: the options to be forwarded to the underlying Werkzeug + server. See :func:`werkzeug.serving.run_simple` for more + information. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment + variables from :file:`.env` and :file:`.flaskenv` files. + + The :envvar:`FLASK_DEBUG` environment variable will override :attr:`debug`. + + Threaded mode is enabled by default. + + .. versionchanged:: 0.10 + The default port is now picked from the ``SERVER_NAME`` + variable. + """ + # Ignore this call so that it doesn't start another server if + # the 'flask run' command is used. + if os.environ.get("FLASK_RUN_FROM_CLI") == "true": + if not is_running_from_reloader(): + click.secho( + " * Ignoring a call to 'app.run()' that would block" + " the current 'flask' CLI command.\n" + " Only call 'app.run()' in an 'if __name__ ==" + ' "__main__"\' guard.', + fg="red", + ) + + return + + if get_load_dotenv(load_dotenv): + cli.load_dotenv() + + # if set, env var overrides existing value + if "FLASK_DEBUG" in os.environ: + self.debug = get_debug_flag() + + # debug passed to method overrides all other sources + if debug is not None: + self.debug = bool(debug) + + server_name = self.config.get("SERVER_NAME") + sn_host = sn_port = None + + if server_name: + sn_host, _, sn_port = server_name.partition(":") + + if not host: + if sn_host: + host = sn_host + else: + host = "127.0.0.1" + + if port or port == 0: + port = int(port) + elif sn_port: + port = int(sn_port) + else: + port = 5000 + + options.setdefault("use_reloader", self.debug) + options.setdefault("use_debugger", self.debug) + options.setdefault("threaded", True) + + cli.show_server_banner(self.debug, self.name) + + from werkzeug.serving import run_simple + + try: + run_simple(t.cast(str, host), port, self, **options) + finally: + # reset the first request information if the development server + # reset normally. This makes it possible to restart the server + # without reloader and that stuff from an interactive shell. + self._got_first_request = False + + def test_client(self, use_cookies: bool = True, **kwargs: t.Any) -> FlaskClient: + """Creates a test client for this application. For information + about unit testing head over to :doc:`/testing`. + + Note that if you are testing for assertions or exceptions in your + application code, you must set ``app.testing = True`` in order for the + exceptions to propagate to the test client. Otherwise, the exception + will be handled by the application (not visible to the test client) and + the only indication of an AssertionError or other exception will be a + 500 status code response to the test client. See the :attr:`testing` + attribute. For example:: + + app.testing = True + client = app.test_client() + + The test client can be used in a ``with`` block to defer the closing down + of the context until the end of the ``with`` block. This is useful if + you want to access the context locals for testing:: + + with app.test_client() as c: + rv = c.get('/?vodka=42') + assert request.args['vodka'] == '42' + + Additionally, you may pass optional keyword arguments that will then + be passed to the application's :attr:`test_client_class` constructor. + For example:: + + from flask.testing import FlaskClient + + class CustomClient(FlaskClient): + def __init__(self, *args, **kwargs): + self._authentication = kwargs.pop("authentication") + super(CustomClient,self).__init__( *args, **kwargs) + + app.test_client_class = CustomClient + client = app.test_client(authentication='Basic ....') + + See :class:`~flask.testing.FlaskClient` for more information. + + .. versionchanged:: 0.4 + added support for ``with`` block usage for the client. + + .. versionadded:: 0.7 + The `use_cookies` parameter was added as well as the ability + to override the client to be used by setting the + :attr:`test_client_class` attribute. + + .. versionchanged:: 0.11 + Added `**kwargs` to support passing additional keyword arguments to + the constructor of :attr:`test_client_class`. + """ + cls = self.test_client_class + if cls is None: + from .testing import FlaskClient as cls + return cls( # type: ignore + self, self.response_class, use_cookies=use_cookies, **kwargs + ) + + def test_cli_runner(self, **kwargs: t.Any) -> FlaskCliRunner: + """Create a CLI runner for testing CLI commands. + See :ref:`testing-cli`. + + Returns an instance of :attr:`test_cli_runner_class`, by default + :class:`~flask.testing.FlaskCliRunner`. The Flask app object is + passed as the first argument. + + .. versionadded:: 1.0 + """ + cls = self.test_cli_runner_class + + if cls is None: + from .testing import FlaskCliRunner as cls + + return cls(self, **kwargs) # type: ignore + + def handle_http_exception( + self, e: HTTPException + ) -> HTTPException | ft.ResponseReturnValue: + """Handles an HTTP exception. By default this will invoke the + registered error handlers and fall back to returning the + exception as response. + + .. versionchanged:: 1.0.3 + ``RoutingException``, used internally for actions such as + slash redirects during routing, is not passed to error + handlers. + + .. versionchanged:: 1.0 + Exceptions are looked up by code *and* by MRO, so + ``HTTPException`` subclasses can be handled with a catch-all + handler for the base ``HTTPException``. + + .. versionadded:: 0.3 + """ + # Proxy exceptions don't have error codes. We want to always return + # those unchanged as errors + if e.code is None: + return e + + # RoutingExceptions are used internally to trigger routing + # actions, such as slash redirects raising RequestRedirect. They + # are not raised or handled in user code. + if isinstance(e, RoutingException): + return e + + handler = self._find_error_handler(e, request.blueprints) + if handler is None: + return e + return self.ensure_sync(handler)(e) # type: ignore[no-any-return] + + def handle_user_exception( + self, e: Exception + ) -> HTTPException | ft.ResponseReturnValue: + """This method is called whenever an exception occurs that + should be handled. A special case is :class:`~werkzeug + .exceptions.HTTPException` which is forwarded to the + :meth:`handle_http_exception` method. This function will either + return a response value or reraise the exception with the same + traceback. + + .. versionchanged:: 1.0 + Key errors raised from request data like ``form`` show the + bad key in debug mode rather than a generic bad request + message. + + .. versionadded:: 0.7 + """ + if isinstance(e, BadRequestKeyError) and ( + self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"] + ): + e.show_exception = True + + if isinstance(e, HTTPException) and not self.trap_http_exception(e): + return self.handle_http_exception(e) + + handler = self._find_error_handler(e, request.blueprints) + + if handler is None: + raise + + return self.ensure_sync(handler)(e) # type: ignore[no-any-return] + + def handle_exception(self, e: Exception) -> Response: + """Handle an exception that did not have an error handler + associated with it, or that was raised from an error handler. + This always causes a 500 ``InternalServerError``. + + Always sends the :data:`got_request_exception` signal. + + If :data:`PROPAGATE_EXCEPTIONS` is ``True``, such as in debug + mode, the error will be re-raised so that the debugger can + display it. Otherwise, the original exception is logged, and + an :exc:`~werkzeug.exceptions.InternalServerError` is returned. + + If an error handler is registered for ``InternalServerError`` or + ``500``, it will be used. For consistency, the handler will + always receive the ``InternalServerError``. The original + unhandled exception is available as ``e.original_exception``. + + .. versionchanged:: 1.1.0 + Always passes the ``InternalServerError`` instance to the + handler, setting ``original_exception`` to the unhandled + error. + + .. versionchanged:: 1.1.0 + ``after_request`` functions and other finalization is done + even for the default 500 response when there is no handler. + + .. versionadded:: 0.3 + """ + exc_info = sys.exc_info() + got_request_exception.send(self, _async_wrapper=self.ensure_sync, exception=e) + propagate = self.config["PROPAGATE_EXCEPTIONS"] + + if propagate is None: + propagate = self.testing or self.debug + + if propagate: + # Re-raise if called with an active exception, otherwise + # raise the passed in exception. + if exc_info[1] is e: + raise + + raise e + + self.log_exception(exc_info) + server_error: InternalServerError | ft.ResponseReturnValue + server_error = InternalServerError(original_exception=e) + handler = self._find_error_handler(server_error, request.blueprints) + + if handler is not None: + server_error = self.ensure_sync(handler)(server_error) + + return self.finalize_request(server_error, from_error_handler=True) + + def log_exception( + self, + exc_info: (tuple[type, BaseException, TracebackType] | tuple[None, None, None]), + ) -> None: + """Logs an exception. This is called by :meth:`handle_exception` + if debugging is disabled and right before the handler is called. + The default implementation logs the exception as error on the + :attr:`logger`. + + .. versionadded:: 0.8 + """ + self.logger.error( + f"Exception on {request.path} [{request.method}]", exc_info=exc_info + ) + + def dispatch_request(self) -> ft.ResponseReturnValue: + """Does the request dispatching. Matches the URL and returns the + return value of the view or error handler. This does not have to + be a response object. In order to convert the return value to a + proper response object, call :func:`make_response`. + + .. versionchanged:: 0.7 + This no longer does the exception handling, this code was + moved to the new :meth:`full_dispatch_request`. + """ + req = request_ctx.request + if req.routing_exception is not None: + self.raise_routing_exception(req) + rule: Rule = req.url_rule # type: ignore[assignment] + # if we provide automatic options for this URL and the + # request came with the OPTIONS method, reply automatically + if ( + getattr(rule, "provide_automatic_options", False) + and req.method == "OPTIONS" + ): + return self.make_default_options_response() + # otherwise dispatch to the handler for that endpoint + view_args: dict[str, t.Any] = req.view_args # type: ignore[assignment] + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + + def full_dispatch_request(self) -> Response: + """Dispatches the request and on top of that performs request + pre and postprocessing as well as HTTP exception catching and + error handling. + + .. versionadded:: 0.7 + """ + self._got_first_request = True + + try: + request_started.send(self, _async_wrapper=self.ensure_sync) + rv = self.preprocess_request() + if rv is None: + rv = self.dispatch_request() + except Exception as e: + rv = self.handle_user_exception(e) + return self.finalize_request(rv) + + def finalize_request( + self, + rv: ft.ResponseReturnValue | HTTPException, + from_error_handler: bool = False, + ) -> Response: + """Given the return value from a view function this finalizes + the request by converting it into a response and invoking the + postprocessing functions. This is invoked for both normal + request dispatching as well as error handlers. + + Because this means that it might be called as a result of a + failure a special safe mode is available which can be enabled + with the `from_error_handler` flag. If enabled, failures in + response processing will be logged and otherwise ignored. + + :internal: + """ + response = self.make_response(rv) + try: + response = self.process_response(response) + request_finished.send( + self, _async_wrapper=self.ensure_sync, response=response + ) + except Exception: + if not from_error_handler: + raise + self.logger.exception( + "Request finalizing failed with an error while handling an error" + ) + return response + + def make_default_options_response(self) -> Response: + """This method is called to create the default ``OPTIONS`` response. + This can be changed through subclassing to change the default + behavior of ``OPTIONS`` responses. + + .. versionadded:: 0.7 + """ + adapter = request_ctx.url_adapter + methods = adapter.allowed_methods() # type: ignore[union-attr] + rv = self.response_class() + rv.allow.update(methods) + return rv + + def ensure_sync(self, func: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """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.Any, t.Any, t.Any]] + ) -> t.Callable[..., t.Any]: + """Return a sync function that will run the coroutine function. + + .. code-block:: python + + result = app.async_to_sync(func)(*args, **kwargs) + + Override this method to change how the app converts async code + to be synchronously callable. + + .. versionadded:: 2.0 + """ + try: + from asgiref.sync import async_to_sync as asgiref_async_to_sync + except ImportError: + raise RuntimeError( + "Install Flask with the 'async' extra in order to use async views." + ) from None + + return asgiref_async_to_sync(func) + + def url_for( + self, + /, + endpoint: str, + *, + _anchor: str | None = None, + _method: str | None = None, + _scheme: str | None = None, + _external: bool | None = None, + **values: t.Any, + ) -> str: + """Generate a URL to the given endpoint with the given values. + + This is called by :func:`flask.url_for`, and can be called + directly as well. + + An *endpoint* is the name of a URL rule, usually added with + :meth:`@app.route() `, and usually the same name as the + view function. A route defined in a :class:`~flask.Blueprint` + will prepend the blueprint's name separated by a ``.`` to the + endpoint. + + In some cases, such as email messages, you want URLs to include + the scheme and domain, like ``https://example.com/hello``. When + not in an active request, URLs will be external by default, but + this requires setting :data:`SERVER_NAME` so Flask knows what + domain to use. :data:`APPLICATION_ROOT` and + :data:`PREFERRED_URL_SCHEME` should also be configured as + needed. This config is only used when not in an active request. + + Functions can be decorated with :meth:`url_defaults` to modify + keyword arguments before the URL is built. + + If building fails for some reason, such as an unknown endpoint + or incorrect values, the app's :meth:`handle_url_build_error` + method is called. If that returns a string, that is returned, + otherwise a :exc:`~werkzeug.routing.BuildError` is raised. + + :param endpoint: The endpoint name associated with the URL to + generate. If this starts with a ``.``, the current blueprint + name (if any) will be used. + :param _anchor: If given, append this as ``#anchor`` to the URL. + :param _method: If given, generate the URL associated with this + method for the endpoint. + :param _scheme: If given, the URL will have this scheme if it + is external. + :param _external: If given, prefer the URL to be internal + (False) or require it to be external (True). External URLs + include the scheme and domain. When not in an active + request, URLs are external by default. + :param values: Values to use for the variable parts of the URL + rule. Unknown keys are appended as query string arguments, + like ``?a=b&c=d``. + + .. versionadded:: 2.2 + Moved from ``flask.url_for``, which calls this method. + """ + req_ctx = _cv_request.get(None) + + if req_ctx is not None: + url_adapter = req_ctx.url_adapter + blueprint_name = req_ctx.request.blueprint + + # If the endpoint starts with "." and the request matches a + # blueprint, the endpoint is relative to the blueprint. + if endpoint[:1] == ".": + if blueprint_name is not None: + endpoint = f"{blueprint_name}{endpoint}" + else: + endpoint = endpoint[1:] + + # When in a request, generate a URL without scheme and + # domain by default, unless a scheme is given. + if _external is None: + _external = _scheme is not None + else: + app_ctx = _cv_app.get(None) + + # If called by helpers.url_for, an app context is active, + # use its url_adapter. Otherwise, app.url_for was called + # directly, build an adapter. + if app_ctx is not None: + url_adapter = app_ctx.url_adapter + else: + url_adapter = self.create_url_adapter(None) + + if url_adapter is None: + raise RuntimeError( + "Unable to build URLs outside an active request" + " without 'SERVER_NAME' configured. Also configure" + " 'APPLICATION_ROOT' and 'PREFERRED_URL_SCHEME' as" + " needed." + ) + + # When outside a request, generate a URL with scheme and + # domain by default. + if _external is None: + _external = True + + # It is an error to set _scheme when _external=False, in order + # to avoid accidental insecure URLs. + if _scheme is not None and not _external: + raise ValueError("When specifying '_scheme', '_external' must be True.") + + self.inject_url_defaults(endpoint, values) + + try: + rv = url_adapter.build( # type: ignore[union-attr] + endpoint, + values, + method=_method, + url_scheme=_scheme, + force_external=_external, + ) + except BuildError as error: + values.update( + _anchor=_anchor, _method=_method, _scheme=_scheme, _external=_external + ) + return self.handle_url_build_error(error, endpoint, values) + + if _anchor is not None: + _anchor = _url_quote(_anchor, safe="%!#$&'()*+,/:;=?@") + rv = f"{rv}#{_anchor}" + + return rv + + def make_response(self, rv: ft.ResponseReturnValue) -> Response: + """Convert the return value from a view function to an instance of + :attr:`response_class`. + + :param rv: the return value from the view function. The view function + must return a response. Returning ``None``, or the view ending + without returning, is not allowed. The following types are allowed + for ``view_rv``: + + ``str`` + A response object is created with the string encoded to UTF-8 + as the body. + + ``bytes`` + A response object is created with the bytes as the body. + + ``dict`` + A dictionary that will be jsonify'd before being returned. + + ``list`` + A list that will be jsonify'd before being returned. + + ``generator`` or ``iterator`` + A generator that returns ``str`` or ``bytes`` to be + streamed as the response. + + ``tuple`` + Either ``(body, status, headers)``, ``(body, status)``, or + ``(body, headers)``, where ``body`` is any of the other types + allowed here, ``status`` is a string or an integer, and + ``headers`` is a dictionary or a list of ``(key, value)`` + tuples. If ``body`` is a :attr:`response_class` instance, + ``status`` overwrites the exiting value and ``headers`` are + extended. + + :attr:`response_class` + The object is returned unchanged. + + other :class:`~werkzeug.wrappers.Response` class + The object is coerced to :attr:`response_class`. + + :func:`callable` + The function is called as a WSGI application. The result is + used to create a response object. + + .. versionchanged:: 2.2 + A generator will be converted to a streaming response. + A list will be converted to a JSON response. + + .. versionchanged:: 1.1 + A dict will be converted to a JSON response. + + .. versionchanged:: 0.9 + Previously a tuple was interpreted as the arguments for the + response object. + """ + + status: int | None = None + headers: HeadersValue | None = None + + # unpack tuple returns + if isinstance(rv, tuple): + len_rv = len(rv) + + # a 3-tuple is unpacked directly + if len_rv == 3: + rv, status, headers = rv # type: ignore[misc] + # decide if a 2-tuple has status or headers + elif len_rv == 2: + if isinstance(rv[1], (Headers, dict, tuple, list)): + rv, headers = rv # pyright: ignore + else: + rv, status = rv # type: ignore[assignment,misc] + # other sized tuples are not allowed + else: + raise TypeError( + "The view function did not return a valid response tuple." + " The tuple must have the form (body, status, headers)," + " (body, status), or (body, headers)." + ) + + # the body must not be None + if rv is None: + raise TypeError( + f"The view function for {request.endpoint!r} did not" + " return a valid response. The function either returned" + " None or ended without a return statement." + ) + + # make sure the body is an instance of the response class + if not isinstance(rv, self.response_class): + if isinstance(rv, (str, bytes, bytearray)) or isinstance(rv, cabc.Iterator): + # let the response class set the status and headers instead of + # waiting to do it manually, so that the class can handle any + # special logic + rv = self.response_class( + rv, # pyright: ignore + status=status, + headers=headers, # type: ignore[arg-type] + ) + status = headers = None + elif isinstance(rv, (dict, list)): + rv = self.json.response(rv) + elif isinstance(rv, BaseResponse) or callable(rv): + # evaluate a WSGI callable, or coerce a different response + # class to the correct type + try: + rv = self.response_class.force_type( + rv, # type: ignore[arg-type] + request.environ, + ) + except TypeError as e: + raise TypeError( + f"{e}\nThe view function did not return a valid" + " response. The return type must be a string," + " dict, list, tuple with headers or status," + " Response instance, or WSGI callable, but it" + f" was a {type(rv).__name__}." + ).with_traceback(sys.exc_info()[2]) from None + else: + raise TypeError( + "The view function did not return a valid" + " response. The return type must be a string," + " dict, list, tuple with headers or status," + " Response instance, or WSGI callable, but it was a" + f" {type(rv).__name__}." + ) + + rv = t.cast(Response, rv) + # prefer the status if it was provided + if status is not None: + if isinstance(status, (str, bytes, bytearray)): + rv.status = status + else: + rv.status_code = status + + # extend existing headers with provided headers + if headers: + rv.headers.update(headers) + + return rv + + def preprocess_request(self) -> ft.ResponseReturnValue | None: + """Called before the request is dispatched. Calls + :attr:`url_value_preprocessors` registered with the app and the + current blueprint (if any). Then calls :attr:`before_request_funcs` + registered with the app and the blueprint. + + If any :meth:`before_request` handler returns a non-None value, the + value is handled as if it was the return value from the view, and + further request handling is stopped. + """ + names = (None, *reversed(request.blueprints)) + + for name in names: + if name in self.url_value_preprocessors: + for url_func in self.url_value_preprocessors[name]: + url_func(request.endpoint, request.view_args) + + for name in names: + if name in self.before_request_funcs: + for before_func in self.before_request_funcs[name]: + rv = self.ensure_sync(before_func)() + + if rv is not None: + return rv # type: ignore[no-any-return] + + return None + + def process_response(self, response: Response) -> Response: + """Can be overridden in order to modify the response object + before it's sent to the WSGI server. By default this will + call all the :meth:`after_request` decorated functions. + + .. versionchanged:: 0.5 + As of Flask 0.5 the functions registered for after request + execution are called in reverse order of registration. + + :param response: a :attr:`response_class` object. + :return: a new response object or the same, has to be an + instance of :attr:`response_class`. + """ + ctx = request_ctx._get_current_object() # type: ignore[attr-defined] + + for func in ctx._after_request_functions: + response = self.ensure_sync(func)(response) + + for name in chain(request.blueprints, (None,)): + if name in self.after_request_funcs: + for func in reversed(self.after_request_funcs[name]): + response = self.ensure_sync(func)(response) + + if not self.session_interface.is_null_session(ctx._session): + self.session_interface.save_session(self, ctx._session, response) + + return response + + def do_teardown_request( + self, + exc: BaseException | None = _sentinel, # type: ignore[assignment] + ) -> None: + """Called after the request is dispatched and the response is + returned, right before the request context is popped. + + This calls all functions decorated with + :meth:`teardown_request`, and :meth:`Blueprint.teardown_request` + if a blueprint handled the request. Finally, the + :data:`request_tearing_down` signal is sent. + + This is called by + :meth:`RequestContext.pop() `, + which may be delayed during testing to maintain access to + resources. + + :param exc: An unhandled exception raised while dispatching the + request. Detected from the current exception information if + not passed. Passed to each teardown function. + + .. versionchanged:: 0.9 + Added the ``exc`` argument. + """ + if exc is _sentinel: + exc = sys.exc_info()[1] + + for name in chain(request.blueprints, (None,)): + if name in self.teardown_request_funcs: + for func in reversed(self.teardown_request_funcs[name]): + self.ensure_sync(func)(exc) + + request_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc) + + def do_teardown_appcontext( + self, + exc: BaseException | None = _sentinel, # type: ignore[assignment] + ) -> None: + """Called right before the application context is popped. + + When handling a request, the application context is popped + after the request context. See :meth:`do_teardown_request`. + + This calls all functions decorated with + :meth:`teardown_appcontext`. Then the + :data:`appcontext_tearing_down` signal is sent. + + This is called by + :meth:`AppContext.pop() `. + + .. versionadded:: 0.9 + """ + if exc is _sentinel: + exc = sys.exc_info()[1] + + for func in reversed(self.teardown_appcontext_funcs): + self.ensure_sync(func)(exc) + + appcontext_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc) + + def app_context(self) -> AppContext: + """Create an :class:`~flask.ctx.AppContext`. Use as a ``with`` + block to push the context, which will make :data:`current_app` + point at this application. + + An application context is automatically pushed by + :meth:`RequestContext.push() ` + when handling a request, and when running a CLI command. Use + this to manually create a context outside of these situations. + + :: + + with app.app_context(): + init_db() + + See :doc:`/appcontext`. + + .. versionadded:: 0.9 + """ + return AppContext(self) + + def request_context(self, environ: WSGIEnvironment) -> RequestContext: + """Create a :class:`~flask.ctx.RequestContext` representing a + WSGI environment. Use a ``with`` block to push the context, + which will make :data:`request` point at this request. + + See :doc:`/reqcontext`. + + Typically you should not call this from your own code. A request + context is automatically pushed by the :meth:`wsgi_app` when + handling a request. Use :meth:`test_request_context` to create + an environment and context instead of this method. + + :param environ: a WSGI environment + """ + return RequestContext(self, environ) + + def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> RequestContext: + """Create a :class:`~flask.ctx.RequestContext` for a WSGI + environment created from the given values. This is mostly useful + during testing, where you may want to run a function that uses + request data without dispatching a full request. + + See :doc:`/reqcontext`. + + Use a ``with`` block to push the context, which will make + :data:`request` point at the request for the created + environment. :: + + with app.test_request_context(...): + generate_report() + + When using the shell, it may be easier to push and pop the + context manually to avoid indentation. :: + + ctx = app.test_request_context(...) + ctx.push() + ... + ctx.pop() + + Takes the same arguments as Werkzeug's + :class:`~werkzeug.test.EnvironBuilder`, with some defaults from + the application. See the linked Werkzeug docs for most of the + available arguments. Flask-specific behavior is listed here. + + :param path: URL path being requested. + :param base_url: Base URL where the app is being served, which + ``path`` is relative to. If not given, built from + :data:`PREFERRED_URL_SCHEME`, ``subdomain``, + :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. + :param subdomain: Subdomain name to append to + :data:`SERVER_NAME`. + :param url_scheme: Scheme to use instead of + :data:`PREFERRED_URL_SCHEME`. + :param data: The request body, either as a string or a dict of + form keys and values. + :param json: If given, this is serialized as JSON and passed as + ``data``. Also defaults ``content_type`` to + ``application/json``. + :param args: other positional arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + :param kwargs: other keyword arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + """ + from .testing import EnvironBuilder + + builder = EnvironBuilder(self, *args, **kwargs) + + try: + return self.request_context(builder.get_environ()) + finally: + builder.close() + + def wsgi_app( + self, environ: WSGIEnvironment, start_response: StartResponse + ) -> cabc.Iterable[bytes]: + """The actual WSGI application. This is not implemented in + :meth:`__call__` so that middlewares can be applied without + losing a reference to the app object. Instead of doing this:: + + app = MyMiddleware(app) + + It's a better idea to do this instead:: + + app.wsgi_app = MyMiddleware(app.wsgi_app) + + Then you still have the original application object around and + can continue to call methods on it. + + .. versionchanged:: 0.7 + Teardown events for the request and app contexts are called + even if an unhandled error occurs. Other events may not be + called depending on when an error occurs during dispatch. + See :ref:`callbacks-and-errors`. + + :param environ: A WSGI environment. + :param start_response: A callable accepting a status code, + a list of headers, and an optional exception context to + start the response. + """ + ctx = self.request_context(environ) + error: BaseException | None = None + try: + try: + ctx.push() + response = self.full_dispatch_request() + except Exception as e: + error = e + response = self.handle_exception(e) + except: + error = sys.exc_info()[1] + raise + return response(environ, start_response) + finally: + if "werkzeug.debug.preserve_context" in environ: + environ["werkzeug.debug.preserve_context"](_cv_app.get()) + environ["werkzeug.debug.preserve_context"](_cv_request.get()) + + if error is not None and self.should_ignore_error(error): + error = None + + ctx.pop(error) + + def __call__( + self, environ: WSGIEnvironment, start_response: StartResponse + ) -> cabc.Iterable[bytes]: + """The WSGI server calls the Flask application object as the + WSGI application. This calls :meth:`wsgi_app`, which can be + wrapped to apply middleware. + """ + return self.wsgi_app(environ, start_response) diff --git a/venv/Lib/site-packages/flask/blueprints.py b/venv/Lib/site-packages/flask/blueprints.py new file mode 100644 index 0000000..b6d4e43 --- /dev/null +++ b/venv/Lib/site-packages/flask/blueprints.py @@ -0,0 +1,128 @@ +from __future__ import annotations + +import os +import typing as t +from datetime import timedelta + +from .cli import AppGroup +from .globals import current_app +from .helpers import send_from_directory +from .sansio.blueprints import Blueprint as SansioBlueprint +from .sansio.blueprints import BlueprintSetupState as BlueprintSetupState # noqa +from .sansio.scaffold import _sentinel + +if t.TYPE_CHECKING: # pragma: no cover + from .wrappers import Response + + +class Blueprint(SansioBlueprint): + def __init__( + self, + name: str, + import_name: str, + static_folder: str | os.PathLike[str] | None = None, + static_url_path: str | None = None, + template_folder: str | os.PathLike[str] | None = None, + url_prefix: str | None = None, + subdomain: str | None = None, + url_defaults: dict[str, t.Any] | None = None, + root_path: str | None = None, + cli_group: str | None = _sentinel, # type: ignore + ) -> None: + super().__init__( + name, + import_name, + static_folder, + static_url_path, + template_folder, + url_prefix, + subdomain, + url_defaults, + root_path, + cli_group, + ) + + #: The Click command group for registering CLI commands for this + #: object. The commands are available from the ``flask`` command + #: once the application has been discovered and blueprints have + #: been registered. + self.cli = AppGroup() + + # 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 get_send_file_max_age(self, filename: str | None) -> int | None: + """Used by :func:`send_file` to determine the ``max_age`` cache + value for a given file path if it wasn't passed. + + By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from + the configuration of :data:`~flask.current_app`. This defaults + to ``None``, which tells the browser to use conditional requests + instead of a timed cache, which is usually preferable. + + Note this is a duplicate of the same method in the Flask + class. + + .. versionchanged:: 2.0 + The default configuration is ``None`` instead of 12 hours. + + .. versionadded:: 0.9 + """ + value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"] + + if value is None: + return None + + if isinstance(value, timedelta): + return int(value.total_seconds()) + + return value # type: ignore[no-any-return] + + def send_static_file(self, filename: str) -> Response: + """The view function used to serve files from + :attr:`static_folder`. A route is automatically registered for + this view at :attr:`static_url_path` if :attr:`static_folder` is + set. + + Note this is a duplicate of the same method in the Flask + class. + + .. versionadded:: 0.5 + + """ + if not self.has_static_folder: + raise RuntimeError("'static_folder' must be set to serve static_files.") + + # send_file only knows to call get_send_file_max_age on the app, + # call it here so it works for blueprints too. + max_age = self.get_send_file_max_age(filename) + return send_from_directory( + t.cast(str, self.static_folder), filename, max_age=max_age + ) + + def open_resource( + self, resource: str, mode: str = "rb", encoding: str | None = "utf-8" + ) -> t.IO[t.AnyStr]: + """Open a resource file relative to :attr:`root_path` for reading. The + blueprint-relative equivalent of the app's :meth:`~.Flask.open_resource` + method. + + :param resource: Path to the resource relative to :attr:`root_path`. + :param mode: Open the file in this mode. Only reading is supported, + valid values are ``"r"`` (or ``"rt"``) and ``"rb"``. + :param encoding: Open the file with this encoding when opening in text + mode. This is ignored when opening in binary mode. + + .. versionchanged:: 3.1 + Added the ``encoding`` parameter. + """ + if mode not in {"r", "rt", "rb"}: + raise ValueError("Resources can only be opened for reading.") + + path = os.path.join(self.root_path, resource) + + if mode == "rb": + return open(path, mode) # pyright: ignore + + return open(path, mode, encoding=encoding) diff --git a/venv/Lib/site-packages/flask/cli.py b/venv/Lib/site-packages/flask/cli.py new file mode 100644 index 0000000..ed11f25 --- /dev/null +++ b/venv/Lib/site-packages/flask/cli.py @@ -0,0 +1,1135 @@ +from __future__ import annotations + +import ast +import collections.abc as cabc +import importlib.metadata +import inspect +import os +import platform +import re +import sys +import traceback +import typing as t +from functools import update_wrapper +from operator import itemgetter +from types import ModuleType + +import click +from click.core import ParameterSource +from werkzeug import run_simple +from werkzeug.serving import is_running_from_reloader +from werkzeug.utils import import_string + +from .globals import current_app +from .helpers import get_debug_flag +from .helpers import get_load_dotenv + +if t.TYPE_CHECKING: + import ssl + + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + from .app import Flask + + +class NoAppException(click.UsageError): + """Raised if an application cannot be found or loaded.""" + + +def find_best_app(module: ModuleType) -> Flask: + """Given a module instance this tries to find the best possible + application in the module or raises an exception. + """ + from . import Flask + + # Search for the most common names first. + for attr_name in ("app", "application"): + app = getattr(module, attr_name, None) + + if isinstance(app, Flask): + return app + + # Otherwise find the only object that is a Flask instance. + matches = [v for v in module.__dict__.values() if isinstance(v, Flask)] + + if len(matches) == 1: + return matches[0] + elif len(matches) > 1: + raise NoAppException( + "Detected multiple Flask applications in module" + f" '{module.__name__}'. Use '{module.__name__}:name'" + " to specify the correct one." + ) + + # Search for app factory functions. + for attr_name in ("create_app", "make_app"): + app_factory = getattr(module, attr_name, None) + + if inspect.isfunction(app_factory): + try: + app = app_factory() + + if isinstance(app, Flask): + return app + except TypeError as e: + if not _called_with_wrong_args(app_factory): + raise + + raise NoAppException( + f"Detected factory '{attr_name}' in module '{module.__name__}'," + " but could not call it without arguments. Use" + f" '{module.__name__}:{attr_name}(args)'" + " to specify arguments." + ) from e + + raise NoAppException( + "Failed to find Flask application or factory in module" + f" '{module.__name__}'. Use '{module.__name__}:name'" + " to specify one." + ) + + +def _called_with_wrong_args(f: t.Callable[..., Flask]) -> bool: + """Check whether calling a function raised a ``TypeError`` because + the call failed or because something in the factory raised the + error. + + :param f: The function that was called. + :return: ``True`` if the call failed. + """ + tb = sys.exc_info()[2] + + try: + while tb is not None: + if tb.tb_frame.f_code is f.__code__: + # In the function, it was called successfully. + return False + + tb = tb.tb_next + + # Didn't reach the function. + return True + finally: + # Delete tb to break a circular reference. + # https://docs.python.org/2/library/sys.html#sys.exc_info + del tb + + +def find_app_by_string(module: ModuleType, app_name: str) -> Flask: + """Check if the given string is a variable name or a function. Call + a function to get the app instance, or return the variable directly. + """ + from . import Flask + + # Parse app_name as a single expression to determine if it's a valid + # attribute name or function call. + try: + expr = ast.parse(app_name.strip(), mode="eval").body + except SyntaxError: + raise NoAppException( + f"Failed to parse {app_name!r} as an attribute name or function call." + ) from None + + if isinstance(expr, ast.Name): + name = expr.id + args = [] + kwargs = {} + elif isinstance(expr, ast.Call): + # Ensure the function name is an attribute name only. + if not isinstance(expr.func, ast.Name): + raise NoAppException( + f"Function reference must be a simple name: {app_name!r}." + ) + + name = expr.func.id + + # Parse the positional and keyword arguments as literals. + try: + args = [ast.literal_eval(arg) for arg in expr.args] + kwargs = { + kw.arg: ast.literal_eval(kw.value) + for kw in expr.keywords + if kw.arg is not None + } + except ValueError: + # literal_eval gives cryptic error messages, show a generic + # message with the full expression instead. + raise NoAppException( + f"Failed to parse arguments as literal values: {app_name!r}." + ) from None + else: + raise NoAppException( + f"Failed to parse {app_name!r} as an attribute name or function call." + ) + + try: + attr = getattr(module, name) + except AttributeError as e: + raise NoAppException( + f"Failed to find attribute {name!r} in {module.__name__!r}." + ) from e + + # If the attribute is a function, call it with any args and kwargs + # to get the real application. + if inspect.isfunction(attr): + try: + app = attr(*args, **kwargs) + except TypeError as e: + if not _called_with_wrong_args(attr): + raise + + raise NoAppException( + f"The factory {app_name!r} in module" + f" {module.__name__!r} could not be called with the" + " specified arguments." + ) from e + else: + app = attr + + if isinstance(app, Flask): + return app + + raise NoAppException( + "A valid Flask application was not obtained from" + f" '{module.__name__}:{app_name}'." + ) + + +def prepare_import(path: str) -> str: + """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]) + + +@t.overload +def locate_app( + module_name: str, app_name: str | None, raise_if_not_found: t.Literal[True] = True +) -> Flask: ... + + +@t.overload +def locate_app( + module_name: str, app_name: str | None, raise_if_not_found: t.Literal[False] = ... +) -> Flask | None: ... + + +def locate_app( + module_name: str, app_name: str | None, raise_if_not_found: bool = True +) -> Flask | None: + 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: # type: ignore[union-attr] + 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 None + + module = sys.modules[module_name] + + if app_name is None: + return find_best_app(module) + else: + return find_app_by_string(module, app_name) + + +def get_version(ctx: click.Context, param: click.Parameter, value: t.Any) -> None: + if not value or ctx.resilient_parsing: + return + + flask_version = importlib.metadata.version("flask") + werkzeug_version = importlib.metadata.version("werkzeug") + + click.echo( + f"Python {platform.python_version()}\n" + f"Flask {flask_version}\n" + f"Werkzeug {werkzeug_version}", + color=ctx.color, + ) + ctx.exit() + + +version_option = click.Option( + ["--version"], + help="Show the Flask version.", + expose_value=False, + callback=get_version, + is_flag=True, + is_eager=True, +) + + +class ScriptInfo: + """Helper object to deal with Flask applications. This is usually not + necessary to interface with as it's used internally in the dispatching + to click. In future versions of Flask this object will most likely play + a bigger role. Typically it's created automatically by the + :class:`FlaskGroup` but you can also manually create it and pass it + onwards as click object. + + .. versionchanged:: 3.1 + Added the ``load_dotenv_defaults`` parameter and attribute. + """ + + def __init__( + self, + app_import_path: str | None = None, + create_app: t.Callable[..., Flask] | None = None, + set_debug_flag: bool = True, + load_dotenv_defaults: bool = True, + ) -> None: + #: Optionally the import path for the Flask application. + self.app_import_path = app_import_path + #: Optionally a function that is passed the script info to create + #: the instance of the application. + self.create_app = create_app + #: A dictionary with arbitrary data that can be associated with + #: this script info. + self.data: dict[t.Any, t.Any] = {} + self.set_debug_flag = set_debug_flag + + self.load_dotenv_defaults = get_load_dotenv(load_dotenv_defaults) + """Whether default ``.flaskenv`` and ``.env`` files should be loaded. + + ``ScriptInfo`` doesn't load anything, this is for reference when doing + the load elsewhere during processing. + + .. versionadded:: 3.1 + """ + + self._loaded_app: Flask | None = None + + def load_app(self) -> Flask: + """Loads the Flask app (if not yet loaded) and returns it. Calling + this multiple times will just result in the already loaded app to + be returned. + """ + if self._loaded_app is not None: + return self._loaded_app + app: Flask | None = None + if self.create_app is not None: + app = self.create_app() + else: + if self.app_import_path: + path, name = ( + re.split(r":(?![\\/])", self.app_import_path, maxsplit=1) + [None] + )[:2] + import_name = prepare_import(path) + app = locate_app(import_name, name) + else: + for path in ("wsgi.py", "app.py"): + import_name = prepare_import(path) + app = locate_app(import_name, None, raise_if_not_found=False) + + if app is not None: + break + + if app is None: + raise NoAppException( + "Could not locate a Flask application. Use the" + " 'flask --app' option, 'FLASK_APP' environment" + " variable, or a 'wsgi.py' or 'app.py' file in the" + " current directory." + ) + + if self.set_debug_flag: + # Update the app's debug flag through the descriptor so that + # other values repopulate as well. + app.debug = get_debug_flag() + + self._loaded_app = app + return app + + +pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True) + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def with_appcontext(f: F) -> F: + """Wraps a callback so that it's guaranteed to be executed with the + script's application context. + + Custom commands (and their options) registered under ``app.cli`` or + ``blueprint.cli`` will always have an app context available, this + decorator is not required in that case. + + .. versionchanged:: 2.2 + The app context is active for subcommands as well as the + decorated callback. The app context is always available to + ``app.cli`` command and parameter callbacks. + """ + + @click.pass_context + def decorator(ctx: click.Context, /, *args: t.Any, **kwargs: t.Any) -> t.Any: + if not current_app: + app = ctx.ensure_object(ScriptInfo).load_app() + ctx.with_resource(app.app_context()) + + return ctx.invoke(f, *args, **kwargs) + + return update_wrapper(decorator, f) # type: ignore[return-value] + + +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( # type: ignore[override] + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], click.Command]: + """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: t.Callable[..., t.Any]) -> click.Command: + if wrap_for_ctx: + f = with_appcontext(f) + return super(AppGroup, self).command(*args, **kwargs)(f) # type: ignore[no-any-return] + + return decorator + + def group( # type: ignore[override] + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], click.Group]: + """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 super().group(*args, **kwargs) # type: ignore[no-any-return] + + +def _set_app(ctx: click.Context, param: click.Option, value: str | None) -> str | None: + if value is None: + return None + + info = ctx.ensure_object(ScriptInfo) + info.app_import_path = value + return value + + +# This option is eager so the app will be available if --help is given. +# --help is also eager, so --app must be before it in the param list. +# no_args_is_help bypasses eager processing, so this option must be +# processed manually in that case to ensure FLASK_APP gets picked up. +_app_option = click.Option( + ["-A", "--app"], + metavar="IMPORT", + help=( + "The Flask application or factory function to load, in the form 'module:name'." + " Module can be a dotted import or file path. Name is not required if it is" + " 'app', 'application', 'create_app', or 'make_app', and can be 'name(args)' to" + " pass arguments." + ), + is_eager=True, + expose_value=False, + callback=_set_app, +) + + +def _set_debug(ctx: click.Context, param: click.Option, value: bool) -> bool | None: + # If the flag isn't provided, it will default to False. Don't use + # that, let debug be set by env in that case. + source = ctx.get_parameter_source(param.name) # type: ignore[arg-type] + + if source is not None and source in ( + ParameterSource.DEFAULT, + ParameterSource.DEFAULT_MAP, + ): + return None + + # Set with env var instead of ScriptInfo.load so that it can be + # accessed early during a factory function. + os.environ["FLASK_DEBUG"] = "1" if value else "0" + return value + + +_debug_option = click.Option( + ["--debug/--no-debug"], + help="Set debug mode.", + expose_value=False, + callback=_set_debug, +) + + +def _env_file_callback( + ctx: click.Context, param: click.Option, value: str | None +) -> str | None: + try: + import dotenv # noqa: F401 + except ImportError: + # Only show an error if a value was passed, otherwise we still want to + # call load_dotenv and show a message without exiting. + if value is not None: + raise click.BadParameter( + "python-dotenv must be installed to load an env file.", + ctx=ctx, + param=param, + ) from None + + # Load if a value was passed, or we want to load default files, or both. + if value is not None or ctx.obj.load_dotenv_defaults: + load_dotenv(value, load_defaults=ctx.obj.load_dotenv_defaults) + + return value + + +# This option is eager so env vars are loaded as early as possible to be +# used by other options. +_env_file_option = click.Option( + ["-e", "--env-file"], + type=click.Path(exists=True, dir_okay=False), + help=( + "Load environment variables from this file, taking precedence over" + " those set by '.env' and '.flaskenv'. Variables set directly in the" + " environment take highest precedence. python-dotenv must be installed." + ), + is_eager=True, + expose_value=False, + callback=_env_file_callback, +) + + +class FlaskGroup(AppGroup): + """Special subclass of the :class:`AppGroup` group that supports + loading more commands from the configured Flask app. Normally a + developer does not have to interface with this class but there are + some very advanced use cases for which it makes sense to create an + instance of this. see :ref:`custom-scripts`. + + :param add_default_commands: if this is True then the default run and + shell commands will be added. + :param add_version_option: adds the ``--version`` option. + :param create_app: an optional callback that is passed the script info and + returns the loaded app. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param set_debug_flag: Set the app's debug flag. + + .. versionchanged:: 3.1 + ``-e path`` takes precedence over default ``.env`` and ``.flaskenv`` files. + + .. versionchanged:: 2.2 + Added the ``-A/--app``, ``--debug/--no-debug``, ``-e/--env-file`` options. + + .. versionchanged:: 2.2 + An app context is pushed when running ``app.cli`` commands, so + ``@with_appcontext`` is no longer required for those commands. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment variables + from :file:`.env` and :file:`.flaskenv` files. + """ + + def __init__( + self, + add_default_commands: bool = True, + create_app: t.Callable[..., Flask] | None = None, + add_version_option: bool = True, + load_dotenv: bool = True, + set_debug_flag: bool = True, + **extra: t.Any, + ) -> None: + params: list[click.Parameter] = list(extra.pop("params", None) or ()) + # Processing is done with option callbacks instead of a group + # callback. This allows users to make a custom group callback + # without losing the behavior. --env-file must come first so + # that it is eagerly evaluated before --app. + params.extend((_env_file_option, _app_option, _debug_option)) + + if add_version_option: + params.append(version_option) + + if "context_settings" not in extra: + extra["context_settings"] = {} + + extra["context_settings"].setdefault("auto_envvar_prefix", "FLASK") + + super().__init__(params=params, **extra) + + self.create_app = create_app + self.load_dotenv = load_dotenv + self.set_debug_flag = set_debug_flag + + if add_default_commands: + self.add_command(run_command) + self.add_command(shell_command) + self.add_command(routes_command) + + self._loaded_plugin_commands = False + + def _load_plugin_commands(self) -> None: + if self._loaded_plugin_commands: + return + + if sys.version_info >= (3, 10): + from importlib import metadata + else: + # Use a backport on Python < 3.10. We technically have + # importlib.metadata on 3.8+, but the API changed in 3.10, + # so use the backport for consistency. + import importlib_metadata as metadata # pyright: ignore + + for ep in metadata.entry_points(group="flask.commands"): + self.add_command(ep.load(), ep.name) + + self._loaded_plugin_commands = True + + def get_command(self, ctx: click.Context, name: str) -> click.Command | None: + self._load_plugin_commands() + # Look up built-in and plugin commands, which should be + # available even if the app fails to load. + rv = super().get_command(ctx, name) + + if rv is not None: + return rv + + info = ctx.ensure_object(ScriptInfo) + + # Look up commands provided by the app, showing an error and + # continuing if the app couldn't be loaded. + try: + app = info.load_app() + except NoAppException as e: + click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") + return None + + # Push an app context for the loaded app unless it is already + # active somehow. This makes the context available to parameter + # and command callbacks without needing @with_appcontext. + if not current_app or current_app._get_current_object() is not app: # type: ignore[attr-defined] + ctx.with_resource(app.app_context()) + + return app.cli.get_command(ctx, name) + + def list_commands(self, ctx: click.Context) -> list[str]: + self._load_plugin_commands() + # Start with the built-in and plugin commands. + rv = set(super().list_commands(ctx)) + info = ctx.ensure_object(ScriptInfo) + + # Add commands provided by the app, showing an error and + # continuing if the app couldn't be loaded. + try: + rv.update(info.load_app().cli.list_commands(ctx)) + except NoAppException as e: + # When an app couldn't be loaded, show the error message + # without the traceback. + click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") + except Exception: + # When any other errors occurred during loading, show the + # full traceback. + click.secho(f"{traceback.format_exc()}\n", err=True, fg="red") + + return sorted(rv) + + def make_context( + self, + info_name: str | None, + args: list[str], + parent: click.Context | None = None, + **extra: t.Any, + ) -> click.Context: + # Set a flag to tell app.run to become a no-op. If app.run was + # not in a __name__ == __main__ guard, it would start the server + # when importing, blocking whatever command is being called. + os.environ["FLASK_RUN_FROM_CLI"] = "true" + + if "obj" not in extra and "obj" not in self.context_settings: + extra["obj"] = ScriptInfo( + create_app=self.create_app, + set_debug_flag=self.set_debug_flag, + load_dotenv_defaults=self.load_dotenv, + ) + + return super().make_context(info_name, args, parent=parent, **extra) + + def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]: + if (not args and self.no_args_is_help) or ( + len(args) == 1 and args[0] in self.get_help_option_names(ctx) + ): + # Attempt to load --env-file and --app early in case they + # were given as env vars. Otherwise no_args_is_help will not + # see commands from app.cli. + _env_file_option.handle_parse_result(ctx, {}, []) + _app_option.handle_parse_result(ctx, {}, []) + + return super().parse_args(ctx, args) + + +def _path_is_ancestor(path: str, other: str) -> bool: + """Take ``other`` and remove the length of ``path`` from it. Then join it + to ``path``. If it is the original value, ``path`` is an ancestor of + ``other``.""" + return os.path.join(path, other[len(path) :].lstrip(os.sep)) == other + + +def load_dotenv( + path: str | os.PathLike[str] | None = None, load_defaults: bool = True +) -> bool: + """Load "dotenv" files to set environment variables. A given path takes + precedence over ``.env``, which takes precedence over ``.flaskenv``. After + loading and combining these files, values are only set if the key is not + already set in ``os.environ``. + + 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. + :param load_defaults: Search for and load the default ``.flaskenv`` and + ``.env`` files. + :return: ``True`` if at least one env var was loaded. + + .. versionchanged:: 3.1 + Added the ``load_defaults`` parameter. A given path takes precedence + over default files. + + .. versionchanged:: 2.0 + The current directory is not changed to the location of the + loaded file. + + .. versionchanged:: 2.0 + When loading the env files, set the default encoding to UTF-8. + + .. versionchanged:: 1.1.0 + Returns ``False`` when python-dotenv is not installed, or when + the given path isn't a file. + + .. versionadded:: 1.0 + """ + try: + import dotenv + except ImportError: + if path or os.path.isfile(".env") or os.path.isfile(".flaskenv"): + click.secho( + " * Tip: There are .env files present. Install python-dotenv" + " to use them.", + fg="yellow", + err=True, + ) + + return False + + data: dict[str, str | None] = {} + + if load_defaults: + for default_name in (".flaskenv", ".env"): + if not (default_path := dotenv.find_dotenv(default_name, usecwd=True)): + continue + + data |= dotenv.dotenv_values(default_path, encoding="utf-8") + + if path is not None and os.path.isfile(path): + data |= dotenv.dotenv_values(path, encoding="utf-8") + + for key, value in data.items(): + if key in os.environ or value is None: + continue + + os.environ[key] = value + + return bool(data) # True if at least one env var was loaded. + + +def show_server_banner(debug: bool, app_import_path: str | None) -> None: + """Show extra startup messages the first time the server is run, + ignoring the reloader. + """ + if is_running_from_reloader(): + return + + if app_import_path is not None: + click.echo(f" * Serving Flask app '{app_import_path}'") + + if debug is not None: + click.echo(f" * Debug mode: {'on' if debug else 'off'}") + + +class CertParamType(click.ParamType): + """Click option type for the ``--cert`` option. Allows either an + existing file, the string ``'adhoc'``, or an import for a + :class:`~ssl.SSLContext` object. + """ + + name = "path" + + def __init__(self) -> None: + self.path_type = click.Path(exists=True, dir_okay=False, resolve_path=True) + + def convert( + self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None + ) -> t.Any: + try: + import ssl + except ImportError: + raise click.BadParameter( + 'Using "--cert" requires Python to be compiled with SSL support.', + ctx, + param, + ) from None + + try: + return self.path_type(value, param, ctx) + except click.BadParameter: + value = click.STRING(value, param, ctx).lower() + + if value == "adhoc": + try: + import cryptography # noqa: F401 + except ImportError: + raise click.BadParameter( + "Using ad-hoc certificates requires the cryptography library.", + ctx, + param, + ) from None + + return value + + obj = import_string(value, silent=True) + + if isinstance(obj, ssl.SSLContext): + return obj + + raise + + +def _validate_key(ctx: click.Context, param: click.Parameter, value: t.Any) -> t.Any: + """The ``--key`` option must be specified when ``--cert`` is a file. + Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed. + """ + cert = ctx.params.get("cert") + is_adhoc = cert == "adhoc" + + try: + import ssl + except ImportError: + is_context = False + else: + is_context = isinstance(cert, ssl.SSLContext) + + if value is not None: + if is_adhoc: + raise click.BadParameter( + 'When "--cert" is "adhoc", "--key" is not used.', ctx, param + ) + + if is_context: + raise click.BadParameter( + 'When "--cert" is an SSLContext object, "--key" is not used.', + ctx, + param, + ) + + if not cert: + raise click.BadParameter('"--cert" must also be specified.', ctx, param) + + ctx.params["cert"] = cert, value + + else: + if cert and not (is_adhoc or is_context): + raise click.BadParameter('Required when using "--cert".', ctx, param) + + return value + + +class SeparatedPathType(click.Path): + """Click option type that accepts a list of values separated by the + OS's path separator (``:``, ``;`` on Windows). Each value is + validated as a :class:`click.Path` type. + """ + + def convert( + self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None + ) -> t.Any: + items = self.split_envvar_value(value) + # can't call no-arg super() inside list comprehension until Python 3.12 + super_convert = super().convert + return [super_convert(item, param, ctx) for item in items] + + +@click.command("run", short_help="Run a development server.") +@click.option("--host", "-h", default="127.0.0.1", help="The interface to bind to.") +@click.option("--port", "-p", default=5000, help="The port to bind to.") +@click.option( + "--cert", + type=CertParamType(), + help="Specify a certificate file to use HTTPS.", + is_eager=True, +) +@click.option( + "--key", + type=click.Path(exists=True, dir_okay=False, resolve_path=True), + callback=_validate_key, + expose_value=False, + help="The key file to use when specifying a certificate.", +) +@click.option( + "--reload/--no-reload", + default=None, + help="Enable or disable the reloader. By default the reloader " + "is active if debug is enabled.", +) +@click.option( + "--debugger/--no-debugger", + default=None, + help="Enable or disable the debugger. By default the debugger " + "is active if debug is enabled.", +) +@click.option( + "--with-threads/--without-threads", + default=True, + help="Enable or disable multithreading.", +) +@click.option( + "--extra-files", + default=None, + type=SeparatedPathType(), + help=( + "Extra files that trigger a reload on change. Multiple paths" + f" are separated by {os.path.pathsep!r}." + ), +) +@click.option( + "--exclude-patterns", + default=None, + type=SeparatedPathType(), + help=( + "Files matching these fnmatch patterns will not trigger a reload" + " on change. Multiple patterns are separated by" + f" {os.path.pathsep!r}." + ), +) +@pass_script_info +def run_command( + info: ScriptInfo, + host: str, + port: int, + reload: bool, + debugger: bool, + with_threads: bool, + cert: ssl.SSLContext | tuple[str, str | None] | t.Literal["adhoc"] | None, + extra_files: list[str] | None, + exclude_patterns: list[str] | None, +) -> None: + """Run a local development server. + + This server is for development purposes only. It does not provide + the stability, security, or performance of production WSGI servers. + + The reloader and debugger are enabled by default with the '--debug' + option. + """ + try: + app: WSGIApplication = info.load_app() # pyright: ignore + except Exception as e: + if is_running_from_reloader(): + # When reloading, print out the error immediately, but raise + # it later so the debugger or server can handle it. + traceback.print_exc() + err = e + + def app( + environ: WSGIEnvironment, start_response: StartResponse + ) -> cabc.Iterable[bytes]: + raise err from None + + else: + # When not reloading, raise the error immediately so the + # command fails. + raise e from None + + debug = get_debug_flag() + + if reload is None: + reload = debug + + if debugger is None: + debugger = debug + + show_server_banner(debug, info.app_import_path) + + run_simple( + host, + port, + app, + use_reloader=reload, + use_debugger=debugger, + threaded=with_threads, + ssl_context=cert, + extra_files=extra_files, + exclude_patterns=exclude_patterns, + ) + + +run_command.params.insert(0, _debug_option) + + +@click.command("shell", short_help="Run a shell in the app context.") +@with_appcontext +def shell_command() -> None: + """Run an interactive Python shell in the context of a given + Flask application. The application will populate the default + namespace of this shell according to its configuration. + + This is useful for executing small snippets of management code + without having to manually configure the application. + """ + import code + + banner = ( + f"Python {sys.version} on {sys.platform}\n" + f"App: {current_app.import_name}\n" + f"Instance: {current_app.instance_path}" + ) + ctx: dict[str, t.Any] = {} + + # Support the regular Python interpreter startup script if someone + # is using it. + startup = os.environ.get("PYTHONSTARTUP") + if startup and os.path.isfile(startup): + with open(startup) as f: + eval(compile(f.read(), startup, "exec"), ctx) + + ctx.update(current_app.make_shell_context()) + + # Site, customize, or startup script can set a hook to call when + # entering interactive mode. The default one sets up readline with + # tab and history completion. + interactive_hook = getattr(sys, "__interactivehook__", None) + + if interactive_hook is not None: + try: + import readline + from rlcompleter import Completer + except ImportError: + pass + else: + # rlcompleter uses __main__.__dict__ by default, which is + # flask.__main__. Use the shell context instead. + readline.set_completer(Completer(ctx).complete) + + interactive_hook() + + code.interact(banner=banner, local=ctx) + + +@click.command("routes", short_help="Show the routes for the app.") +@click.option( + "--sort", + "-s", + type=click.Choice(("endpoint", "methods", "domain", "rule", "match")), + default="endpoint", + help=( + "Method to sort routes by. 'match' is the order that Flask will match routes" + " when dispatching a request." + ), +) +@click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.") +@with_appcontext +def routes_command(sort: str, all_methods: bool) -> None: + """Show all registered routes with endpoints and methods.""" + rules = list(current_app.url_map.iter_rules()) + + if not rules: + click.echo("No routes were registered.") + return + + ignored_methods = set() if all_methods else {"HEAD", "OPTIONS"} + host_matching = current_app.url_map.host_matching + has_domain = any(rule.host if host_matching else rule.subdomain for rule in rules) + rows = [] + + for rule in rules: + row = [ + rule.endpoint, + ", ".join(sorted((rule.methods or set()) - ignored_methods)), + ] + + if has_domain: + row.append((rule.host if host_matching else rule.subdomain) or "") + + row.append(rule.rule) + rows.append(row) + + headers = ["Endpoint", "Methods"] + sorts = ["endpoint", "methods"] + + if has_domain: + headers.append("Host" if host_matching else "Subdomain") + sorts.append("domain") + + headers.append("Rule") + sorts.append("rule") + + try: + rows.sort(key=itemgetter(sorts.index(sort))) + except ValueError: + pass + + rows.insert(0, headers) + widths = [max(len(row[i]) for row in rows) for i in range(len(headers))] + rows.insert(1, ["-" * w for w in widths]) + template = " ".join(f"{{{i}:<{w}}}" for i, w in enumerate(widths)) + + for row in rows: + click.echo(template.format(*row)) + + +cli = FlaskGroup( + name="flask", + help="""\ +A general utility script for Flask applications. + +An application to load must be given with the '--app' option, +'FLASK_APP' environment variable, or with a 'wsgi.py' or 'app.py' file +in the current directory. +""", +) + + +def main() -> None: + cli.main() + + +if __name__ == "__main__": + main() diff --git a/venv/Lib/site-packages/flask/config.py b/venv/Lib/site-packages/flask/config.py new file mode 100644 index 0000000..34ef1a5 --- /dev/null +++ b/venv/Lib/site-packages/flask/config.py @@ -0,0 +1,367 @@ +from __future__ import annotations + +import errno +import json +import os +import types +import typing as t + +from werkzeug.utils import import_string + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .sansio.app import App + + +T = t.TypeVar("T") + + +class ConfigAttribute(t.Generic[T]): + """Makes an attribute forward to the config""" + + def __init__( + self, name: str, get_converter: t.Callable[[t.Any], T] | None = None + ) -> None: + self.__name__ = name + self.get_converter = get_converter + + @t.overload + def __get__(self, obj: None, owner: None) -> te.Self: ... + + @t.overload + def __get__(self, obj: App, owner: type[App]) -> T: ... + + def __get__(self, obj: App | None, owner: type[App] | None = None) -> T | te.Self: + 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 # type: ignore[no-any-return] + + def __set__(self, obj: App, value: t.Any) -> None: + obj.config[self.__name__] = value + + +class Config(dict): # type: ignore[type-arg] + """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 | os.PathLike[str], + defaults: dict[str, t.Any] | None = None, + ) -> None: + super().__init__(defaults or {}) + self.root_path = root_path + + def from_envvar(self, variable_name: str, silent: bool = False) -> bool: + """Loads a configuration from an environment variable pointing to + a configuration file. This is basically just a shortcut with nicer + error messages for this line of code:: + + app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) + + :param variable_name: name of the environment variable + :param silent: set to ``True`` if you want silent failure for missing + files. + :return: ``True`` if the file was loaded successfully. + """ + rv = os.environ.get(variable_name) + if not rv: + if silent: + return False + raise RuntimeError( + f"The environment variable {variable_name!r} is not set" + " and as such configuration could not be loaded. Set" + " this variable and make it point to a configuration" + " file" + ) + return self.from_pyfile(rv, silent=silent) + + def from_prefixed_env( + self, prefix: str = "FLASK", *, loads: t.Callable[[str], t.Any] = json.loads + ) -> bool: + """Load any environment variables that start with ``FLASK_``, + dropping the prefix from the env key for the config key. Values + are passed through a loading function to attempt to convert them + to more specific types than strings. + + Keys are loaded in :func:`sorted` order. + + The default loading function attempts to parse values as any + valid JSON type, including dicts and lists. + + Specific items in nested dicts can be set by separating the + keys with double underscores (``__``). If an intermediate key + doesn't exist, it will be initialized to an empty dict. + + :param prefix: Load env vars that start with this prefix, + separated with an underscore (``_``). + :param loads: Pass each string value to this function and use + the returned value as the config value. If any error is + raised it is ignored and the value remains a string. The + default is :func:`json.loads`. + + .. versionadded:: 2.1 + """ + prefix = f"{prefix}_" + + for key in sorted(os.environ): + if not key.startswith(prefix): + continue + + value = os.environ[key] + key = key.removeprefix(prefix) + + try: + value = loads(value) + except Exception: + # Keep the value as a string if loading failed. + pass + + if "__" not in key: + # A non-nested key, set directly. + self[key] = value + continue + + # Traverse nested dictionaries with keys separated by "__". + current = self + *parts, tail = key.split("__") + + for part in parts: + # If an intermediate dict does not exist, create it. + if part not in current: + current[part] = {} + + current = current[part] + + current[tail] = value + + return True + + def from_pyfile( + self, filename: str | os.PathLike[str], silent: bool = False + ) -> bool: + """Updates the values in the config from a Python file. This function + behaves as if the file was imported as module with the + :meth:`from_object` function. + + :param filename: the filename of the config. This can either be an + absolute filename or a filename relative to the + root path. + :param silent: set to ``True`` if you want silent failure for missing + files. + :return: ``True`` if the file was loaded successfully. + + .. versionadded:: 0.7 + `silent` parameter. + """ + filename = os.path.join(self.root_path, filename) + d = types.ModuleType("config") + d.__file__ = filename + try: + with open(filename, mode="rb") as config_file: + exec(compile(config_file.read(), filename, "exec"), d.__dict__) + except OSError as e: + if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR): + return False + e.strerror = f"Unable to load configuration file ({e.strerror})" + raise + self.from_object(d) + return True + + def from_object(self, obj: object | str) -> None: + """Updates the values from the given object. An object can be of one + of the following two types: + + - a string: in this case the object with that name will be imported + - an actual object reference: that object is used directly + + Objects are usually either modules or classes. :meth:`from_object` + loads only the uppercase attributes of the module/class. A ``dict`` + object will not work with :meth:`from_object` because the keys of a + ``dict`` are not attributes of the ``dict`` class. + + Example of module-based configuration:: + + app.config.from_object('yourapplication.default_config') + from yourapplication import default_config + app.config.from_object(default_config) + + Nothing is done to the object before loading. If the object is a + class and has ``@property`` attributes, it needs to be + instantiated before being passed to this method. + + You should not use this function to load the actual configuration but + rather configuration defaults. The actual config should be loaded + with :meth:`from_pyfile` and ideally from a location not within the + package because the package might be installed system wide. + + See :ref:`config-dev-prod` for an example of class-based configuration + using :meth:`from_object`. + + :param obj: an import name or object + """ + if isinstance(obj, str): + obj = import_string(obj) + for key in dir(obj): + if key.isupper(): + self[key] = getattr(obj, key) + + def from_file( + self, + filename: str | os.PathLike[str], + load: t.Callable[[t.IO[t.Any]], t.Mapping[str, t.Any]], + silent: bool = False, + text: bool = True, + ) -> bool: + """Update the values in the config from a file that is loaded + using the ``load`` parameter. The loaded data is passed to the + :meth:`from_mapping` method. + + .. code-block:: python + + import json + app.config.from_file("config.json", load=json.load) + + import tomllib + app.config.from_file("config.toml", load=tomllib.load, text=False) + + :param filename: The path to the data file. This can be an + absolute path or relative to the config root path. + :param load: A callable that takes a file handle and returns a + mapping of loaded data from the file. + :type load: ``Callable[[Reader], Mapping]`` where ``Reader`` + implements a ``read`` method. + :param silent: Ignore the file if it doesn't exist. + :param text: Open the file in text or binary mode. + :return: ``True`` if the file was loaded successfully. + + .. versionchanged:: 2.3 + The ``text`` parameter was added. + + .. versionadded:: 2.0 + """ + filename = os.path.join(self.root_path, filename) + + try: + with open(filename, "r" if text else "rb") as f: + obj = load(f) + except OSError as e: + if silent and e.errno in (errno.ENOENT, errno.EISDIR): + return False + + e.strerror = f"Unable to load configuration file ({e.strerror})" + raise + + return self.from_mapping(obj) + + def from_mapping( + self, mapping: t.Mapping[str, t.Any] | None = None, **kwargs: t.Any + ) -> bool: + """Updates the config like :meth:`update` ignoring items with + non-upper keys. + + :return: Always returns ``True``. + + .. versionadded:: 0.11 + """ + mappings: dict[str, t.Any] = {} + if mapping is not None: + mappings.update(mapping) + mappings.update(kwargs) + for key, value in mappings.items(): + if key.isupper(): + self[key] = value + return True + + def get_namespace( + self, namespace: str, lowercase: bool = True, trim_namespace: bool = True + ) -> dict[str, t.Any]: + """Returns a dictionary containing a subset of configuration options + that match the specified namespace/prefix. Example usage:: + + app.config['IMAGE_STORE_TYPE'] = 'fs' + app.config['IMAGE_STORE_PATH'] = '/var/app/images' + app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com' + image_store_config = app.config.get_namespace('IMAGE_STORE_') + + The resulting dictionary `image_store_config` would look like:: + + { + 'type': 'fs', + 'path': '/var/app/images', + 'base_url': 'http://img.website.com' + } + + This is often useful when configuration options map directly to + keyword arguments in functions or class constructors. + + :param namespace: a configuration namespace + :param lowercase: a flag indicating if the keys of the resulting + dictionary should be lowercase + :param trim_namespace: a flag indicating if the keys of the resulting + dictionary should not include the namespace + + .. versionadded:: 0.11 + """ + rv = {} + for k, v in self.items(): + if not k.startswith(namespace): + continue + if trim_namespace: + key = k[len(namespace) :] + else: + key = k + if lowercase: + key = key.lower() + rv[key] = v + return rv + + def __repr__(self) -> str: + return f"<{type(self).__name__} {dict.__repr__(self)}>" diff --git a/venv/Lib/site-packages/flask/ctx.py b/venv/Lib/site-packages/flask/ctx.py new file mode 100644 index 0000000..5f7b1f1 --- /dev/null +++ b/venv/Lib/site-packages/flask/ctx.py @@ -0,0 +1,459 @@ +from __future__ import annotations + +import contextvars +import sys +import typing as t +from functools import update_wrapper +from types import TracebackType + +from werkzeug.exceptions import HTTPException + +from . import typing as ft +from .globals import _cv_app +from .globals import _cv_request +from .signals import appcontext_popped +from .signals import appcontext_pushed + +if t.TYPE_CHECKING: # pragma: no cover + from _typeshed.wsgi import WSGIEnvironment + + from .app import Flask + from .sessions import SessionMixin + from .wrappers import Request + + +# a singleton sentinel value for parameter defaults +_sentinel = object() + + +class _AppCtxGlobals: + """A plain object. Used as a namespace for storing data during an + application context. + + Creating an app context automatically creates this object, which is + made available as the :data:`g` proxy. + + .. describe:: 'key' in g + + Check whether an attribute is present. + + .. versionadded:: 0.10 + + .. describe:: iter(g) + + Return an iterator over the attribute names. + + .. versionadded:: 0.10 + """ + + # Define attr methods to let mypy know this is a namespace object + # that has arbitrary attributes. + + def __getattr__(self, name: str) -> t.Any: + try: + return self.__dict__[name] + except KeyError: + raise AttributeError(name) from None + + def __setattr__(self, name: str, value: t.Any) -> None: + self.__dict__[name] = value + + def __delattr__(self, name: str) -> None: + try: + del self.__dict__[name] + except KeyError: + raise AttributeError(name) from None + + def get(self, name: str, default: t.Any | None = None) -> t.Any: + """Get an attribute by name, or a default value. Like + :meth:`dict.get`. + + :param name: Name of attribute to get. + :param default: Value to return if the attribute is not present. + + .. versionadded:: 0.10 + """ + return self.__dict__.get(name, default) + + def pop(self, name: str, default: t.Any = _sentinel) -> t.Any: + """Get and remove an attribute by name. Like :meth:`dict.pop`. + + :param name: Name of attribute to pop. + :param default: Value to return if the attribute is not present, + instead of raising a ``KeyError``. + + .. versionadded:: 0.11 + """ + if default is _sentinel: + return self.__dict__.pop(name) + else: + return self.__dict__.pop(name, default) + + def setdefault(self, name: str, default: t.Any = None) -> t.Any: + """Get the value of an attribute if it is present, otherwise + set and return a default value. Like :meth:`dict.setdefault`. + + :param name: Name of attribute to get. + :param default: Value to set and return if the attribute is not + present. + + .. versionadded:: 0.11 + """ + return self.__dict__.setdefault(name, default) + + def __contains__(self, item: str) -> bool: + return item in self.__dict__ + + def __iter__(self) -> t.Iterator[str]: + return iter(self.__dict__) + + def __repr__(self) -> str: + ctx = _cv_app.get(None) + if ctx is not None: + return f"" + return object.__repr__(self) + + +def after_this_request( + f: ft.AfterRequestCallable[t.Any], +) -> ft.AfterRequestCallable[t.Any]: + """Executes a function after this request. This is useful to modify + response objects. The function is passed the response object and has + to return the same or a new one. + + Example:: + + @app.route('/') + def index(): + @after_this_request + def add_header(response): + response.headers['X-Foo'] = 'Parachute' + return response + return 'Hello World!' + + This is more useful if a function other than the view function wants to + modify a response. For instance think of a decorator that wants to add + some headers without converting the return value into a response object. + + .. versionadded:: 0.9 + """ + ctx = _cv_request.get(None) + + if ctx is None: + raise RuntimeError( + "'after_this_request' can only be used when a request" + " context is active, such as in a view function." + ) + + ctx._after_request_functions.append(f) + return f + + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def copy_current_request_context(f: F) -> F: + """A helper function that decorates a function to retain the current + request context. This is useful when working with greenlets. The moment + the function is decorated a copy of the request context is created and + then pushed when the function is called. The current session is also + included in the copied request context. + + Example:: + + import gevent + from flask import copy_current_request_context + + @app.route('/') + def index(): + @copy_current_request_context + def do_some_work(): + # do some work here, it can access flask.request or + # flask.session like you would otherwise in the view function. + ... + gevent.spawn(do_some_work) + return 'Regular response' + + .. versionadded:: 0.10 + """ + ctx = _cv_request.get(None) + + if ctx is None: + raise RuntimeError( + "'copy_current_request_context' can only be used when a" + " request context is active, such as in a view function." + ) + + ctx = ctx.copy() + + def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any: + with ctx: + return ctx.app.ensure_sync(f)(*args, **kwargs) + + return update_wrapper(wrapper, f) # type: ignore[return-value] + + +def has_request_context() -> bool: + """If you have code that wants to test if a request context is there or + not this function can be used. For instance, you may want to take advantage + of request information if the request object is available, but fail + silently if it is unavailable. + + :: + + class User(db.Model): + + def __init__(self, username, remote_addr=None): + self.username = username + if remote_addr is None and has_request_context(): + remote_addr = request.remote_addr + self.remote_addr = remote_addr + + Alternatively you can also just test any of the context bound objects + (such as :class:`request` or :class:`g`) for truthness:: + + class User(db.Model): + + def __init__(self, username, remote_addr=None): + self.username = username + if remote_addr is None and request: + remote_addr = request.remote_addr + self.remote_addr = remote_addr + + .. versionadded:: 0.7 + """ + return _cv_request.get(None) is not None + + +def has_app_context() -> bool: + """Works like :func:`has_request_context` but for the application + context. You can also just do a boolean check on the + :data:`current_app` object instead. + + .. versionadded:: 0.9 + """ + return _cv_app.get(None) is not None + + +class AppContext: + """The app context contains application-specific information. An app + context is created and pushed at the beginning of each request if + one is not already active. An app context is also pushed when + running CLI commands. + """ + + def __init__(self, app: Flask) -> None: + self.app = app + self.url_adapter = app.create_url_adapter(None) + self.g: _AppCtxGlobals = app.app_ctx_globals_class() + self._cv_tokens: list[contextvars.Token[AppContext]] = [] + + def push(self) -> None: + """Binds the app context to the current context.""" + self._cv_tokens.append(_cv_app.set(self)) + appcontext_pushed.send(self.app, _async_wrapper=self.app.ensure_sync) + + def pop(self, exc: BaseException | None = _sentinel) -> None: # type: ignore + """Pops the app context.""" + try: + if len(self._cv_tokens) == 1: + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_appcontext(exc) + finally: + ctx = _cv_app.get() + _cv_app.reset(self._cv_tokens.pop()) + + if ctx is not self: + raise AssertionError( + f"Popped wrong app context. ({ctx!r} instead of {self!r})" + ) + + appcontext_popped.send(self.app, _async_wrapper=self.app.ensure_sync) + + def __enter__(self) -> AppContext: + self.push() + return self + + def __exit__( + self, + exc_type: type | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.pop(exc_value) + + +class RequestContext: + """The request context contains per-request information. The Flask + app creates and pushes it at the beginning of the request, then pops + it at the end of the request. It will create the URL adapter and + request object for the WSGI environment provided. + + Do not attempt to use this class directly, instead use + :meth:`~flask.Flask.test_request_context` and + :meth:`~flask.Flask.request_context` to create this object. + + When the request context is popped, it will evaluate all the + functions registered on the application for teardown execution + (:meth:`~flask.Flask.teardown_request`). + + The request context is automatically popped at the end of the + request. When using the interactive debugger, the context will be + restored so ``request`` is still accessible. Similarly, the test + client can preserve the context after the request ends. However, + teardown functions may already have closed some resources such as + database connections. + """ + + def __init__( + self, + app: Flask, + environ: WSGIEnvironment, + request: Request | None = None, + session: SessionMixin | None = None, + ) -> None: + self.app = app + if request is None: + request = app.request_class(environ) + request.json_module = app.json + self.request: Request = request + self.url_adapter = None + try: + self.url_adapter = app.create_url_adapter(self.request) + except HTTPException as e: + self.request.routing_exception = e + self.flashes: list[tuple[str, str]] | None = None + self._session: SessionMixin | None = session + # Functions that should be executed after the request on the response + # object. These will be called before the regular "after_request" + # functions. + self._after_request_functions: list[ft.AfterRequestCallable[t.Any]] = [] + + self._cv_tokens: list[ + tuple[contextvars.Token[RequestContext], AppContext | None] + ] = [] + + def copy(self) -> RequestContext: + """Creates a copy of this request context with the same request object. + This can be used to move a request context to a different greenlet. + Because the actual request object is the same this cannot be used to + move a request context to a different thread unless access to the + request object is locked. + + .. versionadded:: 0.10 + + .. versionchanged:: 1.1 + The current session object is used instead of reloading the original + data. This prevents `flask.session` pointing to an out-of-date object. + """ + return self.__class__( + self.app, + environ=self.request.environ, + request=self.request, + session=self._session, + ) + + def match_request(self) -> None: + """Can be overridden by a subclass to hook into the matching + of the request. + """ + try: + result = self.url_adapter.match(return_rule=True) # type: ignore + self.request.url_rule, self.request.view_args = result # type: ignore + except HTTPException as e: + self.request.routing_exception = e + + @property + def session(self) -> SessionMixin: + """The session data associated with this request. Not available until + this context has been pushed. Accessing this property, also accessed by + the :data:`~flask.session` proxy, sets :attr:`.SessionMixin.accessed`. + """ + assert self._session is not None, "The session has not yet been opened." + self._session.accessed = True + return self._session + + def push(self) -> None: + # Before we push the request context we have to ensure that there + # is an application context. + app_ctx = _cv_app.get(None) + + if app_ctx is None or app_ctx.app is not self.app: + app_ctx = self.app.app_context() + app_ctx.push() + else: + app_ctx = None + + self._cv_tokens.append((_cv_request.set(self), app_ctx)) + + # Open the session at the moment that the request context is available. + # This allows a custom open_session method to use the request context. + # Only open a new session if this is the first time the request was + # pushed, otherwise stream_with_context loses the session. + if self._session is None: + session_interface = self.app.session_interface + self._session = session_interface.open_session(self.app, self.request) + + if self._session is None: + self._session = session_interface.make_null_session(self.app) + + # Match the request URL after loading the session, so that the + # session is available in custom URL converters. + if self.url_adapter is not None: + self.match_request() + + def pop(self, exc: BaseException | None = _sentinel) -> None: # type: ignore + """Pops the request context and unbinds it by doing that. This will + also trigger the execution of functions registered by the + :meth:`~flask.Flask.teardown_request` decorator. + + .. versionchanged:: 0.9 + Added the `exc` argument. + """ + clear_request = len(self._cv_tokens) == 1 + + try: + if clear_request: + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_request(exc) + + request_close = getattr(self.request, "close", None) + if request_close is not None: + request_close() + finally: + ctx = _cv_request.get() + token, app_ctx = self._cv_tokens.pop() + _cv_request.reset(token) + + # get rid of circular dependencies at the end of the request + # so that we don't require the GC to be active. + if clear_request: + ctx.request.environ["werkzeug.request"] = None + + if app_ctx is not None: + app_ctx.pop(exc) + + if ctx is not self: + raise AssertionError( + f"Popped wrong request context. ({ctx!r} instead of {self!r})" + ) + + def __enter__(self) -> RequestContext: + self.push() + return self + + def __exit__( + self, + exc_type: type | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.pop(exc_value) + + def __repr__(self) -> str: + return ( + f"<{type(self).__name__} {self.request.url!r}" + f" [{self.request.method}] of {self.app.name}>" + ) diff --git a/venv/Lib/site-packages/flask/debughelpers.py b/venv/Lib/site-packages/flask/debughelpers.py new file mode 100644 index 0000000..2c8c4c4 --- /dev/null +++ b/venv/Lib/site-packages/flask/debughelpers.py @@ -0,0 +1,178 @@ +from __future__ import annotations + +import typing as t + +from jinja2.loaders import BaseLoader +from werkzeug.routing import RequestRedirect + +from .blueprints import Blueprint +from .globals import request_ctx +from .sansio.app import App + +if t.TYPE_CHECKING: + from .sansio.scaffold import Scaffold + from .wrappers import Request + + +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: Request, key: str) -> None: + 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) -> str: + return self.msg + + +class FormDataRoutingRedirect(AssertionError): + """This exception is raised in debug mode if a routing redirect + would cause the browser to drop the method or body. This happens + when method is not GET, HEAD or OPTIONS and the status code is not + 307 or 308. + """ + + def __init__(self, request: Request) -> None: + exc = request.routing_exception + assert isinstance(exc, RequestRedirect) + buf = [ + f"A request was sent to '{request.url}', but routing issued" + f" a redirect to the canonical URL '{exc.new_url}'." + ] + + if f"{request.base_url}/" == exc.new_url.partition("?")[0]: + buf.append( + " The URL was defined with a trailing slash. Flask" + " will redirect to the URL with a trailing slash if it" + " was accessed without one." + ) + + buf.append( + " Send requests to the canonical URL, or use 307 or 308 for" + " routing redirects. Otherwise, browsers will drop form" + " data.\n\n" + "This exception is only raised in debug mode." + ) + super().__init__("".join(buf)) + + +def attach_enctype_error_multidict(request: Request) -> None: + """Patch ``request.files.__getitem__`` to raise a descriptive error + about ``enctype=multipart/form-data``. + + :param request: The request to patch. + :meta private: + """ + oldcls = request.files.__class__ + + class newcls(oldcls): # type: ignore[valid-type, misc] + def __getitem__(self, key: str) -> t.Any: + try: + return super().__getitem__(key) + except KeyError as e: + if key not in request.form: + raise + + raise DebugFilesKeyError(request, key).with_traceback( + e.__traceback__ + ) from None + + newcls.__name__ = oldcls.__name__ + newcls.__module__ = oldcls.__module__ + request.files.__class__ = newcls + + +def _dump_loader_info(loader: BaseLoader) -> t.Iterator[str]: + 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: App, + template: str, + attempts: list[ + tuple[ + BaseLoader, + Scaffold, + tuple[str, str | None, t.Callable[[], bool] | None] | None, + ] + ], +) -> None: + """This should help developers understand what failed""" + info = [f"Locating template {template!r}:"] + total_found = 0 + blueprint = None + if request_ctx and request_ctx.request.blueprint is not None: + blueprint = request_ctx.request.blueprint + + for idx, (loader, srcobj, triple) in enumerate(attempts): + if isinstance(srcobj, App): + src_info = f"application {srcobj.import_name!r}" + elif isinstance(srcobj, Blueprint): + src_info = f"blueprint {srcobj.name!r} ({srcobj.import_name})" + else: + src_info = repr(srcobj) + + info.append(f"{idx + 1:5}: trying loader of {src_info}") + + for line in _dump_loader_info(loader): + info.append(f" {line}") + + if triple is None: + detail = "no match" + else: + detail = f"found ({triple[1] or ''!r})" + total_found += 1 + info.append(f" -> {detail}") + + seems_fishy = False + if total_found == 0: + info.append("Error: the template could not be found.") + seems_fishy = True + elif total_found > 1: + info.append("Warning: multiple loaders returned a match for the template.") + seems_fishy = True + + if blueprint is not None and seems_fishy: + info.append( + " The template was looked up from an endpoint that belongs" + f" to the blueprint {blueprint!r}." + ) + info.append(" Maybe you did not place a template in the right folder?") + info.append(" See https://flask.palletsprojects.com/blueprints/#templates") + + app.logger.info("\n".join(info)) diff --git a/venv/Lib/site-packages/flask/globals.py b/venv/Lib/site-packages/flask/globals.py new file mode 100644 index 0000000..e2c410c --- /dev/null +++ b/venv/Lib/site-packages/flask/globals.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import typing as t +from contextvars import ContextVar + +from werkzeug.local import LocalProxy + +if t.TYPE_CHECKING: # pragma: no cover + from .app import Flask + from .ctx import _AppCtxGlobals + from .ctx import AppContext + from .ctx import RequestContext + from .sessions import SessionMixin + from .wrappers import Request + + +_no_app_msg = """\ +Working outside of application context. + +This typically means that you attempted to use functionality that needed +the current application. To solve this, set up an application context +with app.app_context(). See the documentation for more information.\ +""" +_cv_app: ContextVar[AppContext] = ContextVar("flask.app_ctx") +app_ctx: AppContext = LocalProxy( # type: ignore[assignment] + _cv_app, unbound_message=_no_app_msg +) +current_app: Flask = LocalProxy( # type: ignore[assignment] + _cv_app, "app", unbound_message=_no_app_msg +) +g: _AppCtxGlobals = LocalProxy( # type: ignore[assignment] + _cv_app, "g", unbound_message=_no_app_msg +) + +_no_req_msg = """\ +Working outside of request context. + +This typically means that you attempted to use functionality that needed +an active HTTP request. Consult the documentation on testing for +information about how to avoid this problem.\ +""" +_cv_request: ContextVar[RequestContext] = ContextVar("flask.request_ctx") +request_ctx: RequestContext = LocalProxy( # type: ignore[assignment] + _cv_request, unbound_message=_no_req_msg +) +request: Request = LocalProxy( # type: ignore[assignment] + _cv_request, "request", unbound_message=_no_req_msg +) +session: SessionMixin = LocalProxy( # type: ignore[assignment] + _cv_request, "session", unbound_message=_no_req_msg +) diff --git a/venv/Lib/site-packages/flask/helpers.py b/venv/Lib/site-packages/flask/helpers.py new file mode 100644 index 0000000..5d412c9 --- /dev/null +++ b/venv/Lib/site-packages/flask/helpers.py @@ -0,0 +1,641 @@ +from __future__ import annotations + +import importlib.util +import os +import sys +import typing as t +from datetime import datetime +from functools import cache +from functools import update_wrapper + +import werkzeug.utils +from werkzeug.exceptions import abort as _wz_abort +from werkzeug.utils import redirect as _wz_redirect +from werkzeug.wrappers import Response as BaseResponse + +from .globals import _cv_app +from .globals import _cv_request +from .globals import current_app +from .globals import request +from .globals import request_ctx +from .globals import session +from .signals import message_flashed + +if t.TYPE_CHECKING: # pragma: no cover + from .wrappers import Response + + +def get_debug_flag() -> bool: + """Get whether debug mode should be enabled for the app, indicated by the + :envvar:`FLASK_DEBUG` environment variable. The default is ``False``. + """ + val = os.environ.get("FLASK_DEBUG") + return bool(val and val.lower() not in {"0", "false", "no"}) + + +def get_load_dotenv(default: bool = True) -> bool: + """Get whether the user has disabled loading default dotenv files by + setting :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load + the files. + + :param default: What to return if the env var isn't set. + """ + val = os.environ.get("FLASK_SKIP_DOTENV") + + if not val: + return default + + return val.lower() in ("0", "false", "no") + + +@t.overload +def stream_with_context( + generator_or_function: t.Iterator[t.AnyStr], +) -> t.Iterator[t.AnyStr]: ... + + +@t.overload +def stream_with_context( + generator_or_function: t.Callable[..., t.Iterator[t.AnyStr]], +) -> t.Callable[[t.Iterator[t.AnyStr]], t.Iterator[t.AnyStr]]: ... + + +def stream_with_context( + generator_or_function: t.Iterator[t.AnyStr] | t.Callable[..., t.Iterator[t.AnyStr]], +) -> t.Iterator[t.AnyStr] | t.Callable[[t.Iterator[t.AnyStr]], t.Iterator[t.AnyStr]]: + """Wrap a response generator function so that it runs inside the current + request context. This keeps :data:`request`, :data:`session`, and :data:`g` + available, even though at the point the generator runs the request context + will typically have ended. + + Use it as a decorator on a generator function: + + .. code-block:: python + + from flask import stream_with_context, request, Response + + @app.get("/stream") + def streamed_response(): + @stream_with_context + def generate(): + yield "Hello " + yield request.args["name"] + yield "!" + + return Response(generate()) + + Or use it as a wrapper around a created generator: + + .. code-block:: python + + from flask import stream_with_context, request, Response + + @app.get("/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[arg-type] + except TypeError: + + def decorator(*args: t.Any, **kwargs: t.Any) -> t.Any: + gen = generator_or_function(*args, **kwargs) # type: ignore[operator] + return stream_with_context(gen) + + return update_wrapper(decorator, generator_or_function) # type: ignore[arg-type] + + def generator() -> t.Iterator[t.AnyStr]: + if (req_ctx := _cv_request.get(None)) is None: + raise RuntimeError( + "'stream_with_context' can only be used when a request" + " context is active, such as in a view function." + ) + + app_ctx = _cv_app.get() + # Setup code below will run the generator to this point, so that the + # current contexts are recorded. The contexts must be pushed after, + # otherwise their ContextVar will record the wrong event loop during + # async view functions. + yield None # type: ignore[misc] + + # Push the app context first, so that the request context does not + # automatically create and push a different app context. + with app_ctx, req_ctx: + try: + yield from gen + finally: + # Clean up in case the user wrapped a WSGI iterator. + if hasattr(gen, "close"): + gen.close() + + # Execute the generator to the sentinel value. This ensures the context is + # preserved in the generator's state. Further iteration will push the + # context and yield from the original iterator. + 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, + *, + _anchor: str | None = None, + _method: str | None = None, + _scheme: str | None = None, + _external: bool | None = None, + **values: t.Any, +) -> str: + """Generate a URL to the given endpoint with the given values. + + This requires an active request or application context, and calls + :meth:`current_app.url_for() `. See that method + for full documentation. + + :param endpoint: The endpoint name associated with the URL to + generate. If this starts with a ``.``, the current blueprint + name (if any) will be used. + :param _anchor: If given, append this as ``#anchor`` to the URL. + :param _method: If given, generate the URL associated with this + method for the endpoint. + :param _scheme: If given, the URL will have this scheme if it is + external. + :param _external: If given, prefer the URL to be internal (False) or + require it to be external (True). External URLs include the + scheme and domain. When not in an active request, URLs are + external by default. + :param values: Values to use for the variable parts of the URL rule. + Unknown keys are appended as query string arguments, like + ``?a=b&c=d``. + + .. versionchanged:: 2.2 + Calls ``current_app.url_for``, allowing an app to override the + behavior. + + .. versionchanged:: 0.10 + The ``_scheme`` parameter was added. + + .. versionchanged:: 0.9 + The ``_anchor`` and ``_method`` parameters were added. + + .. versionchanged:: 0.9 + Calls ``app.handle_url_build_error`` on build errors. + """ + return current_app.url_for( + endpoint, + _anchor=_anchor, + _method=_method, + _scheme=_scheme, + _external=_external, + **values, + ) + + +def redirect( + location: str, code: int = 302, Response: type[BaseResponse] | None = None +) -> BaseResponse: + """Create a redirect response object. + + If :data:`~flask.current_app` is available, it will use its + :meth:`~flask.Flask.redirect` method, otherwise it will use + :func:`werkzeug.utils.redirect`. + + :param location: The URL to redirect to. + :param code: The status code for the redirect. + :param Response: The response class to use. Not used when + ``current_app`` is active, which uses ``app.response_class``. + + .. versionadded:: 2.2 + Calls ``current_app.redirect`` if available instead of always + using Werkzeug's default ``redirect``. + """ + if current_app: + return current_app.redirect(location, code=code) + + return _wz_redirect(location, code=code, Response=Response) + + +def abort(code: int | BaseResponse, *args: t.Any, **kwargs: t.Any) -> t.NoReturn: + """Raise an :exc:`~werkzeug.exceptions.HTTPException` for the given + status code. + + If :data:`~flask.current_app` is available, it will call its + :attr:`~flask.Flask.aborter` object, otherwise it will use + :func:`werkzeug.exceptions.abort`. + + :param code: The status code for the exception, which must be + registered in ``app.aborter``. + :param args: Passed to the exception. + :param kwargs: Passed to the exception. + + .. versionadded:: 2.2 + Calls ``current_app.aborter`` if available instead of always + using Werkzeug's default ``abort``. + """ + if current_app: + current_app.aborter(code, *args, **kwargs) + + _wz_abort(code, *args, **kwargs) + + +def get_template_attribute(template_name: str, attribute: str) -> t.Any: + """Loads a macro (or variable) a template exports. This can be used to + invoke a macro from within Python code. If you for example have a + template named :file:`_cider.html` with the following contents: + + .. sourcecode:: html+jinja + + {% macro hello(name) %}Hello {{ name }}!{% endmacro %} + + You can access this from Python code like this:: + + hello = get_template_attribute('_cider.html', 'hello') + return hello('World') + + .. versionadded:: 0.2 + + :param template_name: the name of the template + :param attribute: the name of the variable of macro to access + """ + return getattr(current_app.jinja_env.get_template(template_name).module, attribute) + + +def flash(message: str, category: str = "message") -> None: + """Flashes a message to the next request. In order to remove the + flashed message from the session and to display it to the user, + the template has to call :func:`get_flashed_messages`. + + .. versionchanged:: 0.3 + `category` parameter added. + + :param message: the message to be flashed. + :param category: the category for the message. The following values + are recommended: ``'message'`` for any kind of message, + ``'error'`` for errors, ``'info'`` for information + messages and ``'warning'`` for warnings. However any + kind of string can be used as category. + """ + # Original implementation: + # + # session.setdefault('_flashes', []).append((category, message)) + # + # This assumed that changes made to mutable structures in the session are + # always in sync with the session object, which is not true for session + # implementations that use external storage for keeping their keys/values. + flashes = session.get("_flashes", []) + flashes.append((category, message)) + session["_flashes"] = flashes + app = current_app._get_current_object() # type: ignore + message_flashed.send( + app, + _async_wrapper=app.ensure_sync, + message=message, + category=category, + ) + + +def get_flashed_messages( + with_categories: bool = False, category_filter: t.Iterable[str] = () +) -> list[str] | list[tuple[str, str]]: + """Pulls all flashed messages from the session and returns them. + Further calls in the same request to the function will return + the same messages. By default just the messages are returned, + but when `with_categories` is set to ``True``, the return value will + be a list of tuples in the form ``(category, message)`` instead. + + Filter the flashed messages to one or more categories by providing those + categories in `category_filter`. This allows rendering categories in + separate html blocks. The `with_categories` and `category_filter` + arguments are distinct: + + * `with_categories` controls whether categories are returned with message + text (``True`` gives a tuple, where ``False`` gives just the message text). + * `category_filter` filters the messages down to only those matching the + provided categories. + + See :doc:`/patterns/flashing` for examples. + + .. versionchanged:: 0.3 + `with_categories` parameter added. + + .. versionchanged:: 0.9 + `category_filter` parameter added. + + :param with_categories: set to ``True`` to also receive categories. + :param category_filter: filter of categories to limit return values. Only + categories in the list will be returned. + """ + flashes = request_ctx.flashes + if flashes is None: + flashes = session.pop("_flashes") if "_flashes" in session else [] + request_ctx.flashes = flashes + if category_filter: + flashes = list(filter(lambda f: f[0] in category_filter, flashes)) + if not with_categories: + return [x[1] for x in flashes] + return flashes + + +def _prepare_send_file_kwargs(**kwargs: t.Any) -> dict[str, t.Any]: + if kwargs.get("max_age") is None: + kwargs["max_age"] = current_app.get_send_file_max_age + + kwargs.update( + environ=request.environ, + use_x_sendfile=current_app.config["USE_X_SENDFILE"], + response_class=current_app.response_class, + _root_path=current_app.root_path, + ) + return kwargs + + +def send_file( + path_or_file: os.PathLike[t.AnyStr] | str | t.IO[bytes], + mimetype: str | None = None, + as_attachment: bool = False, + download_name: str | None = None, + conditional: bool = True, + etag: bool | str = True, + last_modified: datetime | int | float | None = None, + max_age: None | (int | t.Callable[[str | None], int | None]) = None, +) -> Response: + """Send the contents of a file to the client. + + The first argument can be a file path or a file-like object. Paths + are preferred in most cases because Werkzeug can manage the file and + get extra information from the path. Passing a file-like object + requires that the file is opened in binary mode, and is mostly + useful when building a file in memory with :class:`io.BytesIO`. + + Never pass file paths provided by a user. The path is assumed to be + trusted, so a user could craft a path to access a file you didn't + intend. Use :func:`send_from_directory` to safely serve + user-requested paths from within a directory. + + If the WSGI server sets a ``file_wrapper`` in ``environ``, it is + used, otherwise Werkzeug's built-in wrapper is used. Alternatively, + if the HTTP server supports ``X-Sendfile``, configuring Flask with + ``USE_X_SENDFILE = True`` will tell the server to send the given + path, which is much more efficient than reading it in Python. + + :param path_or_file: The path to the file to send, relative to the + current working directory if a relative path is given. + Alternatively, a file-like object opened in binary mode. Make + sure the file pointer is seeked to the start of the data. + :param mimetype: The MIME type to send for the file. If not + provided, it will try to detect it from the file name. + :param as_attachment: Indicate to a browser that it should offer to + save the file instead of displaying it. + :param download_name: The default name browsers will use when saving + the file. Defaults to the passed file name. + :param conditional: Enable conditional and range responses based on + request headers. Requires passing a file path and ``environ``. + :param etag: Calculate an ETag for the file, which requires passing + a file path. Can also be a string to use instead. + :param last_modified: The last modified time to send for the file, + in seconds. If not provided, it will try to detect it from the + file path. + :param max_age: How long the client should cache the file, in + seconds. If set, ``Cache-Control`` will be ``public``, otherwise + it will be ``no-cache`` to prefer conditional caching. + + .. versionchanged:: 2.0 + ``download_name`` replaces the ``attachment_filename`` + parameter. If ``as_attachment=False``, it is passed with + ``Content-Disposition: inline`` instead. + + .. versionchanged:: 2.0 + ``max_age`` replaces the ``cache_timeout`` parameter. + ``conditional`` is enabled and ``max_age`` is not set by + default. + + .. versionchanged:: 2.0 + ``etag`` replaces the ``add_etags`` parameter. It can be a + string to use instead of generating one. + + .. versionchanged:: 2.0 + Passing a file-like object that inherits from + :class:`~io.TextIOBase` will raise a :exc:`ValueError` rather + than sending an empty file. + + .. versionadded:: 2.0 + Moved the implementation to Werkzeug. This is now a wrapper to + pass some Flask-specific arguments. + + .. versionchanged:: 1.1 + ``filename`` may be a :class:`~os.PathLike` object. + + .. versionchanged:: 1.1 + Passing a :class:`~io.BytesIO` object supports range requests. + + .. versionchanged:: 1.0.3 + Filenames are encoded with ASCII instead of Latin-1 for broader + compatibility with WSGI servers. + + .. versionchanged:: 1.0 + UTF-8 filenames as specified in :rfc:`2231` are supported. + + .. versionchanged:: 0.12 + The filename is no longer automatically inferred from file + objects. If you want to use automatic MIME and etag support, + pass a filename via ``filename_or_fp`` or + ``attachment_filename``. + + .. versionchanged:: 0.12 + ``attachment_filename`` is preferred over ``filename`` for MIME + detection. + + .. versionchanged:: 0.9 + ``cache_timeout`` defaults to + :meth:`Flask.get_send_file_max_age`. + + .. versionchanged:: 0.7 + MIME guessing and etag support for file-like objects was + removed because it was unreliable. Pass a filename if you are + able to, otherwise attach an etag yourself. + + .. versionchanged:: 0.5 + The ``add_etags``, ``cache_timeout`` and ``conditional`` + parameters were added. The default behavior is to add etags. + + .. versionadded:: 0.2 + """ + return werkzeug.utils.send_file( # type: ignore[return-value] + **_prepare_send_file_kwargs( + path_or_file=path_or_file, + environ=request.environ, + mimetype=mimetype, + as_attachment=as_attachment, + download_name=download_name, + conditional=conditional, + etag=etag, + last_modified=last_modified, + max_age=max_age, + ) + ) + + +def send_from_directory( + directory: os.PathLike[str] | str, + path: os.PathLike[str] | str, + **kwargs: t.Any, +) -> Response: + """Send a file from within a directory using :func:`send_file`. + + .. code-block:: python + + @app.route("/uploads/") + def download_file(name): + return send_from_directory( + app.config['UPLOAD_FOLDER'], name, as_attachment=True + ) + + This is a secure way to serve files from a folder, such as static + files or uploads. Uses :func:`~werkzeug.security.safe_join` to + ensure the path coming from the client is not maliciously crafted to + point outside the specified directory. + + If the final path does not point to an existing regular file, + raises a 404 :exc:`~werkzeug.exceptions.NotFound` error. + + :param directory: The directory that ``path`` must be located under, + relative to the current application's root path. This *must not* + be a value provided by the client, otherwise it becomes insecure. + :param path: The path to the file to send, relative to + ``directory``. + :param kwargs: Arguments to pass to :func:`send_file`. + + .. versionchanged:: 2.0 + ``path`` replaces the ``filename`` parameter. + + .. versionadded:: 2.0 + Moved the implementation to Werkzeug. This is now a wrapper to + pass some Flask-specific arguments. + + .. versionadded:: 0.5 + """ + return werkzeug.utils.send_from_directory( # type: ignore[return-value] + directory, path, **_prepare_send_file_kwargs(**kwargs) + ) + + +def get_root_path(import_name: str) -> str: + """Find the root path of a package, or the path that contains a + module. If it cannot be found, returns the current working + directory. + + Not to be confused with the value returned by :func:`find_package`. + + :meta private: + """ + # Module already imported and has a file attribute. Use that first. + mod = sys.modules.get(import_name) + + if mod is not None and hasattr(mod, "__file__") and mod.__file__ is not None: + return os.path.dirname(os.path.abspath(mod.__file__)) + + # Next attempt: check the loader. + try: + spec = importlib.util.find_spec(import_name) + + if spec is None: + raise ValueError + except (ImportError, ValueError): + loader = None + else: + loader = spec.loader + + # 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: + return os.getcwd() + + if hasattr(loader, "get_filename"): + filepath = loader.get_filename(import_name) # pyright: 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)) # type: ignore[no-any-return] + + +@cache +def _split_blueprint_path(name: str) -> list[str]: + out: list[str] = [name] + + if "." in name: + out.extend(_split_blueprint_path(name.rpartition(".")[0])) + + return out diff --git a/venv/Lib/site-packages/flask/json/__init__.py b/venv/Lib/site-packages/flask/json/__init__.py new file mode 100644 index 0000000..c0941d0 --- /dev/null +++ b/venv/Lib/site-packages/flask/json/__init__.py @@ -0,0 +1,170 @@ +from __future__ import annotations + +import json as _json +import typing as t + +from ..globals import current_app +from .provider import _default + +if t.TYPE_CHECKING: # pragma: no cover + from ..wrappers import Response + + +def dumps(obj: t.Any, **kwargs: t.Any) -> str: + """Serialize data as JSON. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.dumps() ` + method, otherwise it will use :func:`json.dumps`. + + :param obj: The data to serialize. + :param kwargs: Arguments passed to the ``dumps`` implementation. + + .. versionchanged:: 2.3 + The ``app`` parameter was removed. + + .. versionchanged:: 2.2 + Calls ``current_app.json.dumps``, allowing an app to override + the behavior. + + .. versionchanged:: 2.0.2 + :class:`decimal.Decimal` is supported by converting to a string. + + .. versionchanged:: 2.0 + ``encoding`` will be removed in Flask 2.1. + + .. versionchanged:: 1.0.3 + ``app`` can be passed directly, rather than requiring an app + context for configuration. + """ + if current_app: + return current_app.json.dumps(obj, **kwargs) + + kwargs.setdefault("default", _default) + return _json.dumps(obj, **kwargs) + + +def dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None: + """Serialize data as JSON and write to a file. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.dump() ` + method, otherwise it will use :func:`json.dump`. + + :param obj: The data to serialize. + :param fp: A file opened for writing text. Should use the UTF-8 + encoding to be valid JSON. + :param kwargs: Arguments passed to the ``dump`` implementation. + + .. versionchanged:: 2.3 + The ``app`` parameter was removed. + + .. versionchanged:: 2.2 + Calls ``current_app.json.dump``, allowing an app to override + the behavior. + + .. versionchanged:: 2.0 + Writing to a binary file, and the ``encoding`` argument, will be + removed in Flask 2.1. + """ + if current_app: + current_app.json.dump(obj, fp, **kwargs) + else: + kwargs.setdefault("default", _default) + _json.dump(obj, fp, **kwargs) + + +def loads(s: str | bytes, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.loads() ` + method, otherwise it will use :func:`json.loads`. + + :param s: Text or UTF-8 bytes. + :param kwargs: Arguments passed to the ``loads`` implementation. + + .. versionchanged:: 2.3 + The ``app`` parameter was removed. + + .. versionchanged:: 2.2 + Calls ``current_app.json.loads``, allowing an app to override + the behavior. + + .. versionchanged:: 2.0 + ``encoding`` will be removed in Flask 2.1. The data must be a + string or UTF-8 bytes. + + .. versionchanged:: 1.0.3 + ``app`` can be passed directly, rather than requiring an app + context for configuration. + """ + if current_app: + return current_app.json.loads(s, **kwargs) + + return _json.loads(s, **kwargs) + + +def load(fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON read from a file. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.load() ` + method, otherwise it will use :func:`json.load`. + + :param fp: A file opened for reading text or UTF-8 bytes. + :param kwargs: Arguments passed to the ``load`` implementation. + + .. versionchanged:: 2.3 + The ``app`` parameter was removed. + + .. versionchanged:: 2.2 + Calls ``current_app.json.load``, allowing an app to override + the behavior. + + .. versionchanged:: 2.2 + The ``app`` parameter will be removed in Flask 2.3. + + .. versionchanged:: 2.0 + ``encoding`` will be removed in Flask 2.1. The file must be text + mode, or binary mode with UTF-8 bytes. + """ + if current_app: + return current_app.json.load(fp, **kwargs) + + return _json.load(fp, **kwargs) + + +def jsonify(*args: t.Any, **kwargs: t.Any) -> Response: + """Serialize the given arguments as JSON, and return a + :class:`~flask.Response` object with the ``application/json`` + mimetype. A dict or list returned from a view will be converted to a + JSON response automatically without needing to call this. + + This requires an active request or application context, and calls + :meth:`app.json.response() `. + + In debug mode, the output is formatted with indentation to make it + easier to read. This may also be controlled by the provider. + + Either positional or keyword arguments can be given, not both. + If no arguments are given, ``None`` is serialized. + + :param args: A single value to serialize, or multiple values to + treat as a list to serialize. + :param kwargs: Treat as a dict to serialize. + + .. versionchanged:: 2.2 + Calls ``current_app.json.response``, allowing an app to override + the behavior. + + .. versionchanged:: 2.0.2 + :class:`decimal.Decimal` is supported by converting to a string. + + .. versionchanged:: 0.11 + Added support for serializing top-level arrays. This was a + security risk in ancient browsers. See :ref:`security-json`. + + .. versionadded:: 0.2 + """ + return current_app.json.response(*args, **kwargs) # type: ignore[return-value] diff --git a/venv/Lib/site-packages/flask/json/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/flask/json/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e922f731f39e88380a83a02b2163be4a591d4ab3 GIT binary patch literal 5979 zcmd5=&u=416?WTB;_)Pz-R!XQW7*kamjgzbo*yeAWUWSo*%`<#WL6{y5rXV;S2?Mq z-Q85RZHyvLoH+0|fW(D=sgEEIb3@`pPJFMbyX{GwC^O2g#**#ouBum6uipFK`(C`V z(yZX~@BjQy{qtLu%D?Dg_EW>dk17?<9HSvB!V4<{Z`bpvT@95Q|}BaBcURSCI>cP5lXam%{6V<=tiUek4}J>YwVnD}sqN@g7>M;ydDfaSN>* z;$v|~+(qjh@rn3Q+(YYKQQfYzzWb8))$+3IJc{C!rz(!j*2`-CG8k$tqqM`5E2eAKZho_rB*yte~?V@ zlrwJF7uzqk{AQEkv)N~Ddfo2+u^)1C{PKM@mTT)4E?34|e2}u9ZSUVzJKR^g!ydvht^E zwfn;;Xm_XbyZPqZ3D^Ba_T3)cFsJo4+tB;N0WQt3gc~CT!wjs; zx?Q`aE{;7&LPXEP^MC|G1RT) zARb9^p`6F1oToet4J6H6#EfdY-F1cm@mNLsj7JRX(f%P{Yv{O?LI>)}13pr*zHs4( z{&eYf0246nu8@Hm@X-I<{aZUQ!z77y3U&1+EQlj4n9?4wIA<_mv@c$#+*-FQqaYTT z08Lqg>d8V!OhxQD*&$T>=mL3<{D(8DFVr4DA&`|{t58}7X*gMDnnN8LmyCWbe>YUL z+gy9)ws59Y{yt^>Skw9XYJZ4%xNc`e*;3)wi<*2htCKxu3)W_`rjcphuNSFzp+nuR zs&3#(6WaZwqSx?u@@%8M2PZhh-pt;{W)Ke&9$}fik&H%rKU2Luqf&W4;lUB#muAm8 z(gTwApwm&2N;@4tndlpkwSupq1?nH%^p>k@o_(8D`^m|*)ZcvZ8{Erpv-(yX$$&7W z_z=97`0`^s0AFqt_;T#^t3a44Aq-#>Ab4CojWTD)v})Wt7i*wu!NwZLa^})kq0BG$ z>%gB&!_OO`&zpfyKLPMCIyj3H8NrRn&PXTLiC}m>+dhbgp|J2u?)%G~=l6eDnrGoN zq(RvD2#79nASd+6q407T_f|vP)gxS~!(SEZCd}%oi0g^H5_n^rqw^|M(^eP^*Izfbf2TcnN`dzeIH3tkg=`Epy#Rm8D9zEW|&jWm& zd*8YX_rC4fx0z>7uEVj}F_1WwCO9v1`0w{I|Gdm0t)0mhJiZDK{#=&P^P9*Q!kCN8 zV_z$%Qd|nwSlSCMD?N zr4*MooVf?vKQG<-=A(YAZiBsZwbWapOfoSB2d3`(_5=*dUvvF zW3uG{+X&3(II_BVQ_>(!e+J&Qs1gKTy(Xi$HnC zUS2Ey(LUeW_~$lv#a=tj?|0@?6ykLAQ}9nONWmZb4@*aWO*pV=Pyq*&beDn#u>jEo zFz3;ko)LZ1gR@Beifh(MbA@M~8&p4pUxoNO&%A9tlip=@;^TS=%>du>Q}FN?#KS3h zFLLB51BaVtNTsJ7o3e;~gs^lquegdaHKy+!Gdw+a0MXCyDftUJ8XNP+q8jv~C?rG#yBclpGg{OhMor`E`Iyq5tSnsn`u6U^jVr#j?UeuQuSt$^B4{WQeQRd}4Cz zqq5RW9wkvMQt5Ox>DR4Q;8P@_;h3LE$6?VS84TOz@#Klw8T7=q9>rWyV~ifl3D8rp86ZC$6wW$JIt z^ji!^Y52^@_i5y!%f9Z+=_i@!xAVV!e!t!Qyq!=GXsu<DBvnWjl%0)320^K*IzyBJpJjjr(bSv{baW>)&M|UhK)0}Qe*j? z%?&k$A*-bsB1jQO6C2DLp8>&!p?pGI&rs&MTxnDrAcf1`BEHR9^W)~d=0~gd{tuGM B#OnY6 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/json/__pycache__/provider.cpython-310.pyc b/venv/Lib/site-packages/flask/json/__pycache__/provider.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1e69987cd3eb33953de56c4a25d175173e0a3183 GIT binary patch literal 7671 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/json/__pycache__/tag.cpython-310.pyc b/venv/Lib/site-packages/flask/json/__pycache__/tag.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52cf0385540e009beb23c7573bc8b72df64f2c6e GIT binary patch literal 11629 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/json/provider.py b/venv/Lib/site-packages/flask/json/provider.py new file mode 100644 index 0000000..ea7e475 --- /dev/null +++ b/venv/Lib/site-packages/flask/json/provider.py @@ -0,0 +1,215 @@ +from __future__ import annotations + +import dataclasses +import decimal +import json +import typing as t +import uuid +import weakref +from datetime import date + +from werkzeug.http import http_date + +if t.TYPE_CHECKING: # pragma: no cover + from werkzeug.sansio.response import Response + + from ..sansio.app import App + + +class JSONProvider: + """A standard set of JSON operations for an application. Subclasses + of this can be used to customize JSON behavior or use different + JSON libraries. + + To implement a provider for a specific library, subclass this base + class and implement at least :meth:`dumps` and :meth:`loads`. All + other methods have default implementations. + + To use a different provider, either subclass ``Flask`` and set + :attr:`~flask.Flask.json_provider_class` to a provider class, or set + :attr:`app.json ` to an instance of the class. + + :param app: An application instance. This will be stored as a + :class:`weakref.proxy` on the :attr:`_app` attribute. + + .. versionadded:: 2.2 + """ + + def __init__(self, app: App) -> None: + self._app: App = weakref.proxy(app) + + def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: + """Serialize data as JSON. + + :param obj: The data to serialize. + :param kwargs: May be passed to the underlying JSON library. + """ + raise NotImplementedError + + def dump(self, obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None: + """Serialize data as JSON and write to a file. + + :param obj: The data to serialize. + :param fp: A file opened for writing text. Should use the UTF-8 + encoding to be valid JSON. + :param kwargs: May be passed to the underlying JSON library. + """ + fp.write(self.dumps(obj, **kwargs)) + + def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON. + + :param s: Text or UTF-8 bytes. + :param kwargs: May be passed to the underlying JSON library. + """ + raise NotImplementedError + + def load(self, fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON read from a file. + + :param fp: A file opened for reading text or UTF-8 bytes. + :param kwargs: May be passed to the underlying JSON library. + """ + return self.loads(fp.read(), **kwargs) + + def _prepare_response_obj( + self, args: tuple[t.Any, ...], kwargs: dict[str, t.Any] + ) -> t.Any: + if args and kwargs: + raise TypeError("app.json.response() takes either args or kwargs, not both") + + if not args and not kwargs: + return None + + if len(args) == 1: + return args[0] + + return args or kwargs + + def response(self, *args: t.Any, **kwargs: t.Any) -> Response: + """Serialize the given arguments as JSON, and return a + :class:`~flask.Response` object with the ``application/json`` + mimetype. + + The :func:`~flask.json.jsonify` function calls this method for + the current application. + + Either positional or keyword arguments can be given, not both. + If no arguments are given, ``None`` is serialized. + + :param args: A single value to serialize, or multiple values to + treat as a list to serialize. + :param kwargs: Treat as a dict to serialize. + """ + obj = self._prepare_response_obj(args, kwargs) + return self._app.response_class(self.dumps(obj), mimetype="application/json") + + +def _default(o: t.Any) -> t.Any: + if isinstance(o, date): + return http_date(o) + + if isinstance(o, (decimal.Decimal, uuid.UUID)): + return str(o) + + if dataclasses and dataclasses.is_dataclass(o): + return dataclasses.asdict(o) # type: ignore[arg-type] + + if hasattr(o, "__html__"): + return str(o.__html__()) + + raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable") + + +class DefaultJSONProvider(JSONProvider): + """Provide JSON operations using Python's built-in :mod:`json` + library. Serializes the following additional data types: + + - :class:`datetime.datetime` and :class:`datetime.date` are + serialized to :rfc:`822` strings. This is the same as the HTTP + date format. + - :class:`uuid.UUID` is serialized to a string. + - :class:`dataclasses.dataclass` is passed to + :func:`dataclasses.asdict`. + - :class:`~markupsafe.Markup` (or any object with a ``__html__`` + method) will call the ``__html__`` method to get a string. + """ + + default: t.Callable[[t.Any], t.Any] = staticmethod(_default) # type: ignore[assignment] + """Apply this function to any object that :meth:`json.dumps` does + not know how to serialize. It should return a valid JSON type or + raise a ``TypeError``. + """ + + ensure_ascii = True + """Replace non-ASCII characters with escape sequences. This may be + more compatible with some clients, but can be disabled for better + performance and size. + """ + + sort_keys = True + """Sort the keys in any serialized dicts. This may be useful for + some caching situations, but can be disabled for better performance. + When enabled, keys must all be strings, they are not converted + before sorting. + """ + + compact: bool | None = None + """If ``True``, or ``None`` out of debug mode, the :meth:`response` + output will not add indentation, newlines, or spaces. If ``False``, + or ``None`` in debug mode, it will use a non-compact representation. + """ + + mimetype = "application/json" + """The mimetype set in :meth:`response`.""" + + def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: + """Serialize data as JSON to a string. + + Keyword arguments are passed to :func:`json.dumps`. Sets some + parameter defaults from the :attr:`default`, + :attr:`ensure_ascii`, and :attr:`sort_keys` attributes. + + :param obj: The data to serialize. + :param kwargs: Passed to :func:`json.dumps`. + """ + kwargs.setdefault("default", self.default) + kwargs.setdefault("ensure_ascii", self.ensure_ascii) + kwargs.setdefault("sort_keys", self.sort_keys) + return json.dumps(obj, **kwargs) + + def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON from a string or bytes. + + :param s: Text or UTF-8 bytes. + :param kwargs: Passed to :func:`json.loads`. + """ + return json.loads(s, **kwargs) + + def response(self, *args: t.Any, **kwargs: t.Any) -> Response: + """Serialize the given arguments as JSON, and return a + :class:`~flask.Response` object with it. The response mimetype + will be "application/json" and can be changed with + :attr:`mimetype`. + + If :attr:`compact` is ``False`` or debug mode is enabled, the + output will be formatted to be easier to read. + + Either positional or keyword arguments can be given, not both. + If no arguments are given, ``None`` is serialized. + + :param args: A single value to serialize, or multiple values to + treat as a list to serialize. + :param kwargs: Treat as a dict to serialize. + """ + obj = self._prepare_response_obj(args, kwargs) + dump_args: dict[str, t.Any] = {} + + if (self.compact is None and self._app.debug) or self.compact is False: + dump_args.setdefault("indent", 2) + else: + dump_args.setdefault("separators", (",", ":")) + + return self._app.response_class( + f"{self.dumps(obj, **dump_args)}\n", mimetype=self.mimetype + ) diff --git a/venv/Lib/site-packages/flask/json/tag.py b/venv/Lib/site-packages/flask/json/tag.py new file mode 100644 index 0000000..8dc3629 --- /dev/null +++ b/venv/Lib/site-packages/flask/json/tag.py @@ -0,0 +1,327 @@ +""" +Tagged JSON +~~~~~~~~~~~ + +A compact representation for lossless serialization of non-standard JSON +types. :class:`~flask.sessions.SecureCookieSessionInterface` uses this +to serialize the session data, but it may be useful in other places. It +can be extended to support other types. + +.. autoclass:: TaggedJSONSerializer + :members: + +.. autoclass:: JSONTag + :members: + +Let's see an example that adds support for +:class:`~collections.OrderedDict`. Dicts don't have an order in JSON, so +to handle this we will dump the items as a list of ``[key, value]`` +pairs. Subclass :class:`JSONTag` and give it the new key ``' od'`` to +identify the type. The session serializer processes dicts first, so +insert the new tag at the front of the order since ``OrderedDict`` must +be processed before ``dict``. + +.. code-block:: python + + from flask.json.tag import JSONTag + + class TagOrderedDict(JSONTag): + __slots__ = ('serializer',) + key = ' od' + + def check(self, value): + return isinstance(value, OrderedDict) + + def to_json(self, value): + return [[k, self.serializer.tag(v)] for k, v in iteritems(value)] + + def to_python(self, value): + return OrderedDict(value) + + app.session_interface.serializer.register(TagOrderedDict, index=0) +""" + +from __future__ import annotations + +import typing as t +from base64 import b64decode +from base64 import b64encode +from datetime import datetime +from uuid import UUID + +from markupsafe import Markup +from werkzeug.http import http_date +from werkzeug.http import parse_date + +from ..json import dumps +from ..json import loads + + +class JSONTag: + """Base class for defining type tags for :class:`TaggedJSONSerializer`.""" + + __slots__ = ("serializer",) + + #: The tag to mark the serialized object with. If empty, this tag is + #: only used as an intermediate step during tagging. + key: str = "" + + def __init__(self, serializer: TaggedJSONSerializer) -> None: + """Create a tagger for the given serializer.""" + self.serializer = serializer + + def check(self, value: t.Any) -> bool: + """Check if the given value should be tagged by this tag.""" + raise NotImplementedError + + def to_json(self, value: t.Any) -> t.Any: + """Convert the Python object to an object that is a valid JSON type. + The tag will be added later.""" + raise NotImplementedError + + def to_python(self, value: t.Any) -> t.Any: + """Convert the JSON representation back to the correct type. The tag + will already be removed.""" + raise NotImplementedError + + def tag(self, value: t.Any) -> dict[str, t.Any]: + """Convert the value to a valid JSON type and add the tag structure + around it.""" + return {self.key: self.to_json(value)} + + +class TagDict(JSONTag): + """Tag for 1-item dicts whose only key matches a registered tag. + + Internally, the dict key is suffixed with `__`, and the suffix is removed + when deserializing. + """ + + __slots__ = () + key = " di" + + def check(self, value: t.Any) -> bool: + return ( + isinstance(value, dict) + and len(value) == 1 + and next(iter(value)) in self.serializer.tags + ) + + def to_json(self, value: t.Any) -> t.Any: + key = next(iter(value)) + return {f"{key}__": self.serializer.tag(value[key])} + + def to_python(self, value: t.Any) -> t.Any: + key = next(iter(value)) + return {key[:-2]: value[key]} + + +class PassDict(JSONTag): + __slots__ = () + + def check(self, value: t.Any) -> bool: + return isinstance(value, dict) + + def to_json(self, value: t.Any) -> t.Any: + # JSON objects may only have string keys, so don't bother tagging the + # key here. + return {k: self.serializer.tag(v) for k, v in value.items()} + + tag = to_json + + +class TagTuple(JSONTag): + __slots__ = () + key = " t" + + def check(self, value: t.Any) -> bool: + return isinstance(value, tuple) + + def to_json(self, value: t.Any) -> t.Any: + return [self.serializer.tag(item) for item in value] + + def to_python(self, value: t.Any) -> t.Any: + return tuple(value) + + +class PassList(JSONTag): + __slots__ = () + + def check(self, value: t.Any) -> bool: + return isinstance(value, list) + + def to_json(self, value: t.Any) -> t.Any: + return [self.serializer.tag(item) for item in value] + + tag = to_json + + +class TagBytes(JSONTag): + __slots__ = () + key = " b" + + def check(self, value: t.Any) -> bool: + return isinstance(value, bytes) + + def to_json(self, value: t.Any) -> t.Any: + return b64encode(value).decode("ascii") + + def to_python(self, value: t.Any) -> t.Any: + return b64decode(value) + + +class TagMarkup(JSONTag): + """Serialize anything matching the :class:`~markupsafe.Markup` API by + having a ``__html__`` method to the result of that method. Always + deserializes to an instance of :class:`~markupsafe.Markup`.""" + + __slots__ = () + key = " m" + + def check(self, value: t.Any) -> bool: + return callable(getattr(value, "__html__", None)) + + def to_json(self, value: t.Any) -> t.Any: + return str(value.__html__()) + + def to_python(self, value: t.Any) -> t.Any: + return Markup(value) + + +class TagUUID(JSONTag): + __slots__ = () + key = " u" + + def check(self, value: t.Any) -> bool: + return isinstance(value, UUID) + + def to_json(self, value: t.Any) -> t.Any: + return value.hex + + def to_python(self, value: t.Any) -> t.Any: + return UUID(value) + + +class TagDateTime(JSONTag): + __slots__ = () + key = " d" + + def check(self, value: t.Any) -> bool: + return isinstance(value, datetime) + + def to_json(self, value: t.Any) -> t.Any: + return http_date(value) + + def to_python(self, value: t.Any) -> t.Any: + return parse_date(value) + + +class TaggedJSONSerializer: + """Serializer that uses a tag system to compactly represent objects that + are not JSON types. Passed as the intermediate serializer to + :class:`itsdangerous.Serializer`. + + The following extra types are supported: + + * :class:`dict` + * :class:`tuple` + * :class:`bytes` + * :class:`~markupsafe.Markup` + * :class:`~uuid.UUID` + * :class:`~datetime.datetime` + """ + + __slots__ = ("tags", "order") + + #: Tag classes to bind when creating the serializer. Other tags can be + #: added later using :meth:`~register`. + default_tags = [ + TagDict, + PassDict, + TagTuple, + PassList, + TagBytes, + TagMarkup, + TagUUID, + TagDateTime, + ] + + def __init__(self) -> None: + self.tags: dict[str, JSONTag] = {} + self.order: list[JSONTag] = [] + + for cls in self.default_tags: + self.register(cls) + + def register( + self, + tag_class: type[JSONTag], + force: bool = False, + index: int | None = None, + ) -> None: + """Register a new tag with this serializer. + + :param tag_class: tag class to register. Will be instantiated with this + serializer instance. + :param force: overwrite an existing tag. If false (default), a + :exc:`KeyError` is raised. + :param index: index to insert the new tag in the tag order. Useful when + the new tag is a special case of an existing tag. If ``None`` + (default), the tag is appended to the end of the order. + + :raise KeyError: if the tag key is already registered and ``force`` is + not true. + """ + tag = tag_class(self) + key = tag.key + + if key: + if not force and key in self.tags: + raise KeyError(f"Tag '{key}' is already registered.") + + self.tags[key] = tag + + if index is None: + self.order.append(tag) + else: + self.order.insert(index, tag) + + def tag(self, value: t.Any) -> t.Any: + """Convert a value to a tagged representation if necessary.""" + for tag in self.order: + if tag.check(value): + return tag.tag(value) + + return value + + def untag(self, value: dict[str, t.Any]) -> t.Any: + """Convert a tagged representation back to the original type.""" + if len(value) != 1: + return value + + key = next(iter(value)) + + if key not in self.tags: + return value + + return self.tags[key].to_python(value[key]) + + def _untag_scan(self, value: t.Any) -> t.Any: + if isinstance(value, dict): + # untag each item recursively + value = {k: self._untag_scan(v) for k, v in value.items()} + # untag the dict itself + value = self.untag(value) + elif isinstance(value, list): + # untag each item recursively + value = [self._untag_scan(item) for item in value] + + return value + + def dumps(self, value: t.Any) -> str: + """Tag the value and dump it to a compact JSON string.""" + return dumps(self.tag(value), separators=(",", ":")) + + def loads(self, value: str) -> t.Any: + """Load data from a JSON string and deserialized any tagged objects.""" + return self._untag_scan(loads(value)) diff --git a/venv/Lib/site-packages/flask/logging.py b/venv/Lib/site-packages/flask/logging.py new file mode 100644 index 0000000..0cb8f43 --- /dev/null +++ b/venv/Lib/site-packages/flask/logging.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +import logging +import sys +import typing as t + +from werkzeug.local import LocalProxy + +from .globals import request + +if t.TYPE_CHECKING: # pragma: no cover + from .sansio.app import App + + +@LocalProxy +def wsgi_errors_stream() -> t.TextIO: + """Find the most appropriate error stream for the application. If a request + is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``. + + If you configure your own :class:`logging.StreamHandler`, you may want to + use this for the stream. If you are using file or dict configuration and + can't import this directly, you can refer to it as + ``ext://flask.logging.wsgi_errors_stream``. + """ + if request: + return request.environ["wsgi.errors"] # type: ignore[no-any-return] + + return sys.stderr + + +def has_level_handler(logger: logging.Logger) -> bool: + """Check if there is a handler in the logging chain that will handle the + given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`. + """ + level = logger.getEffectiveLevel() + current = logger + + while current: + if any(handler.level <= level for handler in current.handlers): + return True + + if not current.propagate: + break + + current = current.parent # type: ignore + + return False + + +#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format +#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``. +default_handler = logging.StreamHandler(wsgi_errors_stream) # type: ignore +default_handler.setFormatter( + logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s") +) + + +def create_logger(app: App) -> logging.Logger: + """Get the Flask app's logger and configure it if needed. + + The logger name will be the same as + :attr:`app.import_name `. + + When :attr:`~flask.Flask.debug` is enabled, set the logger level to + :data:`logging.DEBUG` if it is not set. + + If there is no handler for the logger's effective level, add a + :class:`~logging.StreamHandler` for + :func:`~flask.logging.wsgi_errors_stream` with a basic format. + """ + logger = logging.getLogger(app.name) + + if app.debug and not logger.level: + logger.setLevel(logging.DEBUG) + + if not has_level_handler(logger): + logger.addHandler(default_handler) + + return logger diff --git a/venv/Lib/site-packages/flask/py.typed b/venv/Lib/site-packages/flask/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/flask/sansio/README.md b/venv/Lib/site-packages/flask/sansio/README.md new file mode 100644 index 0000000..623ac19 --- /dev/null +++ b/venv/Lib/site-packages/flask/sansio/README.md @@ -0,0 +1,6 @@ +# Sansio + +This folder contains code that can be used by alternative Flask +implementations, for example Quart. The code therefore cannot do any +IO, nor be part of a likely IO path. Finally this code cannot use the +Flask globals. diff --git a/venv/Lib/site-packages/flask/sansio/__pycache__/app.cpython-310.pyc b/venv/Lib/site-packages/flask/sansio/__pycache__/app.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..33770314e2181fd1331d3ada577976b7256e8a2e GIT binary patch literal 28625 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 EUjxrVEC2ui literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/sansio/__pycache__/blueprints.cpython-310.pyc b/venv/Lib/site-packages/flask/sansio/__pycache__/blueprints.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..58082c0dd9aa798be872f7cc72546431884390d5 GIT binary patch literal 22918 zcmd^nYit}@c3xNYgWXNCMNuU69Bs{$!yQh~%syjAE3ZbHnU!~zJC-=JagXF?v#TgI z)$FEjH6^j#T6j zMC%>p`_8SpRo!e#n%P~$K$_s~dvD$6Ip?0&J=N{;@nQ;}|N5Q(+xXmaD)lWElAjC; zpHHPMzhb3QZpyWqsdcMjS8Mw_m9EUY_W9J@v2PRVb5<}~Yqi>)TBp%&`HL*O-fC^L*>M~74vMAC4Y$_u zs@GL*W5ZM5wso&uXKSO;y7X-ziGv)fdM$UO-Dq{HtE#T0{`B0uVToz0DPuXDN0V*1p^MsVQe=BBryFwl8EJdcHW!M>$xZMEa6 zs`9RGdOnwxU1@K&++bpy;5s(0y9PjiwR0g)lJo`hG6r9L%Mve zhMiz^>?WL`PKYNzhSoohS}#?x+!W~Bs-)e6?ilcwsbmVJLdiV@Y)-o~x3iV3TLSKm zsBv`Y{?8@srLAge%ef4Qddg|9takXdb8&H4l~LF)Lds}%k*9#>K2y2x{5Ye$_8=e z_1YB=I0yQw7_H+iI~vChFyavQ%GtoEo-1qUw4GYBY1Zr1QCg|huQ((hp~|wf3kqfv zLBUKGq_GDz25H&@*Ddacn4ilj$S-fH7UkZIM{yVVrey1nx39FPjVs(!hqKa8h zIT~g$$f(Vx7oPM<3D| z>FtZ8wLd@xn1#ICwAA6ArDj}v2UuTA?bt5x+_QGl{9YwJSuN6E>ZaApfCiZdy;z`C z^adz9xnBB~brTe|HgYrTW_p=xDYeka_p+;2BZWQ%H*?4OUF+vbyQ#I}+9>IJZR|H7 zJMld(-}Y~$-pgXnEapzHrZGykf%(`AGSdRh<+^`#%DL<{Ar+znj=MWpgp2SIhC zCXXYTvlkc9PuBesD`oMYWKMSQY35`bGJ?aR^vm%C3e%|u#cFlE?E)axYA{xsm{gQ1>{COSaz7G+DkL4>UCVdjs zhQ^pq{(0Heyu0#FYSnTl-2=DBD>=6gxqApwcf`xPu6xuyb~|&+suZO3n0s7G#e(g6 z?yCEYI}3R{if5PHkGs#wvoZIwJMTV^5ystyTXtVSuH@wlY5uuu?iKel?q_djG3Gwj zx=nZ8{SkLTY9_*(misyPq}1#WYufJT-BVIC8P;sLr`b~l}CN)!GjdIVr zKPENPSflU0?tV$uID=Bh{j&Rpln$b_>7H}XOX(0w*W5SVx1@9!rR#3ReOpRLP`cq> zbbnGxM^V~xm))9_j=hu0q$-brCv+95tM;uzDs>Kuk4Mc9r#3#1^C;BY%_dkp7(3Mr zMLvl(WD{TjDpE|IR~~f**RTp7{Grx?e$aAOJi}+u0OQn9t~Ha)LxvG$qg<5ysdy2> zNiezAmSj}$llFDr!6}E>pvn|$M)3k<9P%10853UJY-sfg^Cy~z!`3c)HLMMKR0I6S zIookIee5$*y?k&bOjn2g)@dv3-**;7*)j^%t5mC&=baTyhIM&pL{wN?bG zNQa|xrZ}X_EuitbG}J0rQV$A2az;qkfx$IS<^H=*`xgMq{P zp1KA|XhR^L1d=xFEhm~w2H>zVjYSY);~G}oHXFoJ`+9RL(xDuSvS>)Xpc;_rHhd~n z+IEm#Zjmf$u@2j&Xu?>8MgdC1pzNT3Xj-kYl5j>3hy&MlmY4e=Mr53&agG>r+4b<#0>A~e=Pd8`7b^E=TXdQDn1_N~Mi zd0dDoaCe{pW8{Z2Rp<(g4+w&c90EZBX~-A2#}KkU2d9n4<%D&Pw)BS2WKbhB66ieZ z;1Ch2k`o!0wcxyLTIjP8??49X*Y=@=n|Z`Hu~LH%fje7XPB2?Jpe?8FsSa#m2NvU| zuzr$v!#5+Cv9ZQZsEtsySgkdh^Nz2L5y5n*aQGWuy|LOzoQU;WOIWseO(5(;;ZiO; z*C4DRZR()8OP;&1;Jj3Rsr;gu`{f8xVCkA%mzV3!23YtdU~Yp16^T8Fdw6kN=k)7m zp*O8#O4pD16c3zqtx87>Ac74WQfS@ksCJXv?w4c!2G^tecghYZNZE_j53UZufrgROC~LjoJ z;=@Jm@Zg)5A@_2LlcGCZ;lr-ASG?upS)C(2F_S zod=M?Dq3xb860p$*&8aq;6^-4242o=_utQo_J0`~eys^J#oa=CsJhV+$LR+1ZhAGc ze@cF6_FKdvhW52?K-GfsomK{#1Lr=SyRA-ht6cmNWs1KEVM!^ZPoX~#wiekI^RWdp z?|7YhS!@W513rVDIpjEfYZk!^mlo!vZ{4hvhC-uLo=XQ))l1Mj;~iAF@St=GUR_1% ziHU5^*7Wk@fOx{Vr(GXRSL>I(`jyaEOv}r^?79CDkNgsn90(zA0p<5I`0IWwf%0Mf zf(%WVVERl*%3@!<8ak18kwO|12q{b;LsEdZ4c^?)w`F(#^SRK0PP@n0MuVo#Fq6fw z`4~JDQUtLe(D>U$r#C|n7*J?#1|oYzXKF;3HrIi6=CuzSSPT{M&>O$Y`EEwhp;gQdSG%?bhh|!@R_JNq-rDGl6`n0)uakb!b z?)Dg6lRK#n`0aM;mUd!~;ItLG-}_2iU4exI0pW!E(PJ-*70f#*Eze@8f%D;XM1BLx zGV@H3C5nXClJh|nn5Teuy9FnrIk7i2X_ep9rDgM(9uBLw4q2zEuCF{79{ z^|c2LBA#eHbBqu$Ya&`McM?pDS8UOTdBXny!8(JCFtNfmzHgCs!L;~)*TPMn`19^s z>L<_)Uol$c?2UvMdf&lQpL{WAjafKl&kUkfj&}J3tyql4M2Nnw&`fC_V z(a$}X)+b*kzQ|f(`WKj!C5&p^$^`!nwLW{5@>7}a^w32NI4$o@3@QK#4<=oPecpDC zO@fp&PwvtI%pk-e@^588d{*~cr{#(Zr>y@TtpA$JP{ac~8KlCba|!s?K9~^y;JYNn zh(8D+47>8;IOjK4L}o%1Q0BCuTB?RiBLbs}qR6W(@F*Aq`m!!`V`!Nr6cv*gsDhB0 z%V^XL+{p-jBoX{f4DyoygRXM?OC#MQF^UG8X*V++!n;@ku^{&yPa#7U3ilQH4(<~~ zrGg5!V-qL5;3>)R1Q zFN8e2gt9EH00^Nx3rq8PsCO^~;`hnLx7gNo~+TT=Txkb@znJ+FKcgC#N|02H%^ z?$f6w3c*YaJjZJSv>P2>Brbxbybi)qi%fVy67iV$=5m0BC!?*zD(rpq6ONZJ<*ljY zyg}5&os3)HNN%y0 zLps_UK|1!{2+vF>D`IurE#1y@)ScX&)EUTvThHW+U!g*FV9DNGRj~*o=;=$uU)Wjj^8ZxN;@-mQu3X0rvd4Gs7bjq$W8PnWbQxh zjjtUw4g_(1V4^wVOAw3eDYP zUPAHU(T<6ZdcvPWJ|zs(Y$qHz3@llQ#EH?veR90(2rdHL>Blo2IaH)K?1_fN4r2Bg z`;0zO{$$^Hi`o-p_Ch7{I&49h?R6M}Vq9#+K0&yNO!wi#_lu-+iGZO$#-ME6y-(VsF}^ebV2KSyp9j5T@#PDbz>nBgp~!O^ zt8i}6Y)Zgx$bLlj^=&3}zQH_?$B^q6n79vz@8HIk{y!1=8Uv1_;~yBppxsUMUj-Se zTisE(t0Js)ZD;nLHQOx`KBCdgc8|?ck%`3%cSK6Hd_2pgea}F~CW~_pc&XDaJG|x)K895DS;n-g|Y9jcaVT zAO0u_&og&{I{t#*$jSun?OpQ}s|seS>z=yg#afg&3Vm@PXeVJBuGSzQ`KZ-AXF?c2 zsxrzTl(1ejw;)q*Z*1vEnkU(80ZEXLuspYa&^VExvjR0h?ck&FP#Ia-zMz32PNc3fh%pn6SHO6=ng^FDr9WVKL6b0p)kz)|UzwgHhb6KzLNyKH-)j4~afBYxys38HO>@za`K*gx!g8 zAkZoqXhHR+X#h_ca);@BOjF^#3aCP%vlqqL51x}nz~j0fqto1I>ktCx{_1nvIh`hrPc~1c3?v=%epC9-tg&;WF7wECRgC^(v!G!{FC= zq58=4q;E&S&!uwqQL>_F_bP8GWxa~1 zlJLYz$pog##ZEBQA2P2wd2paF#6GN?i9+YW6eaXNte;NY7E6^L`5>!B)bs_u`@vCj zm&g06zOZ|LE}!7{LdYavbsb5NmBDn>VeG)X!RJEgSCIQD!kTfIC2KN&1dJ1`lWg>Z z%m-s5llB;xWr?hoe;<@GXk{gWCHFWAgQ1$A=4uXyszudY9!E0a-Y5ghqbOhc8KVzR z%Lx8Qd15Gjw^ORGTrO^B)RB(0jR?hCwmO2RnKc{FkKnn!Y)tK>-$}h= zy<@+VekZe<387VT>yV6~qK-#>&CCRK4PGiloI@SoJ*$p|2wUjTpuWhdI7mX= zUoyY~-R27tK|^oSJT<}xPtk7%==;(aW}}JXE7b?xnW`TJj=Gq#&6}dR??5-Q?V5Sc9{uJhQM|fyf;eKMLt~X%$B-g=HX+GxGi9_N{;v|Re<@i1zuvQfzr5q%ClJEN@ ztZi!zB)km?|1Om>i0`{lQ^<<(I@UoF7N8Kn8l@Ks)#P2YxNB<-WzcRichvtDD0x4F zmQYr$q@prp%$ROXcM>k=iT9#&B+9$tHPlml{tYHVs=ve>Nfm-`_qzX>q|5b8jAk<3 z#}Wj)yRFQ8?=UpUrI1cu6+((=xlhmLnEV$SJ&p|TMTJK7%_wx;9ca9UwHInmCLeBuNR@dOvU$eKZ4Y<+Q z(r}@J_{B2-vf~w6P*`iU)@pbM@>(#W3E$VO^KCo@@e6GV0SfI2+3k<)FaxAN6T>~wupX9ujs`4wm>xu+b}cn~GHdlWF*_LZ<9m=gd;i6-WzLjj%CX}V)GU#FaO%AnJ60c!Y3ud> zfLOLBCfZF8X4>lK+2n5#vQ{v?i$8Ns>KEBWIH93SVUhnGh)}>g9# zZbod@rz9uC|834TzDAZ~{ zOhS7Ej>^@4fBaNN80uR8%R{IPJQ;`&*^}3*Ut{A6(W7~)`gP=kEccn;KbCI^ZMb`O@RejCH69VRkI%pZRTb>A_RSpt7dgrf~TTzjgs`aSY~ z{utIhlta$$LAbjAUks1bbN%4CWVDALD4(1rE*&JhD32_LQofH*?$%@Vs`liR6Oh-M zQzpD?eLPJM5R=|+UTM~-B)2E8>{nFvxM|M+yV%N}N~&(xr=Pwizhv;p+_Le*CAvr8 zmwFH1AIA*G_<{3GJ?^}IdGY(V$Uc;Yx(C-B(QA4L-+YRsBYg8g%BCJFCf^UuIq!lv zXE@d0CzU=(tJG5|2ITfJLUcBA5i?!v1sPbT5SwLk_b79i?EO3@%VcqFbw86%{Ek9-cXL%j(><}P{t=!6kH4P8 zW1n9-M5EL>KTIt4&-w^RRDXcA_h~R+WyQnM^&no5Jpt!E&eTvWO${61a|ThzV`3QAP(gQjIIey#iK|^#FCnrR zQxQ3881EJPIQ0ReD_nS_fw1t(562aEPyUe*_Vq_Y*q05$UW&m;JndS>hr!tN&=Fq4 zT$%%pgd8ww8Vs9L^{|V3)A$K3@9kG^)IIs#BEG(cpS>D%s`Op&iP6ws(|26yxuRZ6 z?_%gw>HbQR1f;J%aJ*6BPYV$HkHkomFObqz_=pF!Z^{_w+KsWF=yZ_uoml`hzB)OM z0v!1k1imfzUSl_Lc>*V?ysaaGKfm~)v?iY(Z+i81a1D0{w=mJG6PkP5;?t{)B^R#f z$U`&^5vNyfL~c-$UpzC=xPaCB+yO$1U+E{lK{?Oc_N#!0ujsq62n7I z%W6be8xbw;o42_HUOY}%#WeisNx1@ge8@74Gu?;;m)oZ=gbpo`1!hXb2NL zBFPEPb1mY|$!GfB8W?P-=xyzzW&)8{sY1=6X7Kw-Y94;u{HSzoxYr#82X# zoCuxK40BWJDK7XWCZ|pA40EqAd6mg0nY?D|o-}n&nA|LLUtn_9)G;zq`oeejSBCen16%XE{5C6TXp(8nL zE|cXF&tpKu&(n+ep{ucn{Ls~H4)@oY9Am;aoZ|gNs=tgbfOPo$Zy>?yg>C;GRt!83 z0W?s1C(-No9=X>)Nc8%Ld-syH(8|_}XAUTmT3Tnimbs{wW`V9{E~-6XYMDc=2!(%c zYX3!|EqS{hfw^b|atd9`+}v?}XZCs4o?`MclXFZMNUDD%Y>7Dp)1>r^hP>gDfL67_ z9K$Wu6(%Q{{0b9ZZK=P@5>I~c>N+z14J7<=vdpLKT=WjR{+{^X#aX*JX4!fCm&Wef1w>}jr9a} z+gH>C0;>}sXf68TEnmXJf`Y^dAaco F{{gJcopt~K literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/flask/sansio/__pycache__/scaffold.cpython-310.pyc b/venv/Lib/site-packages/flask/sansio/__pycache__/scaffold.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7271c5df0ca4cc384c2d3a3af26739f1c611f9c8 GIT binary patch literal 23835 zcmdUXTWlOxnqF6Rv)OE3MA5oiw#)KGqK9s2Job2okv${JvaA^otyr`jx25SWb`{B{ znq72NwIp(>xmey4y%hj`r^?d@M3ZXS^M zSnuHKq2?ipkM|C*9%&x2N`7tS(XfTyCwfO$A8S5_oX2mKnol6#@%A8m(wjthY|%bk z^7j6w-F!;EO!)^^o@RcU`;6p1g4}1N)Nx7gL;8fIrzO1~>E|Th}9V?8e=oAI&lC`HSwL7kk}KjO4^% z&2wYFbv<;~*8C7T_1E0Ue=Bf3Nlm`d?fIA1qu5_Hc~fZq`HSat z4l?$1+|IJ^wbsG_EywGBZmSVhJ8yJ!9Gntrg4Z@xtS(F@7>1%Fp;G>wIl^mYO zYzDEPSc^$D^y5L;?*LWI4;P?{-!hTt1R}RfE2SS<-pHq=Ppw7U8}@3SjNPy90l z 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¨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 timedelta | None: + if value is None or isinstance(value, timedelta): + return value + + return timedelta(seconds=value) + + +class App(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 of the object assigned to :attr:`aborter`, created by + #: :meth:`create_aborter`. That object is called by + #: :func:`flask.abort` to raise HTTP errors, and can be + #: called directly as well. + #: + #: Defaults to :class:`werkzeug.exceptions.Aborter`. + #: + #: .. versionadded:: 2.2 + aborter_class = Aborter + + #: The class that is used for the Jinja environment. + #: + #: .. versionadded:: 0.11 + jinja_environment = Environment + + #: The class that is used for the :data:`~flask.g` instance. + #: + #: Example use cases for a custom class: + #: + #: 1. Store arbitrary attributes on flask.g. + #: 2. Add a property for lazy per-request database connectors. + #: 3. Return None instead of AttributeError on unexpected attributes. + #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g. + #: + #: In Flask 0.9 this property was called `request_globals_class` but it + #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the + #: flask.g object is now application context scoped. + #: + #: .. versionadded:: 0.10 + app_ctx_globals_class = _AppCtxGlobals + + #: The class that is used for the ``config`` attribute of this app. + #: Defaults to :class:`~flask.Config`. + #: + #: Example use cases for a custom class: + #: + #: 1. Default values for certain config options. + #: 2. Access to config values through attributes in addition to keys. + #: + #: .. versionadded:: 0.11 + config_class = Config + + #: The testing flag. Set this to ``True`` to enable the test mode of + #: Flask extensions (and in the future probably also Flask itself). + #: For example this might activate test helpers that have an + #: additional runtime cost which should not be enabled by default. + #: + #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the + #: default it's implicitly enabled. + #: + #: This attribute can also be configured from the config with the + #: ``TESTING`` configuration key. Defaults to ``False``. + testing = ConfigAttribute[bool]("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[t.Union[str, bytes, None]]("SECRET_KEY") + + #: A :class:`~datetime.timedelta` which is used to set the expiration + #: date of a permanent session. The default is 31 days which makes a + #: permanent session survive for roughly one month. + #: + #: This attribute can also be configured from the config with the + #: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to + #: ``timedelta(days=31)`` + permanent_session_lifetime = ConfigAttribute[timedelta]( + "PERMANENT_SESSION_LIFETIME", + get_converter=_make_timedelta, # type: ignore[arg-type] + ) + + json_provider_class: type[JSONProvider] = DefaultJSONProvider + """A subclass of :class:`~flask.json.provider.JSONProvider`. An + instance is created and assigned to :attr:`app.json` when creating + the app. + + The default, :class:`~flask.json.provider.DefaultJSONProvider`, uses + Python's built-in :mod:`json` library. A different provider can use + a different JSON library. + + .. versionadded:: 2.2 + """ + + #: Options that are passed to the Jinja environment in + #: :meth:`create_jinja_environment`. Changing these options after + #: the environment is created (accessing :attr:`jinja_env`) will + #: have no effect. + #: + #: .. versionchanged:: 1.1.0 + #: This is a ``dict`` instead of an ``ImmutableDict`` to allow + #: easier configuration. + #: + jinja_options: dict[str, t.Any] = {} + + #: The rule object to use for URL rules created. This is used by + #: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`. + #: + #: .. versionadded:: 0.7 + url_rule_class = Rule + + #: The map object to use for storing the URL rules and routing + #: configuration parameters. Defaults to :class:`werkzeug.routing.Map`. + #: + #: .. versionadded:: 1.1.0 + url_map_class = Map + + #: The :meth:`test_client` method creates an instance of this test + #: client class. Defaults to :class:`~flask.testing.FlaskClient`. + #: + #: .. versionadded:: 0.7 + test_client_class: type[FlaskClient] | None = None + + #: The :class:`~click.testing.CliRunner` subclass, by default + #: :class:`~flask.testing.FlaskCliRunner` that is used by + #: :meth:`test_cli_runner`. Its ``__init__`` method should take a + #: Flask app object as the first argument. + #: + #: .. versionadded:: 1.0 + test_cli_runner_class: type[FlaskCliRunner] | None = None + + default_config: dict[str, t.Any] + response_class: type[Response] + + def __init__( + self, + import_name: str, + static_url_path: str | None = None, + static_folder: str | os.PathLike[str] | None = "static", + static_host: str | None = None, + host_matching: bool = False, + subdomain_matching: bool = False, + template_folder: str | os.PathLike[str] | None = "templates", + instance_path: str | None = None, + instance_relative_config: bool = False, + root_path: str | None = None, + ) -> None: + super().__init__( + import_name=import_name, + static_folder=static_folder, + static_url_path=static_url_path, + template_folder=template_folder, + root_path=root_path, + ) + + if instance_path is None: + instance_path = self.auto_find_instance_path() + elif not os.path.isabs(instance_path): + raise ValueError( + "If an instance path is provided it must be absolute." + " A relative path was given instead." + ) + + #: Holds the path to the instance folder. + #: + #: .. versionadded:: 0.8 + self.instance_path = instance_path + + #: The configuration dictionary as :class:`Config`. This behaves + #: exactly like a regular dictionary but supports additional methods + #: to load a config from files. + self.config = self.make_config(instance_relative_config) + + #: An instance of :attr:`aborter_class` created by + #: :meth:`make_aborter`. This is called by :func:`flask.abort` + #: to raise HTTP errors, and can be called directly as well. + #: + #: .. versionadded:: 2.2 + #: Moved from ``flask.abort``, which calls this object. + self.aborter = self.make_aborter() + + self.json: JSONProvider = self.json_provider_class(self) + """Provides access to JSON methods. Functions in ``flask.json`` + will call methods on this provider when the application context + is active. Used for handling JSON requests and responses. + + An instance of :attr:`json_provider_class`. Can be customized by + changing that attribute on a subclass, or by assigning to this + attribute afterwards. + + The default, :class:`~flask.json.provider.DefaultJSONProvider`, + uses Python's built-in :mod:`json` library. A different provider + can use a different JSON library. + + .. versionadded:: 2.2 + """ + + #: A list of functions that are called by + #: :meth:`handle_url_build_error` when :meth:`.url_for` raises a + #: :exc:`~werkzeug.routing.BuildError`. Each function is called + #: with ``error``, ``endpoint`` and ``values``. If a function + #: returns ``None`` or raises a ``BuildError``, it is skipped. + #: Otherwise, its return value is returned by ``url_for``. + #: + #: .. versionadded:: 0.9 + self.url_build_error_handlers: list[ + t.Callable[[Exception, str, dict[str, t.Any]], str] + ] = [] + + #: A list of functions that are called when the application context + #: is destroyed. Since the application context is also torn down + #: if the request ends this is the place to store code that disconnects + #: from databases. + #: + #: .. versionadded:: 0.9 + self.teardown_appcontext_funcs: list[ft.TeardownCallable] = [] + + #: A list of shell context processor functions that should be run + #: when a shell context is created. + #: + #: .. versionadded:: 0.11 + self.shell_context_processors: list[ft.ShellContextProcessorCallable] = [] + + #: Maps registered blueprint names to blueprint objects. The + #: dict retains the order the blueprints were registered in. + #: Blueprints can be registered multiple times, this dict does + #: not track how often they were attached. + #: + #: .. versionadded:: 0.7 + self.blueprints: dict[str, Blueprint] = {} + + #: a place where extensions can store application specific state. For + #: example this is where an extension could store database engines and + #: similar things. + #: + #: The key must match the name of the extension module. For example in + #: case of a "Flask-Foo" extension in `flask_foo`, the key would be + #: ``'foo'``. + #: + #: .. versionadded:: 0.7 + self.extensions: dict[str, t.Any] = {} + + #: 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(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 + + def _check_setup_finished(self, f_name: str) -> None: + if self._got_first_request: + raise AssertionError( + f"The setup method '{f_name}' can no longer be called" + " on the application. It has already handled its first" + " request, any changes will not be applied" + " consistently.\n" + "Make sure all imports, decorators, functions, etc." + " needed to set up the application are done before" + " running it." + ) + + @cached_property + def name(self) -> str: + """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: str | None = getattr(sys.modules["__main__"], "__file__", None) + if fn is None: + return "__main__" + return os.path.splitext(os.path.basename(fn))[0] + return self.import_name + + @cached_property + def logger(self) -> logging.Logger: + """A standard Python :class:`~logging.Logger` for the app, with + the same name as :attr:`name`. + + In debug mode, the logger's :attr:`~logging.Logger.level` will + be set to :data:`~logging.DEBUG`. + + If there are no handlers configured, a default handler will be + added. See :doc:`/logging` for more information. + + .. versionchanged:: 1.1.0 + The logger takes the same name as :attr:`name` rather than + hard-coding ``"flask.app"``. + + .. versionchanged:: 1.0.0 + Behavior was simplified. The logger is always named + ``"flask.app"``. The level is only set during configuration, + it doesn't check ``app.debug`` each time. Only one format is + used, not different ones depending on ``app.debug``. No + handlers are removed, and a handler is only added if no + handlers are already configured. + + .. versionadded:: 0.3 + """ + return create_logger(self) + + @cached_property + def jinja_env(self) -> Environment: + """The Jinja environment used to load templates. + + The environment is created the first time this property is + accessed. Changing :attr:`jinja_options` after that will have no + effect. + """ + return self.create_jinja_environment() + + def create_jinja_environment(self) -> Environment: + raise NotImplementedError() + + def make_config(self, instance_relative: bool = False) -> Config: + """Used to create the config attribute by the Flask constructor. + The `instance_relative` parameter is passed in from the constructor + of Flask (there named `instance_relative_config`) and indicates if + the config should be relative to the instance path or the root path + of the application. + + .. versionadded:: 0.8 + """ + root_path = self.root_path + if instance_relative: + root_path = self.instance_path + defaults = dict(self.default_config) + defaults["DEBUG"] = get_debug_flag() + return self.config_class(root_path, defaults) + + def make_aborter(self) -> Aborter: + """Create the object to assign to :attr:`aborter`. That object + is called by :func:`flask.abort` to raise HTTP errors, and can + be called directly as well. + + By default, this creates an instance of :attr:`aborter_class`, + which defaults to :class:`werkzeug.exceptions.Aborter`. + + .. versionadded:: 2.2 + """ + return self.aborter_class() + + def auto_find_instance_path(self) -> str: + """Tries to locate the instance path if it was not provided to the + constructor of the application class. It will basically calculate + the path to a folder named ``instance`` next to your main file or + the package. + + .. versionadded:: 0.8 + """ + prefix, package_path = find_package(self.import_name) + if prefix is None: + return os.path.join(package_path, "instance") + return os.path.join(prefix, "var", f"{self.name}-instance") + + def create_global_jinja_loader(self) -> DispatchingJinjaLoader: + """Creates the loader for the Jinja 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 | None) -> bool: + """Returns ``True`` if autoescaping should be active for the given + template name. If no template name is given, returns `True`. + + .. versionchanged:: 2.2 + Autoescaping is now enabled by default for ``.svg`` files. + + .. versionadded:: 0.5 + """ + if filename is None: + return True + return filename.endswith((".html", ".htm", ".xml", ".xhtml", ".svg")) + + @property + def debug(self) -> bool: + """Whether debug mode is enabled. When using ``flask run`` to start the + development server, an interactive debugger will be shown for unhandled + exceptions, and the server will be reloaded when code changes. This maps to the + :data:`DEBUG` config key. It may not behave as expected if set late. + + **Do not enable debug mode when deploying in production.** + + Default: ``False`` + """ + return self.config["DEBUG"] # type: ignore[no-any-return] + + @debug.setter + def debug(self, value: bool) -> None: + self.config["DEBUG"] = value + + if self.config["TEMPLATES_AUTO_RELOAD"] is None: + self.jinja_env.auto_reload = value + + @setupmethod + def register_blueprint(self, blueprint: Blueprint, **options: t.Any) -> None: + """Register a :class:`~flask.Blueprint` on the application. Keyword + arguments passed to this method will override the defaults set on the + blueprint. + + Calls the blueprint's :meth:`~flask.Blueprint.register` method after + recording the blueprint in the application's :attr:`blueprints`. + + :param blueprint: The blueprint to register. + :param url_prefix: Blueprint routes will be prefixed with this. + :param subdomain: Blueprint routes will match on this subdomain. + :param url_defaults: Blueprint routes will use these default values for + view arguments. + :param options: Additional keyword arguments are passed to + :class:`~flask.blueprints.BlueprintSetupState`. They can be + accessed in :meth:`~flask.Blueprint.record` callbacks. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionadded:: 0.7 + """ + blueprint.register(self, options) + + def iter_blueprints(self) -> t.ValuesView[Blueprint]: + """Iterates over all blueprints by the order they were registered. + + .. versionadded:: 0.11 + """ + return self.blueprints.values() + + @setupmethod + def add_url_rule( + self, + rule: str, + endpoint: str | None = None, + view_func: ft.RouteCallable | None = None, + provide_automatic_options: bool | None = None, + **options: t.Any, + ) -> None: + if endpoint is None: + endpoint = _endpoint_from_view_func(view_func) # type: ignore + options["endpoint"] = endpoint + methods = options.pop("methods", None) + + # if the methods are not given and the view_func object knows its + # methods we can use that instead. If neither exists, we go with + # a tuple of only ``GET`` as default. + if methods is None: + methods = getattr(view_func, "methods", None) or ("GET",) + if isinstance(methods, str): + raise TypeError( + "Allowed methods must be a list of strings, for" + ' example: @app.route(..., methods=["POST"])' + ) + methods = {item.upper() for item in methods} + + # Methods that should always be added + required_methods: set[str] = 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 and self.config["PROVIDE_AUTOMATIC_OPTIONS"]: + provide_automatic_options = True + required_methods.add("OPTIONS") + else: + provide_automatic_options = False + + # Add the required methods now. + methods |= required_methods + + rule_obj = self.url_rule_class(rule, methods=methods, **options) + rule_obj.provide_automatic_options = provide_automatic_options # type: ignore[attr-defined] + + self.url_map.add(rule_obj) + if view_func is not None: + old_func = self.view_functions.get(endpoint) + if old_func is not None and old_func != view_func: + raise AssertionError( + "View function mapping is overwriting an existing" + f" endpoint function: {endpoint}" + ) + self.view_functions[endpoint] = view_func + + @setupmethod + def template_filter( + self, name: str | None = None + ) -> t.Callable[[T_template_filter], T_template_filter]: + """A decorator that is used to register custom template filter. + You can specify a name for the filter, otherwise the function + name will be used. Example:: + + @app.template_filter() + def reverse(s): + return s[::-1] + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def decorator(f: T_template_filter) -> T_template_filter: + self.add_template_filter(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_filter( + self, f: ft.TemplateFilterCallable, name: str | None = None + ) -> None: + """Register a custom template filter. Works exactly like the + :meth:`template_filter` decorator. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + self.jinja_env.filters[name or f.__name__] = f + + @setupmethod + def template_test( + self, name: str | None = None + ) -> t.Callable[[T_template_test], T_template_test]: + """A decorator that is used to register custom template test. + You can specify a name for the test, otherwise the function + name will be used. Example:: + + @app.template_test() + def is_prime(n): + if n == 2: + return True + for i in range(2, int(math.ceil(math.sqrt(n))) + 1): + if n % i == 0: + return False + return True + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def decorator(f: T_template_test) -> T_template_test: + self.add_template_test(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_test( + self, f: ft.TemplateTestCallable, name: str | None = None + ) -> None: + """Register a custom template test. Works exactly like the + :meth:`template_test` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + self.jinja_env.tests[name or f.__name__] = f + + @setupmethod + def template_global( + self, name: str | None = None + ) -> t.Callable[[T_template_global], T_template_global]: + """A decorator that is used to register a custom template global function. + You can specify a name for the global function, otherwise the function + name will be used. Example:: + + @app.template_global() + def double(n): + return 2 * n + + .. versionadded:: 0.10 + + :param name: the optional name of the global function, otherwise the + function name will be used. + """ + + def decorator(f: T_template_global) -> T_template_global: + self.add_template_global(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_global( + self, f: ft.TemplateGlobalCallable, name: str | None = None + ) -> None: + """Register a custom template global function. Works exactly like the + :meth:`template_global` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the global function, otherwise the + function name will be used. + """ + self.jinja_env.globals[name or f.__name__] = f + + @setupmethod + def teardown_appcontext(self, f: T_teardown) -> T_teardown: + """Registers a function to be called when the application + context is popped. The application context is typically popped + after the request context for each request, at the end of CLI + commands, or after a manually pushed context ends. + + .. code-block:: python + + with app.app_context(): + ... + + When the ``with`` block exits (or ``ctx.pop()`` is called), the + teardown functions are called just before the app context is + made inactive. Since a request context typically also manages an + application context it would also be called when you pop a + request context. + + When a teardown function was called because of an unhandled + exception it will be passed an error object. If an + :meth:`errorhandler` is registered, it will handle the exception + and the teardown will not receive it. + + Teardown functions must avoid raising exceptions. If they + execute code that might fail they must surround that code with a + ``try``/``except`` block and log any errors. + + The return values of teardown functions are ignored. + + .. versionadded:: 0.9 + """ + self.teardown_appcontext_funcs.append(f) + return f + + @setupmethod + def shell_context_processor( + self, f: T_shell_context_processor + ) -> T_shell_context_processor: + """Registers a shell context processor function. + + .. versionadded:: 0.11 + """ + self.shell_context_processors.append(f) + return f + + def _find_error_handler( + self, e: Exception, blueprints: list[str] + ) -> ft.ErrorHandlerCallable | None: + """Return a registered error handler for an exception in this order: + blueprint handler for a specific code, app handler for a specific code, + blueprint handler for an exception class, app handler for an exception + class, or ``None`` if a suitable handler is not found. + """ + exc_class, code = self._get_exc_class_and_code(type(e)) + names = (*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 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 should_ignore_error(self, error: BaseException | None) -> bool: + """This is called to figure out if an error should be ignored + or not as far as the teardown system is concerned. If this + function returns ``True`` then the teardown handlers will not be + passed the error. + + .. versionadded:: 0.10 + """ + return False + + def redirect(self, location: str, code: int = 302) -> BaseResponse: + """Create a redirect response object. + + This is called by :func:`flask.redirect`, and can be called + directly as well. + + :param location: The URL to redirect to. + :param code: The status code for the redirect. + + .. versionadded:: 2.2 + Moved from ``flask.redirect``, which calls this method. + """ + return _wz_redirect( + location, + code=code, + Response=self.response_class, # type: ignore[arg-type] + ) + + def inject_url_defaults(self, endpoint: str, values: dict[str, t.Any]) -> None: + """Injects the URL defaults for the given endpoint directly into + the values dictionary passed. This is used internally and + automatically called on URL building. + + .. versionadded:: 0.7 + """ + names: t.Iterable[str | None] = (None,) + + # url_for may be called outside a request context, parse the + # passed endpoint instead of using request.blueprints. + if "." in endpoint: + names = chain( + names, reversed(_split_blueprint_path(endpoint.rpartition(".")[0])) + ) + + for name in names: + if name in self.url_default_functions: + for func in self.url_default_functions[name]: + func(endpoint, values) + + def handle_url_build_error( + self, error: BuildError, endpoint: str, values: dict[str, t.Any] + ) -> str: + """Called by :meth:`.url_for` if a + :exc:`~werkzeug.routing.BuildError` was raised. If this returns + a value, it will be returned by ``url_for``, otherwise the error + will be re-raised. + + Each function in :attr:`url_build_error_handlers` is called with + ``error``, ``endpoint`` and ``values``. If a function returns + ``None`` or raises a ``BuildError``, it is skipped. Otherwise, + its return value is returned by ``url_for``. + + :param error: The active ``BuildError`` being handled. + :param endpoint: The endpoint being built. + :param values: The keyword arguments passed to ``url_for``. + """ + for handler in self.url_build_error_handlers: + try: + rv = handler(error, endpoint, values) + except BuildError as e: + # make error available outside except block + error = e + else: + if rv is not None: + return rv + + # Re-raise if called with an active exception, otherwise raise + # the passed in exception. + if error is sys.exc_info()[1]: + raise + + raise error diff --git a/venv/Lib/site-packages/flask/sansio/blueprints.py b/venv/Lib/site-packages/flask/sansio/blueprints.py new file mode 100644 index 0000000..4f912cc --- /dev/null +++ b/venv/Lib/site-packages/flask/sansio/blueprints.py @@ -0,0 +1,632 @@ +from __future__ import annotations + +import os +import typing as t +from collections import defaultdict +from functools import update_wrapper + +from .. import typing as ft +from .scaffold import _endpoint_from_view_func +from .scaffold import _sentinel +from .scaffold import Scaffold +from .scaffold import setupmethod + +if t.TYPE_CHECKING: # pragma: no cover + from .app import App + +DeferredSetupFunction = t.Callable[["BlueprintSetupState"], None] +T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable[t.Any]) +T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable) +T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable) +T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) +T_template_context_processor = t.TypeVar( + "T_template_context_processor", bound=ft.TemplateContextProcessorCallable +) +T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable) +T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable) +T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable) +T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable) +T_url_value_preprocessor = t.TypeVar( + "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable +) + + +class BlueprintSetupState: + """Temporary holder object for registering a blueprint with the + application. An instance of this class is created by the + :meth:`~flask.Blueprint.make_setup_state` method and later passed + to all register callback functions. + """ + + def __init__( + self, + blueprint: Blueprint, + app: App, + options: t.Any, + first_registration: bool, + ) -> None: + #: a reference to the current application + self.app = app + + #: a reference to the blueprint that created this setup state. + self.blueprint = blueprint + + #: a dictionary with all options that were passed to the + #: :meth:`~flask.Flask.register_blueprint` method. + self.options = options + + #: as blueprints can be registered multiple times with the + #: application and not everything wants to be registered + #: multiple times on it, this attribute can be used to figure + #: out if the blueprint was registered in the past already. + self.first_registration = first_registration + + subdomain = self.options.get("subdomain") + if subdomain is None: + subdomain = self.blueprint.subdomain + + #: The subdomain that the blueprint should be active for, ``None`` + #: otherwise. + self.subdomain = subdomain + + url_prefix = self.options.get("url_prefix") + if url_prefix is None: + url_prefix = self.blueprint.url_prefix + #: The prefix that should be used for all URLs defined on the + #: blueprint. + self.url_prefix = url_prefix + + self.name = self.options.get("name", blueprint.name) + self.name_prefix = self.options.get("name_prefix", "") + + #: A dictionary with URL defaults that is added to each and every + #: URL that was defined with the blueprint. + self.url_defaults = dict(self.blueprint.url_values_defaults) + self.url_defaults.update(self.options.get("url_defaults", ())) + + def add_url_rule( + self, + rule: str, + endpoint: str | None = None, + view_func: ft.RouteCallable | None = None, + **options: t.Any, + ) -> None: + """A helper method to register a rule (and optionally a view function) + to the application. The endpoint is automatically prefixed with the + blueprint's name. + """ + if self.url_prefix is not None: + if rule: + rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/"))) + else: + rule = self.url_prefix + options.setdefault("subdomain", self.subdomain) + if endpoint is None: + endpoint = _endpoint_from_view_func(view_func) # type: ignore + defaults = self.url_defaults + if "defaults" in options: + defaults = dict(defaults, **options.pop("defaults")) + + self.app.add_url_rule( + rule, + f"{self.name_prefix}.{self.name}.{endpoint}".lstrip("."), + view_func, + defaults=defaults, + **options, + ) + + +class Blueprint(Scaffold): + """Represents a blueprint, a collection of routes and other + app-related functions that can be registered on a real application + later. + + A blueprint is an object that allows defining application functions + without requiring an application object ahead of time. It uses the + same decorators as :class:`~flask.Flask`, but defers the need for an + application by recording them for later registration. + + Decorating a function with a blueprint creates a deferred function + that is called with :class:`~flask.blueprints.BlueprintSetupState` + when the blueprint is registered on an application. + + See :doc:`/blueprints` for more information. + + :param name: The name of the blueprint. Will be prepended to each + endpoint name. + :param import_name: The name of the blueprint package, usually + ``__name__``. This helps locate the ``root_path`` for the + blueprint. + :param static_folder: A folder with static files that should be + served by the blueprint's static route. The path is relative to + the blueprint's root path. Blueprint static files are disabled + by default. + :param static_url_path: The url to serve static files from. + Defaults to ``static_folder``. If the blueprint does not have + a ``url_prefix``, the app's static route will take precedence, + and the blueprint's static files won't be accessible. + :param template_folder: A folder with templates that should be added + to the app's template search path. The path is relative to the + blueprint's root path. Blueprint templates are disabled by + default. Blueprint templates have a lower precedence than those + in the app's templates folder. + :param url_prefix: A path to prepend to all of the blueprint's URLs, + to make them distinct from the rest of the app's routes. + :param subdomain: A subdomain that blueprint routes will match on by + default. + :param url_defaults: A dict of default values that blueprint routes + will receive by default. + :param root_path: By default, the blueprint will automatically set + this based on ``import_name``. In certain situations this + automatic detection can fail, so the path can be specified + manually instead. + + .. versionchanged:: 1.1.0 + Blueprints have a ``cli`` group to register nested CLI commands. + The ``cli_group`` parameter controls the name of the group under + the ``flask`` command. + + .. versionadded:: 0.7 + """ + + _got_registered_once = False + + def __init__( + self, + name: str, + import_name: str, + static_folder: str | os.PathLike[str] | None = None, + static_url_path: str | None = None, + template_folder: str | os.PathLike[str] | None = None, + url_prefix: str | None = None, + subdomain: str | None = None, + url_defaults: dict[str, t.Any] | None = None, + root_path: str | None = None, + cli_group: str | None = _sentinel, # type: ignore[assignment] + ): + super().__init__( + import_name=import_name, + static_folder=static_folder, + static_url_path=static_url_path, + template_folder=template_folder, + root_path=root_path, + ) + + if not name: + raise ValueError("'name' may not be empty.") + + if "." in name: + raise ValueError("'name' may not contain a dot '.' character.") + + self.name = name + self.url_prefix = url_prefix + self.subdomain = subdomain + self.deferred_functions: list[DeferredSetupFunction] = [] + + if url_defaults is None: + url_defaults = {} + + self.url_values_defaults = url_defaults + self.cli_group = cli_group + self._blueprints: list[tuple[Blueprint, dict[str, t.Any]]] = [] + + def _check_setup_finished(self, f_name: str) -> None: + if self._got_registered_once: + raise AssertionError( + f"The setup method '{f_name}' can no longer be called on the blueprint" + f" '{self.name}'. It has already been registered at least once, any" + " changes will not be applied consistently.\n" + "Make sure all imports, decorators, functions, etc. needed to set up" + " the blueprint are done before registering it." + ) + + @setupmethod + def record(self, func: DeferredSetupFunction) -> None: + """Registers a function that is called when the blueprint is + registered on the application. This function is called with the + state as argument as returned by the :meth:`make_setup_state` + method. + """ + self.deferred_functions.append(func) + + @setupmethod + def record_once(self, func: DeferredSetupFunction) -> None: + """Works like :meth:`record` but wraps the function in another + function that will ensure the function is only called once. If the + blueprint is registered a second time on the application, the + function passed is not called. + """ + + def wrapper(state: BlueprintSetupState) -> None: + if state.first_registration: + func(state) + + self.record(update_wrapper(wrapper, func)) + + def make_setup_state( + self, app: App, options: dict[str, t.Any], first_registration: bool = False + ) -> BlueprintSetupState: + """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState` + object that is later passed to the register callback functions. + Subclasses can override this to return a subclass of the setup state. + """ + return BlueprintSetupState(self, app, options, first_registration) + + @setupmethod + def register_blueprint(self, blueprint: Blueprint, **options: t.Any) -> None: + """Register a :class:`~flask.Blueprint` on this blueprint. Keyword + arguments passed to this method will override the defaults set + on the blueprint. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionadded:: 2.0 + """ + if blueprint is self: + raise ValueError("Cannot register a blueprint on itself") + self._blueprints.append((blueprint, options)) + + def register(self, app: App, options: dict[str, t.Any]) -> None: + """Called by :meth:`Flask.register_blueprint` to register all + views and callbacks registered on the blueprint with the + application. Creates a :class:`.BlueprintSetupState` and calls + each :meth:`record` callback with it. + + :param app: The application this blueprint is being registered + with. + :param options: Keyword arguments forwarded from + :meth:`~Flask.register_blueprint`. + + .. versionchanged:: 2.3 + Nested blueprints now correctly apply subdomains. + + .. versionchanged:: 2.1 + Registering the same blueprint with the same name multiple + times is an error. + + .. versionchanged:: 2.0.1 + Nested blueprints are registered with their dotted name. + This allows different blueprints with the same name to be + nested at different locations. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + """ + name_prefix = options.get("name_prefix", "") + self_name = options.get("name", self.name) + name = f"{name_prefix}.{self_name}".lstrip(".") + + if name in app.blueprints: + bp_desc = "this" if app.blueprints[name] is self else "a different" + existing_at = f" '{name}'" if self_name != name else "" + + raise ValueError( + f"The name '{self_name}' is already registered for" + f" {bp_desc} blueprint{existing_at}. Use 'name=' to" + f" provide a unique name." + ) + + first_bp_registration = not any(bp is self for bp in app.blueprints.values()) + first_name_registration = name not in app.blueprints + + app.blueprints[name] = self + self._got_registered_once = True + state = self.make_setup_state(app, options, first_bp_registration) + + if self.has_static_folder: + state.add_url_rule( + f"{self.static_url_path}/", + view_func=self.send_static_file, # type: ignore[attr-defined] + endpoint="static", + ) + + # Merge blueprint data into parent. + if first_bp_registration or first_name_registration: + self._merge_blueprint_funcs(app, name) + + for deferred in self.deferred_functions: + deferred(state) + + cli_resolved_group = options.get("cli_group", self.cli_group) + + if self.cli.commands: + if cli_resolved_group is None: + app.cli.commands.update(self.cli.commands) + elif cli_resolved_group is _sentinel: + self.cli.name = name + app.cli.add_command(self.cli) + else: + self.cli.name = cli_resolved_group + app.cli.add_command(self.cli) + + for blueprint, bp_options in self._blueprints: + bp_options = bp_options.copy() + bp_url_prefix = bp_options.get("url_prefix") + bp_subdomain = bp_options.get("subdomain") + + if bp_subdomain is None: + bp_subdomain = blueprint.subdomain + + if state.subdomain is not None and bp_subdomain is not None: + bp_options["subdomain"] = bp_subdomain + "." + state.subdomain + elif bp_subdomain is not None: + bp_options["subdomain"] = bp_subdomain + elif state.subdomain is not None: + bp_options["subdomain"] = state.subdomain + + if bp_url_prefix is None: + bp_url_prefix = blueprint.url_prefix + + if state.url_prefix is not None and bp_url_prefix is not None: + bp_options["url_prefix"] = ( + state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/") + ) + elif bp_url_prefix is not None: + bp_options["url_prefix"] = bp_url_prefix + elif state.url_prefix is not None: + bp_options["url_prefix"] = state.url_prefix + + bp_options["name_prefix"] = name + blueprint.register(app, bp_options) + + def _merge_blueprint_funcs(self, app: App, name: str) -> None: + def extend( + bp_dict: dict[ft.AppOrBlueprintKey, list[t.Any]], + parent_dict: dict[ft.AppOrBlueprintKey, list[t.Any]], + ) -> None: + 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) + + @setupmethod + def add_url_rule( + self, + rule: str, + endpoint: str | None = None, + view_func: ft.RouteCallable | None = None, + provide_automatic_options: bool | None = None, + **options: t.Any, + ) -> None: + """Register a URL rule with the blueprint. See :meth:`.Flask.add_url_rule` for + full documentation. + + The URL rule is prefixed with the blueprint's URL prefix. The endpoint name, + used with :func:`url_for`, is prefixed with the blueprint's name. + """ + if endpoint and "." in endpoint: + raise ValueError("'endpoint' may not contain a dot '.' character.") + + if view_func and hasattr(view_func, "__name__") and "." in view_func.__name__: + raise ValueError("'view_func' name may not contain a dot '.' character.") + + self.record( + lambda s: s.add_url_rule( + rule, + endpoint, + view_func, + provide_automatic_options=provide_automatic_options, + **options, + ) + ) + + @setupmethod + def app_template_filter( + self, name: str | None = None + ) -> t.Callable[[T_template_filter], T_template_filter]: + """Register a template filter, available in any template rendered by the + application. Equivalent to :meth:`.Flask.template_filter`. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def decorator(f: T_template_filter) -> T_template_filter: + self.add_app_template_filter(f, name=name) + return f + + return decorator + + @setupmethod + def add_app_template_filter( + self, f: ft.TemplateFilterCallable, name: str | None = None + ) -> None: + """Register a template filter, available in any template rendered by the + application. Works like the :meth:`app_template_filter` decorator. Equivalent to + :meth:`.Flask.add_template_filter`. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.filters[name or f.__name__] = f + + self.record_once(register_template) + + @setupmethod + def app_template_test( + self, name: str | None = None + ) -> t.Callable[[T_template_test], T_template_test]: + """Register a template test, available in any template rendered by the + application. Equivalent to :meth:`.Flask.template_test`. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def decorator(f: T_template_test) -> T_template_test: + self.add_app_template_test(f, name=name) + return f + + return decorator + + @setupmethod + def add_app_template_test( + self, f: ft.TemplateTestCallable, name: str | None = None + ) -> None: + """Register a template test, available in any template rendered by the + application. Works like the :meth:`app_template_test` decorator. Equivalent to + :meth:`.Flask.add_template_test`. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.tests[name or f.__name__] = f + + self.record_once(register_template) + + @setupmethod + def app_template_global( + self, name: str | None = None + ) -> t.Callable[[T_template_global], T_template_global]: + """Register a template global, available in any template rendered by the + application. Equivalent to :meth:`.Flask.template_global`. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + + def decorator(f: T_template_global) -> T_template_global: + self.add_app_template_global(f, name=name) + return f + + return decorator + + @setupmethod + def add_app_template_global( + self, f: ft.TemplateGlobalCallable, name: str | None = None + ) -> None: + """Register a template global, available in any template rendered by the + application. Works like the :meth:`app_template_global` decorator. Equivalent to + :meth:`.Flask.add_template_global`. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.globals[name or f.__name__] = f + + self.record_once(register_template) + + @setupmethod + def before_app_request(self, f: T_before_request) -> T_before_request: + """Like :meth:`before_request`, but before every request, not only those handled + by the blueprint. Equivalent to :meth:`.Flask.before_request`. + """ + self.record_once( + lambda s: s.app.before_request_funcs.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def after_app_request(self, f: T_after_request) -> T_after_request: + """Like :meth:`after_request`, but after every request, not only those handled + by the blueprint. Equivalent to :meth:`.Flask.after_request`. + """ + self.record_once( + lambda s: s.app.after_request_funcs.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def teardown_app_request(self, f: T_teardown) -> T_teardown: + """Like :meth:`teardown_request`, but after every request, not only those + handled by the blueprint. Equivalent to :meth:`.Flask.teardown_request`. + """ + self.record_once( + lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def app_context_processor( + self, f: T_template_context_processor + ) -> T_template_context_processor: + """Like :meth:`context_processor`, but for templates rendered by every view, not + only by the blueprint. Equivalent to :meth:`.Flask.context_processor`. + """ + self.record_once( + lambda s: s.app.template_context_processors.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def app_errorhandler( + self, code: type[Exception] | int + ) -> t.Callable[[T_error_handler], T_error_handler]: + """Like :meth:`errorhandler`, but for every request, not only those handled by + the blueprint. Equivalent to :meth:`.Flask.errorhandler`. + """ + + def decorator(f: T_error_handler) -> T_error_handler: + def from_blueprint(state: BlueprintSetupState) -> None: + state.app.errorhandler(code)(f) + + self.record_once(from_blueprint) + return f + + return decorator + + @setupmethod + def app_url_value_preprocessor( + self, f: T_url_value_preprocessor + ) -> T_url_value_preprocessor: + """Like :meth:`url_value_preprocessor`, but for every request, not only those + handled by the blueprint. Equivalent to :meth:`.Flask.url_value_preprocessor`. + """ + self.record_once( + lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def app_url_defaults(self, f: T_url_defaults) -> T_url_defaults: + """Like :meth:`url_defaults`, but for every request, not only those handled by + the blueprint. Equivalent to :meth:`.Flask.url_defaults`. + """ + self.record_once( + lambda s: s.app.url_default_functions.setdefault(None, []).append(f) + ) + return f diff --git a/venv/Lib/site-packages/flask/sansio/scaffold.py b/venv/Lib/site-packages/flask/sansio/scaffold.py new file mode 100644 index 0000000..0e96f15 --- /dev/null +++ b/venv/Lib/site-packages/flask/sansio/scaffold.py @@ -0,0 +1,792 @@ +from __future__ import annotations + +import importlib.util +import os +import pathlib +import sys +import typing as t +from collections import defaultdict +from functools import update_wrapper + +from jinja2 import BaseLoader +from jinja2 import FileSystemLoader +from werkzeug.exceptions import default_exceptions +from werkzeug.exceptions import HTTPException +from werkzeug.utils import cached_property + +from .. import typing as ft +from ..helpers import get_root_path +from ..templating import _default_template_ctx_processor + +if t.TYPE_CHECKING: # pragma: no cover + from click import Group + +# a singleton sentinel value for parameter defaults +_sentinel = object() + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable[t.Any]) +T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable) +T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable) +T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) +T_template_context_processor = t.TypeVar( + "T_template_context_processor", bound=ft.TemplateContextProcessorCallable +) +T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable) +T_url_value_preprocessor = t.TypeVar( + "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable +) +T_route = t.TypeVar("T_route", bound=ft.RouteCallable) + + +def setupmethod(f: F) -> F: + f_name = f.__name__ + + def wrapper_func(self: Scaffold, *args: t.Any, **kwargs: t.Any) -> t.Any: + self._check_setup_finished(f_name) + return f(self, *args, **kwargs) + + return t.cast(F, update_wrapper(wrapper_func, f)) + + +class Scaffold: + """Common behavior shared between :class:`~flask.Flask` and + :class:`~flask.blueprints.Blueprint`. + + :param import_name: The import name of the module where this object + is defined. Usually :attr:`__name__` should be used. + :param static_folder: Path to a folder of static files to serve. + If this is set, a static route will be added. + :param static_url_path: URL prefix for the static route. + :param template_folder: Path to a folder containing template files. + for rendering. If this is set, a Jinja loader will be added. + :param root_path: The path that static, template, and resource files + are relative to. Typically not set, it is discovered based on + the ``import_name``. + + .. versionadded:: 2.0 + """ + + cli: Group + name: str + _static_folder: str | None = None + _static_url_path: str | None = None + + def __init__( + self, + import_name: str, + static_folder: str | os.PathLike[str] | None = None, + static_url_path: str | None = None, + template_folder: str | os.PathLike[str] | None = None, + root_path: str | None = None, + ): + #: The name of the package or module that this object belongs + #: to. Do not change this once it is set by the constructor. + self.import_name = import_name + + self.static_folder = static_folder + self.static_url_path = static_url_path + + #: The path to the templates folder, relative to + #: :attr:`root_path`, to add to the template loader. ``None`` if + #: templates should not be added. + self.template_folder = template_folder + + if root_path is None: + root_path = get_root_path(self.import_name) + + #: Absolute path to the package on the filesystem. Used to look + #: up resources contained in the package. + self.root_path = root_path + + #: A dictionary mapping endpoint names to view functions. + #: + #: To register a view function, use the :meth:`route` decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.view_functions: dict[str, ft.RouteCallable] = {} + + #: A data structure of registered error handlers, in the format + #: ``{scope: {code: {class: handler}}}``. The ``scope`` key is + #: the name of a blueprint the handlers are active for, or + #: ``None`` for all requests. The ``code`` key is the HTTP + #: status code for ``HTTPException``, or ``None`` for + #: other exceptions. The innermost dictionary maps exception + #: classes to handler functions. + #: + #: To register an error handler, use the :meth:`errorhandler` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.error_handler_spec: dict[ + ft.AppOrBlueprintKey, + dict[int | None, dict[type[Exception], ft.ErrorHandlerCallable]], + ] = defaultdict(lambda: defaultdict(dict)) + + #: A data structure of functions to call at the beginning of + #: each request, in the format ``{scope: [functions]}``. The + #: ``scope`` key is the name of a blueprint the functions are + #: active for, or ``None`` for all requests. + #: + #: To register a function, use the :meth:`before_request` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.before_request_funcs: dict[ + ft.AppOrBlueprintKey, list[ft.BeforeRequestCallable] + ] = defaultdict(list) + + #: A data structure of functions to call at the end of each + #: request, in the format ``{scope: [functions]}``. The + #: ``scope`` key is the name of a blueprint the functions are + #: active for, or ``None`` for all requests. + #: + #: To register a function, use the :meth:`after_request` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.after_request_funcs: dict[ + ft.AppOrBlueprintKey, list[ft.AfterRequestCallable[t.Any]] + ] = defaultdict(list) + + #: A data structure of functions to call at the end of each + #: request even if an exception is raised, in the format + #: ``{scope: [functions]}``. The ``scope`` key is the name of a + #: blueprint the functions are active for, or ``None`` for all + #: requests. + #: + #: To register a function, use the :meth:`teardown_request` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.teardown_request_funcs: dict[ + ft.AppOrBlueprintKey, list[ft.TeardownCallable] + ] = defaultdict(list) + + #: A data structure of functions to call to pass extra context + #: values when rendering templates, in the format + #: ``{scope: [functions]}``. The ``scope`` key is the name of a + #: blueprint the functions are active for, or ``None`` for all + #: requests. + #: + #: To register a function, use the :meth:`context_processor` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.template_context_processors: dict[ + ft.AppOrBlueprintKey, list[ft.TemplateContextProcessorCallable] + ] = defaultdict(list, {None: [_default_template_ctx_processor]}) + + #: A data structure of functions to call to modify the keyword + #: arguments passed to the view function, in the format + #: ``{scope: [functions]}``. The ``scope`` key is the name of a + #: blueprint the functions are active for, or ``None`` for all + #: requests. + #: + #: To register a function, use the + #: :meth:`url_value_preprocessor` decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.url_value_preprocessors: dict[ + ft.AppOrBlueprintKey, + list[ft.URLValuePreprocessorCallable], + ] = defaultdict(list) + + #: A data structure of functions to call to modify the keyword + #: arguments when generating URLs, in the format + #: ``{scope: [functions]}``. The ``scope`` key is the name of a + #: blueprint the functions are active for, or ``None`` for all + #: requests. + #: + #: To register a function, use the :meth:`url_defaults` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.url_default_functions: dict[ + ft.AppOrBlueprintKey, list[ft.URLDefaultCallable] + ] = defaultdict(list) + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.name!r}>" + + def _check_setup_finished(self, f_name: str) -> None: + raise NotImplementedError + + @property + def static_folder(self) -> str | None: + """The absolute path to the configured static folder. ``None`` + if no static folder is set. + """ + if self._static_folder is not None: + return os.path.join(self.root_path, self._static_folder) + else: + return None + + @static_folder.setter + def static_folder(self, value: str | os.PathLike[str] | None) -> None: + if value is not None: + value = os.fspath(value).rstrip(r"\/") + + self._static_folder = value + + @property + def has_static_folder(self) -> bool: + """``True`` if :attr:`static_folder` is set. + + .. versionadded:: 0.5 + """ + return self.static_folder is not None + + @property + def static_url_path(self) -> str | None: + """The URL prefix that the static route will be accessible from. + + If it was not configured during init, it is derived from + :attr:`static_folder`. + """ + if self._static_url_path is not None: + return self._static_url_path + + if self.static_folder is not None: + basename = os.path.basename(self.static_folder) + return f"/{basename}".rstrip("/") + + return None + + @static_url_path.setter + def static_url_path(self, value: str | None) -> None: + if value is not None: + value = value.rstrip("/") + + self._static_url_path = value + + @cached_property + def jinja_loader(self) -> BaseLoader | None: + """The Jinja loader for this object's templates. By default this + is a class :class:`jinja2.loaders.FileSystemLoader` to + :attr:`template_folder` if it is set. + + .. versionadded:: 0.5 + """ + if self.template_folder is not None: + return FileSystemLoader(os.path.join(self.root_path, self.template_folder)) + else: + return None + + def _method_route( + self, + method: str, + rule: str, + options: dict[str, t.Any], + ) -> t.Callable[[T_route], T_route]: + if "methods" in options: + raise TypeError("Use the 'route' decorator to use the 'methods' argument.") + + return self.route(rule, methods=[method], **options) + + @setupmethod + def get(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["GET"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("GET", rule, options) + + @setupmethod + def post(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["POST"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("POST", rule, options) + + @setupmethod + def put(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["PUT"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("PUT", rule, options) + + @setupmethod + def delete(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["DELETE"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("DELETE", rule, options) + + @setupmethod + def patch(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["PATCH"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("PATCH", rule, options) + + @setupmethod + def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Decorate a view function to register it with the given URL + rule and options. Calls :meth:`add_url_rule`, which has more + details about the implementation. + + .. code-block:: python + + @app.route("/") + def index(): + return "Hello, World!" + + See :ref:`url-route-registrations`. + + The endpoint name for the route defaults to the name of the view + function if the ``endpoint`` parameter isn't passed. + + The ``methods`` parameter defaults to ``["GET"]``. ``HEAD`` and + ``OPTIONS`` are added automatically. + + :param rule: The URL rule string. + :param options: Extra options passed to the + :class:`~werkzeug.routing.Rule` object. + """ + + def decorator(f: T_route) -> T_route: + endpoint = options.pop("endpoint", None) + self.add_url_rule(rule, endpoint, f, **options) + return f + + return decorator + + @setupmethod + def add_url_rule( + self, + rule: str, + endpoint: str | None = None, + view_func: ft.RouteCallable | None = None, + provide_automatic_options: bool | None = None, + **options: t.Any, + ) -> None: + """Register a rule for routing incoming requests and building + URLs. The :meth:`route` decorator is a shortcut to call this + with the ``view_func`` argument. These are equivalent: + + .. code-block:: python + + @app.route("/") + def index(): + ... + + .. code-block:: python + + def index(): + ... + + app.add_url_rule("/", view_func=index) + + See :ref:`url-route-registrations`. + + The endpoint name for the route defaults to the name of the view + function if the ``endpoint`` parameter isn't passed. An error + will be raised if a function has already been registered for the + endpoint. + + The ``methods`` parameter defaults to ``["GET"]``. ``HEAD`` is + always added automatically, and ``OPTIONS`` is added + automatically by default. + + ``view_func`` does not necessarily need to be passed, but if the + rule should participate in routing an endpoint name must be + associated with a view function at some point with the + :meth:`endpoint` decorator. + + .. code-block:: python + + app.add_url_rule("/", endpoint="index") + + @app.endpoint("index") + def index(): + ... + + If ``view_func`` has a ``required_methods`` attribute, those + methods are added to the passed and automatic methods. If it + has a ``provide_automatic_methods`` attribute, it is used as the + default if the parameter is not passed. + + :param rule: The URL rule string. + :param endpoint: The endpoint name to associate with the rule + and view function. Used when routing and building URLs. + Defaults to ``view_func.__name__``. + :param view_func: The view function to associate with the + endpoint name. + :param provide_automatic_options: Add the ``OPTIONS`` method and + respond to ``OPTIONS`` requests automatically. + :param options: Extra options passed to the + :class:`~werkzeug.routing.Rule` object. + """ + raise NotImplementedError + + @setupmethod + def endpoint(self, endpoint: str) -> t.Callable[[F], F]: + """Decorate a view function to register it for the given + endpoint. Used if a rule is added without a ``view_func`` with + :meth:`add_url_rule`. + + .. code-block:: python + + app.add_url_rule("/ex", endpoint="example") + + @app.endpoint("example") + def example(): + ... + + :param endpoint: The endpoint name to associate with the view + function. + """ + + def decorator(f: F) -> F: + self.view_functions[endpoint] = f + return f + + return decorator + + @setupmethod + def before_request(self, f: T_before_request) -> T_before_request: + """Register a function to run before each request. + + For example, this can be used to open a database connection, or + to load the logged in user from the session. + + .. code-block:: python + + @app.before_request + def load_user(): + if "user_id" in session: + g.user = db.session.get(session["user_id"]) + + The function will be called without any arguments. If it returns + a non-``None`` value, the value is handled as if it was the + return value from the view, and further request handling is + stopped. + + This is available on both app and blueprint objects. When used on an app, this + executes before every request. When used on a blueprint, this executes before + every request that the blueprint handles. To register with a blueprint and + execute before every request, use :meth:`.Blueprint.before_app_request`. + """ + self.before_request_funcs.setdefault(None, []).append(f) + return f + + @setupmethod + def after_request(self, f: T_after_request) -> T_after_request: + """Register a function to run after each request to this object. + + The function is called with the response object, and must return + a response object. This allows the functions to modify or + replace the response before it is sent. + + If a function raises an exception, any remaining + ``after_request`` functions will not be called. Therefore, this + should not be used for actions that must execute, such as to + close resources. Use :meth:`teardown_request` for that. + + This is available on both app and blueprint objects. When used on an app, this + executes after every request. When used on a blueprint, this executes after + every request that the blueprint handles. To register with a blueprint and + execute after every request, use :meth:`.Blueprint.after_app_request`. + """ + self.after_request_funcs.setdefault(None, []).append(f) + return f + + @setupmethod + def teardown_request(self, f: T_teardown) -> T_teardown: + """Register a function to be called when the request context is + popped. Typically this happens at the end of each request, but + contexts may be pushed manually as well during testing. + + .. code-block:: python + + with app.test_request_context(): + ... + + When the ``with`` block exits (or ``ctx.pop()`` is called), the + teardown functions are called just before the request context is + made inactive. + + When a teardown function was called because of an unhandled + exception it will be passed an error object. If an + :meth:`errorhandler` is registered, it will handle the exception + and the teardown will not receive it. + + Teardown functions must avoid raising exceptions. If they + execute code that might fail they must surround that code with a + ``try``/``except`` block and log any errors. + + The return values of teardown functions are ignored. + + This is available on both app and blueprint objects. When used on an app, this + executes after every request. When used on a blueprint, this executes after + every request that the blueprint handles. To register with a blueprint and + execute after every request, use :meth:`.Blueprint.teardown_app_request`. + """ + self.teardown_request_funcs.setdefault(None, []).append(f) + return f + + @setupmethod + def context_processor( + self, + f: T_template_context_processor, + ) -> T_template_context_processor: + """Registers a template context processor function. These functions run before + rendering a template. The keys of the returned dict are added as variables + available in the template. + + This is available on both app and blueprint objects. When used on an app, this + is called for every rendered template. When used on a blueprint, this is called + for templates rendered from the blueprint's views. To register with a blueprint + and affect every template, use :meth:`.Blueprint.app_context_processor`. + """ + self.template_context_processors[None].append(f) + return f + + @setupmethod + def url_value_preprocessor( + self, + f: T_url_value_preprocessor, + ) -> T_url_value_preprocessor: + """Register a URL value preprocessor function for all view + functions in the application. These functions will be called before the + :meth:`before_request` functions. + + The function can modify the values captured from the matched url before + they are passed to the view. For example, this can be used to pop a + common language code value and place it in ``g`` rather than pass it to + every view. + + The function is passed the endpoint name and values dict. The return + value is ignored. + + This is available on both app and blueprint objects. When used on an app, this + is called for every request. When used on a blueprint, this is called for + requests that the blueprint handles. To register with a blueprint and affect + every request, use :meth:`.Blueprint.app_url_value_preprocessor`. + """ + self.url_value_preprocessors[None].append(f) + return f + + @setupmethod + def url_defaults(self, f: T_url_defaults) -> T_url_defaults: + """Callback function for URL defaults for all view functions of the + application. It's called with the endpoint and values and should + update the values passed in place. + + This is available on both app and blueprint objects. When used on an app, this + is called for every request. When used on a blueprint, this is called for + requests that the blueprint handles. To register with a blueprint and affect + every request, use :meth:`.Blueprint.app_url_defaults`. + """ + self.url_default_functions[None].append(f) + return f + + @setupmethod + def errorhandler( + self, code_or_exception: type[Exception] | int + ) -> t.Callable[[T_error_handler], T_error_handler]: + """Register a function to handle errors by code or exception class. + + A decorator that is used to register a function given an + error code. Example:: + + @app.errorhandler(404) + def page_not_found(error): + return 'This page does not exist', 404 + + You can also register handlers for arbitrary exceptions:: + + @app.errorhandler(DatabaseError) + def special_exception_handler(error): + return 'Database connection failed', 500 + + This is available on both app and blueprint objects. When used on an app, this + can handle errors from every request. When used on a blueprint, this can handle + errors from requests that the blueprint handles. To register with a blueprint + and affect every request, use :meth:`.Blueprint.app_errorhandler`. + + .. versionadded:: 0.7 + Use :meth:`register_error_handler` instead of modifying + :attr:`error_handler_spec` directly, for application wide error + handlers. + + .. versionadded:: 0.7 + One can now additionally also register custom exception types + that do not necessarily have to be a subclass of the + :class:`~werkzeug.exceptions.HTTPException` class. + + :param code_or_exception: the code as integer for the handler, or + an arbitrary exception + """ + + def decorator(f: T_error_handler) -> T_error_handler: + self.register_error_handler(code_or_exception, f) + return f + + return decorator + + @setupmethod + def register_error_handler( + self, + code_or_exception: type[Exception] | int, + f: ft.ErrorHandlerCallable, + ) -> None: + """Alternative error attach function to the :meth:`errorhandler` + decorator that is more straightforward to use for non decorator + usage. + + .. versionadded:: 0.7 + """ + exc_class, code = self._get_exc_class_and_code(code_or_exception) + self.error_handler_spec[None][code][exc_class] = f + + @staticmethod + def _get_exc_class_and_code( + exc_class_or_code: type[Exception] | int, + ) -> tuple[type[Exception], int | None]: + """Get the exception class being handled. For HTTP status codes + or ``HTTPException`` subclasses, return both the exception and + status code. + + :param exc_class_or_code: Any exception class, or an HTTP status + code as an integer. + """ + exc_class: type[Exception] + + if isinstance(exc_class_or_code, int): + try: + exc_class = default_exceptions[exc_class_or_code] + except KeyError: + raise ValueError( + f"'{exc_class_or_code}' is not a recognized HTTP" + " error code. Use a subclass of HTTPException with" + " that code instead." + ) from None + else: + exc_class = exc_class_or_code + + if isinstance(exc_class, Exception): + raise TypeError( + f"{exc_class!r} is an instance, not a class. Handlers" + " can only be registered for Exception classes or HTTP" + " error codes." + ) + + if not issubclass(exc_class, Exception): + raise ValueError( + f"'{exc_class.__name__}' is not a subclass of Exception." + " Handlers can only be registered for Exception classes" + " or HTTP error codes." + ) + + if issubclass(exc_class, HTTPException): + return exc_class, exc_class.code + else: + return exc_class, None + + +def _endpoint_from_view_func(view_func: ft.RouteCallable) -> str: + """Internal helper that returns the default endpoint for a given + function. This always is the function name. + """ + assert view_func is not None, "expected view func if endpoint is not provided." + return view_func.__name__ + + +def _find_package_path(import_name: str) -> str: + """Find the path that contains the package or module.""" + root_mod_name, _, _ = import_name.partition(".") + + try: + root_spec = importlib.util.find_spec(root_mod_name) + + if root_spec is None: + raise ValueError("not found") + except (ImportError, ValueError): + # ImportError: the machinery told us it does not exist + # ValueError: + # - the module name was invalid + # - the module name is __main__ + # - we raised `ValueError` due to `root_spec` being `None` + return os.getcwd() + + if root_spec.submodule_search_locations: + if root_spec.origin is None or root_spec.origin == "namespace": + # namespace package + package_spec = importlib.util.find_spec(import_name) + + if package_spec is not None and package_spec.submodule_search_locations: + # Pick the path in the namespace that contains the submodule. + package_path = pathlib.Path( + os.path.commonpath(package_spec.submodule_search_locations) + ) + search_location = next( + location + for location in root_spec.submodule_search_locations + if package_path.is_relative_to(location) + ) + else: + # Pick the first path. + search_location = root_spec.submodule_search_locations[0] + + return os.path.dirname(search_location) + else: + # package with __init__.py + return os.path.dirname(os.path.dirname(root_spec.origin)) + else: + # module + return os.path.dirname(root_spec.origin) # type: ignore[type-var, return-value] + + +def find_package(import_name: str) -> tuple[str | None, str]: + """Find the prefix that a package is installed under, and the path + that it would be imported from. + + The prefix is the directory containing the standard directory + hierarchy (lib, bin, etc.). If the package is not installed to the + system (:attr:`sys.prefix`) or a virtualenv (``site-packages``), + ``None`` is returned. + + The path is the entry in :attr:`sys.path` that contains the package + for import. If the package is not installed, it's assumed that the + package was imported from the current working directory. + """ + package_path = _find_package_path(import_name) + py_prefix = os.path.abspath(sys.prefix) + + # installed to the system + if pathlib.PurePath(package_path).is_relative_to(py_prefix): + return py_prefix, package_path + + site_parent, site_folder = os.path.split(package_path) + + # installed to a virtualenv + if site_folder.lower() == "site-packages": + parent, folder = os.path.split(site_parent) + + # Windows (prefix/lib/site-packages) + if folder.lower() == "lib": + return parent, package_path + + # Unix (prefix/lib/pythonX.Y/site-packages) + if os.path.basename(parent).lower() == "lib": + return os.path.dirname(parent), package_path + + # something else (prefix/site-packages) + return site_parent, package_path + + # not installed + return None, package_path diff --git a/venv/Lib/site-packages/flask/sessions.py b/venv/Lib/site-packages/flask/sessions.py new file mode 100644 index 0000000..ad35770 --- /dev/null +++ b/venv/Lib/site-packages/flask/sessions.py @@ -0,0 +1,385 @@ +from __future__ import annotations + +import collections.abc as c +import hashlib +import typing as t +from collections.abc import MutableMapping +from datetime import datetime +from datetime import timezone + +from itsdangerous import BadSignature +from itsdangerous import URLSafeTimedSerializer +from werkzeug.datastructures import CallbackDict + +from .json.tag import TaggedJSONSerializer + +if t.TYPE_CHECKING: # pragma: no cover + import typing_extensions as te + + from .app import Flask + from .wrappers import Request + from .wrappers import Response + + +class SessionMixin(MutableMapping[str, t.Any]): + """Expands a basic dictionary with session attributes.""" + + @property + def permanent(self) -> bool: + """This reflects the ``'_permanent'`` key in the dict.""" + return self.get("_permanent", False) # type: ignore[no-any-return] + + @permanent.setter + def permanent(self, value: bool) -> None: + self["_permanent"] = bool(value) + + #: Some implementations can detect whether a session is newly + #: created, but that is not guaranteed. Use with caution. The mixin + # default is hard-coded ``False``. + new = False + + #: Some implementations can detect changes to the session and set + #: this when that happens. The mixin default is hard coded to + #: ``True``. + modified = True + + accessed = False + """Indicates if the session was accessed, even if it was not modified. This + is set when the session object is accessed through the request context, + including the global :data:`.session` proxy. A ``Vary: cookie`` header will + be added if this is ``True``. + + .. versionchanged:: 3.1.3 + This is tracked by the request context. + """ + + +class SecureCookieSession(CallbackDict[str, t.Any], SessionMixin): + """Base class for sessions based on signed cookies. + + This session backend will set the :attr:`modified` and + :attr:`accessed` attributes. It cannot reliably track whether a + session is new (vs. empty), so :attr:`new` remains hard coded to + ``False``. + """ + + #: When data is changed, this is set to ``True``. Only the session + #: dictionary itself is tracked; if the session contains mutable + #: data (for example a nested dict) then this must be set to + #: ``True`` manually when modifying that data. The session cookie + #: will only be written to the response if this is ``True``. + modified = False + + def __init__( + self, + initial: c.Mapping[str, t.Any] | None = None, + ) -> None: + def on_update(self: te.Self) -> None: + self.modified = True + + super().__init__(initial, on_update) + + +class NullSession(SecureCookieSession): + """Class used to generate nicer error messages if sessions are not + available. Will still allow read-only access to the empty session + but fail on setting. + """ + + def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn: + raise RuntimeError( + "The session is unavailable because no secret " + "key was set. Set the secret_key on the " + "application to something unique and secret." + ) + + __setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail + del _fail + + +class SessionInterface: + """The basic interface you have to implement in order to replace the + default session interface which uses werkzeug's securecookie + implementation. The only methods you have to implement are + :meth:`open_session` and :meth:`save_session`, the others have + useful defaults which you don't need to change. + + The session object returned by the :meth:`open_session` method has to + provide a dictionary like interface plus the properties and methods + from the :class:`SessionMixin`. We recommend just subclassing a dict + and adding that mixin:: + + class Session(dict, SessionMixin): + pass + + If :meth:`open_session` returns ``None`` Flask will call into + :meth:`make_null_session` to create a session that acts as replacement + if the session support cannot work because some requirement is not + fulfilled. The default :class:`NullSession` class that is created + will complain that the secret key was not set. + + To replace the session interface on an application all you have to do + is to assign :attr:`flask.Flask.session_interface`:: + + app = Flask(__name__) + app.session_interface = MySessionInterface() + + Multiple requests with the same session may be sent and handled + concurrently. When implementing a new session interface, consider + whether reads or writes to the backing store must be synchronized. + There is no guarantee on the order in which the session for each + request is opened or saved, it will occur in the order that requests + begin and end processing. + + .. versionadded:: 0.8 + """ + + #: :meth:`make_null_session` will look here for the class that should + #: be created when a null session is requested. Likewise the + #: :meth:`is_null_session` method will perform a typecheck against + #: this type. + null_session_class = NullSession + + #: A flag that indicates if the session interface is pickle based. + #: This can be used by Flask extensions to make a decision in regards + #: to how to deal with the session object. + #: + #: .. versionadded:: 0.10 + pickle_based = False + + def make_null_session(self, app: Flask) -> NullSession: + """Creates a null session which acts as a replacement object if the + real session support could not be loaded due to a configuration + error. This mainly aids the user experience because the job of the + null session is to still support lookup without complaining but + modifications are answered with a helpful error message of what + failed. + + This creates an instance of :attr:`null_session_class` by default. + """ + return self.null_session_class() + + def is_null_session(self, obj: object) -> bool: + """Checks if a given object is a null session. Null sessions are + not asked to be saved. + + This checks if the object is an instance of :attr:`null_session_class` + by default. + """ + return isinstance(obj, self.null_session_class) + + def get_cookie_name(self, app: Flask) -> str: + """The name of the session cookie. Uses``app.config["SESSION_COOKIE_NAME"]``.""" + return app.config["SESSION_COOKIE_NAME"] # type: ignore[no-any-return] + + def get_cookie_domain(self, app: Flask) -> str | None: + """The value of the ``Domain`` parameter on the session cookie. If not set, + browsers will only send the cookie to the exact domain it was set from. + Otherwise, they will send it to any subdomain of the given value as well. + + Uses the :data:`SESSION_COOKIE_DOMAIN` config. + + .. versionchanged:: 2.3 + Not set by default, does not fall back to ``SERVER_NAME``. + """ + return app.config["SESSION_COOKIE_DOMAIN"] # type: ignore[no-any-return] + + def get_cookie_path(self, app: Flask) -> str: + """Returns the path for which the cookie should be valid. The + default implementation uses the value from the ``SESSION_COOKIE_PATH`` + config var if it's set, and falls back to ``APPLICATION_ROOT`` or + uses ``/`` if it's ``None``. + """ + return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"] # type: ignore[no-any-return] + + def get_cookie_httponly(self, app: Flask) -> bool: + """Returns True if the session cookie should be httponly. This + currently just returns the value of the ``SESSION_COOKIE_HTTPONLY`` + config var. + """ + return app.config["SESSION_COOKIE_HTTPONLY"] # type: ignore[no-any-return] + + def get_cookie_secure(self, app: Flask) -> bool: + """Returns True if the cookie should be secure. This currently + just returns the value of the ``SESSION_COOKIE_SECURE`` setting. + """ + return app.config["SESSION_COOKIE_SECURE"] # type: ignore[no-any-return] + + def get_cookie_samesite(self, app: Flask) -> str | None: + """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the + ``SameSite`` attribute. This currently just returns the value of + the :data:`SESSION_COOKIE_SAMESITE` setting. + """ + return app.config["SESSION_COOKIE_SAMESITE"] # type: ignore[no-any-return] + + def get_cookie_partitioned(self, app: Flask) -> bool: + """Returns True if the cookie should be partitioned. By default, uses + the value of :data:`SESSION_COOKIE_PARTITIONED`. + + .. versionadded:: 3.1 + """ + return app.config["SESSION_COOKIE_PARTITIONED"] # type: ignore[no-any-return] + + def get_expiration_time(self, app: Flask, session: SessionMixin) -> datetime | None: + """A helper method that returns an expiration date for the session + or ``None`` if the session is linked to the browser session. The + default implementation returns now + the permanent session + lifetime configured on the application. + """ + if session.permanent: + return datetime.now(timezone.utc) + app.permanent_session_lifetime + return None + + def should_set_cookie(self, app: Flask, session: SessionMixin) -> bool: + """Used by session backends to determine if a ``Set-Cookie`` header + should be set for this session cookie for this response. If the session + has been modified, the cookie is set. If the session is permanent and + the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is + always set. + + This check is usually skipped if the session was deleted. + + .. versionadded:: 0.11 + """ + + return session.modified or ( + session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"] + ) + + def open_session(self, app: Flask, request: Request) -> SessionMixin | None: + """This is called at the beginning of each request, after + pushing the request context, before matching the URL. + + This must return an object which implements a dictionary-like + interface as well as the :class:`SessionMixin` interface. + + This will return ``None`` to indicate that loading failed in + some way that is not immediately an error. The request + context will fall back to using :meth:`make_null_session` + in this case. + """ + raise NotImplementedError() + + def save_session( + self, app: Flask, session: SessionMixin, response: Response + ) -> None: + """This is called at the end of each request, after generating + a response, before removing the request context. It is skipped + if :meth:`is_null_session` returns ``True``. + """ + raise NotImplementedError() + + +session_json_serializer = TaggedJSONSerializer() + + +def _lazy_sha1(string: bytes = b"") -> t.Any: + """Don't access ``hashlib.sha1`` until runtime. FIPS builds may not include + SHA-1, in which case the import and use as a default would fail before the + developer can configure something else. + """ + return hashlib.sha1(string) + + +class SecureCookieSessionInterface(SessionInterface): + """The default session interface that stores sessions in signed cookies + through the :mod:`itsdangerous` module. + """ + + #: the salt that should be applied on top of the secret key for the + #: signing of cookie based sessions. + salt = "cookie-session" + #: the hash function to use for the signature. The default is sha1 + digest_method = staticmethod(_lazy_sha1) + #: the name of the itsdangerous supported key derivation. The default + #: is hmac. + key_derivation = "hmac" + #: A python serializer for the payload. The default is a compact + #: JSON derived serializer with support for some extra Python types + #: such as datetime objects or tuples. + serializer = session_json_serializer + session_class = SecureCookieSession + + def get_signing_serializer(self, app: Flask) -> URLSafeTimedSerializer | None: + if not app.secret_key: + return None + + keys: list[str | bytes] = [] + + if fallbacks := app.config["SECRET_KEY_FALLBACKS"]: + keys.extend(fallbacks) + + keys.append(app.secret_key) # itsdangerous expects current key at top + return URLSafeTimedSerializer( + keys, # type: ignore[arg-type] + salt=self.salt, + serializer=self.serializer, + signer_kwargs={ + "key_derivation": self.key_derivation, + "digest_method": self.digest_method, + }, + ) + + def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None: + s = self.get_signing_serializer(app) + if s is None: + return None + val = request.cookies.get(self.get_cookie_name(app)) + if not val: + return self.session_class() + max_age = int(app.permanent_session_lifetime.total_seconds()) + try: + data = s.loads(val, max_age=max_age) + return self.session_class(data) + except BadSignature: + return self.session_class() + + def save_session( + self, app: Flask, session: SessionMixin, response: Response + ) -> None: + name = self.get_cookie_name(app) + domain = self.get_cookie_domain(app) + path = self.get_cookie_path(app) + secure = self.get_cookie_secure(app) + partitioned = self.get_cookie_partitioned(app) + samesite = self.get_cookie_samesite(app) + httponly = self.get_cookie_httponly(app) + + # Add a "Vary: Cookie" header if the session was accessed at all. + if session.accessed: + response.vary.add("Cookie") + + # If the session is modified to be empty, remove the cookie. + # If the session is empty, return without setting the cookie. + if not session: + if session.modified: + response.delete_cookie( + name, + domain=domain, + path=path, + secure=secure, + partitioned=partitioned, + samesite=samesite, + httponly=httponly, + ) + response.vary.add("Cookie") + + return + + if not self.should_set_cookie(app, session): + return + + expires = self.get_expiration_time(app, session) + val = self.get_signing_serializer(app).dumps(dict(session)) # type: ignore[union-attr] + response.set_cookie( + name, + val, + expires=expires, + httponly=httponly, + domain=domain, + path=path, + secure=secure, + partitioned=partitioned, + samesite=samesite, + ) + response.vary.add("Cookie") diff --git a/venv/Lib/site-packages/flask/signals.py b/venv/Lib/site-packages/flask/signals.py new file mode 100644 index 0000000..444fda9 --- /dev/null +++ b/venv/Lib/site-packages/flask/signals.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from blinker import Namespace + +# This namespace is only for signals provided by Flask itself. +_signals = Namespace() + +template_rendered = _signals.signal("template-rendered") +before_render_template = _signals.signal("before-render-template") +request_started = _signals.signal("request-started") +request_finished = _signals.signal("request-finished") +request_tearing_down = _signals.signal("request-tearing-down") +got_request_exception = _signals.signal("got-request-exception") +appcontext_tearing_down = _signals.signal("appcontext-tearing-down") +appcontext_pushed = _signals.signal("appcontext-pushed") +appcontext_popped = _signals.signal("appcontext-popped") +message_flashed = _signals.signal("message-flashed") diff --git a/venv/Lib/site-packages/flask/templating.py b/venv/Lib/site-packages/flask/templating.py new file mode 100644 index 0000000..c5fb5b9 --- /dev/null +++ b/venv/Lib/site-packages/flask/templating.py @@ -0,0 +1,220 @@ +from __future__ import annotations + +import typing as t + +from jinja2 import BaseLoader +from jinja2 import Environment as BaseEnvironment +from jinja2 import Template +from jinja2 import TemplateNotFound + +from .globals import _cv_app +from .globals import _cv_request +from .globals import current_app +from .globals import request +from .helpers import stream_with_context +from .signals import before_render_template +from .signals import template_rendered + +if t.TYPE_CHECKING: # pragma: no cover + from .app import Flask + from .sansio.app import App + from .sansio.scaffold import Scaffold + + +def _default_template_ctx_processor() -> dict[str, t.Any]: + """Default template context processor. Replaces the ``request`` and ``g`` + proxies with their concrete objects for faster access. + """ + appctx = _cv_app.get(None) + reqctx = _cv_request.get(None) + rv: dict[str, t.Any] = {} + if appctx is not None: + rv["g"] = appctx.g + if reqctx is not None: + rv["request"] = reqctx.request + # The session proxy cannot be replaced, accessing it gets + # RequestContext.session, which sets session.accessed. + return rv + + +class Environment(BaseEnvironment): + """Works like a regular Jinja environment but has some additional + knowledge of how Flask's blueprint works so that it can prepend the + name of the blueprint to referenced templates if necessary. + """ + + def __init__(self, app: App, **options: t.Any) -> None: + if "loader" not in options: + options["loader"] = app.create_global_jinja_loader() + BaseEnvironment.__init__(self, **options) + self.app = app + + +class DispatchingJinjaLoader(BaseLoader): + """A loader that looks for templates in the application and all + the blueprint folders. + """ + + def __init__(self, app: App) -> None: + self.app = app + + def get_source( + self, environment: BaseEnvironment, template: str + ) -> tuple[str, str | None, t.Callable[[], bool] | None]: + if self.app.config["EXPLAIN_TEMPLATE_LOADING"]: + return self._get_source_explained(environment, template) + return self._get_source_fast(environment, template) + + def _get_source_explained( + self, environment: BaseEnvironment, template: str + ) -> tuple[str, str | None, t.Callable[[], bool] | None]: + attempts = [] + rv: tuple[str, str | None, t.Callable[[], bool] | None] | None + trv: None | (tuple[str, str | None, t.Callable[[], bool] | None]) = None + + for srcobj, loader in self._iter_loaders(template): + try: + rv = loader.get_source(environment, template) + if trv is None: + trv = rv + except TemplateNotFound: + rv = None + attempts.append((loader, srcobj, rv)) + + from .debughelpers import explain_template_loading_attempts + + explain_template_loading_attempts(self.app, template, attempts) + + if trv is not None: + return trv + raise TemplateNotFound(template) + + def _get_source_fast( + self, environment: BaseEnvironment, template: str + ) -> tuple[str, str | None, t.Callable[[], bool] | None]: + for _srcobj, loader in self._iter_loaders(template): + try: + return loader.get_source(environment, template) + except TemplateNotFound: + continue + raise TemplateNotFound(template) + + def _iter_loaders(self, template: str) -> t.Iterator[tuple[Scaffold, BaseLoader]]: + loader = self.app.jinja_loader + if loader is not None: + yield self.app, loader + + for blueprint in self.app.iter_blueprints(): + loader = blueprint.jinja_loader + if loader is not None: + yield blueprint, loader + + def list_templates(self) -> list[str]: + result = set() + loader = self.app.jinja_loader + if loader is not None: + result.update(loader.list_templates()) + + for blueprint in self.app.iter_blueprints(): + loader = blueprint.jinja_loader + if loader is not None: + for template in loader.list_templates(): + result.add(template) + + return list(result) + + +def _render(app: Flask, template: Template, context: dict[str, t.Any]) -> str: + app.update_template_context(context) + before_render_template.send( + app, _async_wrapper=app.ensure_sync, template=template, context=context + ) + rv = template.render(context) + template_rendered.send( + app, _async_wrapper=app.ensure_sync, template=template, context=context + ) + return rv + + +def render_template( + template_name_or_list: str | Template | list[str | Template], + **context: t.Any, +) -> str: + """Render a template by name with the given context. + + :param template_name_or_list: The name of the template to render. If + a list is given, the first name to exist will be rendered. + :param context: The variables to make available in the template. + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + template = app.jinja_env.get_or_select_template(template_name_or_list) + return _render(app, template, context) + + +def render_template_string(source: str, **context: t.Any) -> str: + """Render a template from the given source string with the given + context. + + :param source: The source code of the template to render. + :param context: The variables to make available in the template. + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + template = app.jinja_env.from_string(source) + return _render(app, template, context) + + +def _stream( + app: Flask, template: Template, context: dict[str, t.Any] +) -> t.Iterator[str]: + app.update_template_context(context) + before_render_template.send( + app, _async_wrapper=app.ensure_sync, template=template, context=context + ) + + def generate() -> t.Iterator[str]: + yield from template.generate(context) + template_rendered.send( + app, _async_wrapper=app.ensure_sync, template=template, context=context + ) + + rv = generate() + + # If a request context is active, keep it while generating. + if request: + rv = stream_with_context(rv) + + return rv + + +def stream_template( + template_name_or_list: str | Template | list[str | Template], + **context: t.Any, +) -> t.Iterator[str]: + """Render a template by name with the given context as a stream. + This returns an iterator of strings, which can be used as a + streaming response from a view. + + :param template_name_or_list: The name of the template to render. If + a list is given, the first name to exist will be rendered. + :param context: The variables to make available in the template. + + .. versionadded:: 2.2 + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + template = app.jinja_env.get_or_select_template(template_name_or_list) + return _stream(app, template, context) + + +def stream_template_string(source: str, **context: t.Any) -> t.Iterator[str]: + """Render a template from the given source string with the given + context as a stream. This returns an iterator of strings, which can + be used as a streaming response from a view. + + :param source: The source code of the template to render. + :param context: The variables to make available in the template. + + .. versionadded:: 2.2 + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + template = app.jinja_env.from_string(source) + return _stream(app, template, context) diff --git a/venv/Lib/site-packages/flask/testing.py b/venv/Lib/site-packages/flask/testing.py new file mode 100644 index 0000000..55eb12f --- /dev/null +++ b/venv/Lib/site-packages/flask/testing.py @@ -0,0 +1,298 @@ +from __future__ import annotations + +import importlib.metadata +import typing as t +from contextlib import contextmanager +from contextlib import ExitStack +from copy import copy +from types import TracebackType +from urllib.parse import urlsplit + +import werkzeug.test +from click.testing import CliRunner +from click.testing import Result +from werkzeug.test import Client +from werkzeug.wrappers import Request as BaseRequest + +from .cli import ScriptInfo +from .sessions import SessionMixin + +if t.TYPE_CHECKING: # pragma: no cover + from _typeshed.wsgi import WSGIEnvironment + from werkzeug.test import TestResponse + + from .app import Flask + + +class EnvironBuilder(werkzeug.test.EnvironBuilder): + """An :class:`~werkzeug.test.EnvironBuilder`, that takes defaults from the + application. + + :param app: The Flask application to configure the environment from. + :param path: URL path being requested. + :param base_url: Base URL where the app is being served, which + ``path`` is relative to. If not given, built from + :data:`PREFERRED_URL_SCHEME`, ``subdomain``, + :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. + :param subdomain: Subdomain name to append to :data:`SERVER_NAME`. + :param url_scheme: Scheme to use instead of + :data:`PREFERRED_URL_SCHEME`. + :param json: If given, this is serialized as JSON and passed as + ``data``. Also defaults ``content_type`` to + ``application/json``. + :param args: other positional arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + :param kwargs: other keyword arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + """ + + def __init__( + self, + app: Flask, + path: str = "/", + base_url: str | None = None, + subdomain: str | None = None, + url_scheme: str | None = None, + *args: t.Any, + **kwargs: t.Any, + ) -> None: + assert not (base_url or subdomain or url_scheme) or ( + base_url is not None + ) != bool(subdomain or url_scheme), ( + 'Cannot pass "subdomain" or "url_scheme" with "base_url".' + ) + + if base_url is None: + http_host = app.config.get("SERVER_NAME") or "localhost" + app_root = app.config["APPLICATION_ROOT"] + + if subdomain: + http_host = f"{subdomain}.{http_host}" + + if url_scheme is None: + url_scheme = app.config["PREFERRED_URL_SCHEME"] + + url = urlsplit(path) + base_url = ( + f"{url.scheme or url_scheme}://{url.netloc or http_host}" + f"/{app_root.lstrip('/')}" + ) + path = url.path + + if url.query: + path = f"{path}?{url.query}" + + self.app = app + super().__init__(path, base_url, *args, **kwargs) + + def json_dumps(self, obj: t.Any, **kwargs: t.Any) -> str: + """Serialize ``obj`` to a JSON-formatted string. + + The serialization will be configured according to the config associated + with this EnvironBuilder's ``app``. + """ + return self.app.json.dumps(obj, **kwargs) + + +_werkzeug_version = "" + + +def _get_werkzeug_version() -> str: + global _werkzeug_version + + if not _werkzeug_version: + _werkzeug_version = importlib.metadata.version("werkzeug") + + return _werkzeug_version + + +class FlaskClient(Client): + """Works like a regular Werkzeug test client but has knowledge about + Flask's contexts to defer the cleanup of the request context until + the end of a ``with`` block. For general information about how to + use this class refer to :class:`werkzeug.test.Client`. + + .. versionchanged:: 0.12 + `app.test_client()` includes preset default environment, which can be + set after instantiation of the `app.test_client()` object in + `client.environ_base`. + + Basic usage is outlined in the :doc:`/testing` chapter. + """ + + application: Flask + + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + super().__init__(*args, **kwargs) + self.preserve_context = False + self._new_contexts: list[t.ContextManager[t.Any]] = [] + self._context_stack = ExitStack() + self.environ_base = { + "REMOTE_ADDR": "127.0.0.1", + "HTTP_USER_AGENT": f"Werkzeug/{_get_werkzeug_version()}", + } + + @contextmanager + def session_transaction( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Iterator[SessionMixin]: + """When used in combination with a ``with`` statement this opens a + session transaction. This can be used to modify the session that + the test client uses. Once the ``with`` block is left the session is + stored back. + + :: + + with client.session_transaction() as session: + session['value'] = 42 + + Internally this is implemented by going through a temporary test + request context and since session handling could depend on + request variables this function accepts the same arguments as + :meth:`~flask.Flask.test_request_context` which are directly + passed through. + """ + if self._cookies is None: + raise TypeError( + "Cookies are disabled. Create a client with 'use_cookies=True'." + ) + + app = self.application + ctx = app.test_request_context(*args, **kwargs) + self._add_cookies_to_wsgi(ctx.request.environ) + + with ctx: + sess = app.session_interface.open_session(app, ctx.request) + + if sess is None: + raise RuntimeError("Session backend did not open a session.") + + yield sess + resp = app.response_class() + + if app.session_interface.is_null_session(sess): + return + + with ctx: + app.session_interface.save_session(app, sess, resp) + + self._update_cookies_from_response( + ctx.request.host.partition(":")[0], + ctx.request.path, + resp.headers.getlist("Set-Cookie"), + ) + + def _copy_environ(self, other: WSGIEnvironment) -> WSGIEnvironment: + out = {**self.environ_base, **other} + + if self.preserve_context: + out["werkzeug.debug.preserve_context"] = self._new_contexts.append + + return out + + def _request_from_builder_args( + self, args: tuple[t.Any, ...], kwargs: dict[str, t.Any] + ) -> BaseRequest: + kwargs["environ_base"] = self._copy_environ(kwargs.get("environ_base", {})) + builder = EnvironBuilder(self.application, *args, **kwargs) + + try: + return builder.get_request() + finally: + builder.close() + + def open( + self, + *args: t.Any, + buffered: bool = False, + follow_redirects: bool = False, + **kwargs: t.Any, + ) -> TestResponse: + if args and isinstance( + args[0], (werkzeug.test.EnvironBuilder, dict, BaseRequest) + ): + if isinstance(args[0], werkzeug.test.EnvironBuilder): + builder = copy(args[0]) + builder.environ_base = self._copy_environ(builder.environ_base or {}) # type: ignore[arg-type] + request = builder.get_request() + elif isinstance(args[0], dict): + request = EnvironBuilder.from_environ( + args[0], app=self.application, environ_base=self._copy_environ({}) + ).get_request() + else: + # isinstance(args[0], BaseRequest) + request = copy(args[0]) + request.environ = self._copy_environ(request.environ) + else: + # request is None + request = self._request_from_builder_args(args, kwargs) + + # Pop any previously preserved contexts. This prevents contexts + # from being preserved across redirects or multiple requests + # within a single block. + self._context_stack.close() + + response = super().open( + request, + buffered=buffered, + follow_redirects=follow_redirects, + ) + response.json_module = self.application.json # type: ignore[assignment] + + # Re-push contexts that were preserved during the request. + for cm in self._new_contexts: + self._context_stack.enter_context(cm) + + self._new_contexts.clear() + return response + + def __enter__(self) -> FlaskClient: + if self.preserve_context: + raise RuntimeError("Cannot nest client invocations") + self.preserve_context = True + return self + + def __exit__( + self, + exc_type: type | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.preserve_context = False + self._context_stack.close() + + +class FlaskCliRunner(CliRunner): + """A :class:`~click.testing.CliRunner` for testing a Flask app's + CLI commands. Typically created using + :meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`. + """ + + def __init__(self, app: Flask, **kwargs: t.Any) -> None: + self.app = app + super().__init__(**kwargs) + + def invoke( # type: ignore + self, cli: t.Any = None, args: t.Any = None, **kwargs: t.Any + ) -> Result: + """Invokes a CLI command in an isolated environment. See + :meth:`CliRunner.invoke ` for + full method documentation. See :ref:`testing-cli` for examples. + + If the ``obj`` argument is not given, passes an instance of + :class:`~flask.cli.ScriptInfo` that knows how to load the Flask + app being tested. + + :param cli: Command object to invoke. Default is the app's + :attr:`~flask.app.Flask.cli` group. + :param args: List of strings to invoke the command with. + + :return: a :class:`~click.testing.Result` object. + """ + if cli is None: + cli = self.app.cli + + if "obj" not in kwargs: + kwargs["obj"] = ScriptInfo(create_app=lambda: self.app) + + return super().invoke(cli, args, **kwargs) diff --git a/venv/Lib/site-packages/flask/typing.py b/venv/Lib/site-packages/flask/typing.py new file mode 100644 index 0000000..6b70c40 --- /dev/null +++ b/venv/Lib/site-packages/flask/typing.py @@ -0,0 +1,93 @@ +from __future__ import annotations + +import collections.abc as cabc +import typing as t + +if t.TYPE_CHECKING: # pragma: no cover + from _typeshed.wsgi import WSGIApplication # noqa: F401 + from werkzeug.datastructures import Headers # noqa: F401 + from werkzeug.sansio.response import Response # noqa: F401 + +# The possible types that are directly convertible or are a Response object. +ResponseValue = t.Union[ + "Response", + str, + bytes, + list[t.Any], + # Only dict is actually accepted, but Mapping allows for TypedDict. + t.Mapping[str, t.Any], + t.Iterator[str], + t.Iterator[bytes], + cabc.AsyncIterable[str], # for Quart, until App is generic. + cabc.AsyncIterable[bytes], +] + +# the possible types for an individual HTTP header +# This should be a Union, but mypy doesn't pass unless it's a TypeVar. +HeaderValue = t.Union[str, list[str], tuple[str, ...]] + +# the possible types for HTTP headers +HeadersValue = t.Union[ + "Headers", + t.Mapping[str, HeaderValue], + t.Sequence[tuple[str, HeaderValue]], +] + +# The possible types returned by a route function. +ResponseReturnValue = t.Union[ + ResponseValue, + tuple[ResponseValue, HeadersValue], + tuple[ResponseValue, int], + tuple[ResponseValue, int, HeadersValue], + "WSGIApplication", +] + +# Allow any subclass of werkzeug.Response, such as the one from Flask, +# as a callback argument. Using werkzeug.Response directly makes a +# callback annotated with flask.Response fail type checking. +ResponseClass = t.TypeVar("ResponseClass", bound="Response") + +AppOrBlueprintKey = t.Optional[str] # The App key is None, whereas blueprints are named +AfterRequestCallable = t.Union[ + t.Callable[[ResponseClass], ResponseClass], + t.Callable[[ResponseClass], t.Awaitable[ResponseClass]], +] +BeforeFirstRequestCallable = t.Union[ + t.Callable[[], None], t.Callable[[], t.Awaitable[None]] +] +BeforeRequestCallable = t.Union[ + t.Callable[[], t.Optional[ResponseReturnValue]], + t.Callable[[], t.Awaitable[t.Optional[ResponseReturnValue]]], +] +ShellContextProcessorCallable = t.Callable[[], dict[str, t.Any]] +TeardownCallable = t.Union[ + t.Callable[[t.Optional[BaseException]], None], + t.Callable[[t.Optional[BaseException]], t.Awaitable[None]], +] +TemplateContextProcessorCallable = t.Union[ + t.Callable[[], dict[str, t.Any]], + t.Callable[[], t.Awaitable[dict[str, t.Any]]], +] +TemplateFilterCallable = t.Callable[..., t.Any] +TemplateGlobalCallable = t.Callable[..., t.Any] +TemplateTestCallable = t.Callable[..., bool] +URLDefaultCallable = t.Callable[[str, dict[str, t.Any]], None] +URLValuePreprocessorCallable = t.Callable[ + [t.Optional[str], t.Optional[dict[str, t.Any]]], None +] + +# This should take Exception, but that either breaks typing the argument +# with a specific exception, or decorating multiple times with different +# exceptions (and using a union type on the argument). +# https://github.com/pallets/flask/issues/4095 +# https://github.com/pallets/flask/issues/4295 +# https://github.com/pallets/flask/issues/4297 +ErrorHandlerCallable = t.Union[ + t.Callable[[t.Any], ResponseReturnValue], + t.Callable[[t.Any], t.Awaitable[ResponseReturnValue]], +] + +RouteCallable = t.Union[ + t.Callable[..., ResponseReturnValue], + t.Callable[..., t.Awaitable[ResponseReturnValue]], +] diff --git a/venv/Lib/site-packages/flask/views.py b/venv/Lib/site-packages/flask/views.py new file mode 100644 index 0000000..53fe976 --- /dev/null +++ b/venv/Lib/site-packages/flask/views.py @@ -0,0 +1,191 @@ +from __future__ import annotations + +import typing as t + +from . import typing as ft +from .globals import current_app +from .globals import request + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + +http_method_funcs = frozenset( + ["get", "post", "head", "options", "delete", "put", "trace", "patch"] +) + + +class View: + """Subclass this class and override :meth:`dispatch_request` to + create a generic class-based view. Call :meth:`as_view` to create a + view function that creates an instance of the class with the given + arguments and calls its ``dispatch_request`` method with any URL + variables. + + See :doc:`views` for a detailed guide. + + .. code-block:: python + + class Hello(View): + init_every_request = False + + def dispatch_request(self, name): + return f"Hello, {name}!" + + app.add_url_rule( + "/hello/", view_func=Hello.as_view("hello") + ) + + Set :attr:`methods` on the class to change what methods the view + accepts. + + Set :attr:`decorators` on the class to apply a list of decorators to + the generated view function. Decorators applied to the class itself + will not be applied to the generated view function! + + Set :attr:`init_every_request` to ``False`` for efficiency, unless + you need to store request-global data on ``self``. + """ + + #: The methods this view is registered for. Uses the same default + #: (``["GET", "HEAD", "OPTIONS"]``) as ``route`` and + #: ``add_url_rule`` by default. + methods: t.ClassVar[t.Collection[str] | None] = None + + #: Control whether the ``OPTIONS`` method is handled automatically. + #: Uses the same default (``True``) as ``route`` and + #: ``add_url_rule`` by default. + provide_automatic_options: t.ClassVar[bool | None] = None + + #: A list of decorators to apply, in order, to the generated view + #: function. Remember that ``@decorator`` syntax is applied bottom + #: to top, so the first decorator in the list would be the bottom + #: decorator. + #: + #: .. versionadded:: 0.8 + decorators: t.ClassVar[list[t.Callable[..., t.Any]]] = [] + + #: Create a new instance of this view class for every request by + #: default. If a view subclass sets this to ``False``, the same + #: instance is used for every request. + #: + #: A single instance is more efficient, especially if complex setup + #: is done during init. However, storing data on ``self`` is no + #: longer safe across requests, and :data:`~flask.g` should be used + #: instead. + #: + #: .. versionadded:: 2.2 + init_every_request: t.ClassVar[bool] = True + + def dispatch_request(self) -> ft.ResponseReturnValue: + """The actual view function behavior. Subclasses must override + this and return a valid response. Any variables from the URL + rule are passed as keyword arguments. + """ + raise NotImplementedError() + + @classmethod + def as_view( + cls, name: str, *class_args: t.Any, **class_kwargs: t.Any + ) -> ft.RouteCallable: + """Convert the class into a view function that can be registered + for a route. + + By default, the generated view will create a new instance of the + view class for every request and call its + :meth:`dispatch_request` method. If the view class sets + :attr:`init_every_request` to ``False``, the same instance will + be used for every request. + + Except for ``name``, all other arguments passed to this method + are forwarded to the view class ``__init__`` method. + + .. versionchanged:: 2.2 + Added the ``init_every_request`` class attribute. + """ + if cls.init_every_request: + + def view(**kwargs: t.Any) -> ft.ResponseReturnValue: + self = view.view_class( # type: ignore[attr-defined] + *class_args, **class_kwargs + ) + return current_app.ensure_sync(self.dispatch_request)(**kwargs) # type: ignore[no-any-return] + + else: + self = cls(*class_args, **class_kwargs) # pyright: ignore + + def view(**kwargs: t.Any) -> ft.ResponseReturnValue: + return current_app.ensure_sync(self.dispatch_request)(**kwargs) # type: ignore[no-any-return] + + if cls.decorators: + view.__name__ = name + view.__module__ = cls.__module__ + for decorator in cls.decorators: + view = decorator(view) + + # We attach the view class to the view function for two reasons: + # first of all it allows us to easily figure out what class-based + # view this thing came from, secondly it's also used for instantiating + # the view class so you can actually replace it with something else + # for testing purposes and debugging. + view.view_class = cls # type: ignore + view.__name__ = name + view.__doc__ = cls.__doc__ + view.__module__ = cls.__module__ + view.methods = cls.methods # type: ignore + view.provide_automatic_options = cls.provide_automatic_options # type: ignore + return view + + +class MethodView(View): + """Dispatches request methods to the corresponding instance methods. + For example, if you implement a ``get`` method, it will be used to + handle ``GET`` requests. + + This can be useful for defining a REST API. + + :attr:`methods` is automatically set based on the methods defined on + the class. + + See :doc:`views` for a detailed guide. + + .. code-block:: python + + class CounterAPI(MethodView): + def get(self): + return str(session.get("counter", 0)) + + def post(self): + session["counter"] = session.get("counter", 0) + 1 + return redirect(url_for("counter")) + + app.add_url_rule( + "/counter", view_func=CounterAPI.as_view("counter") + ) + """ + + def __init_subclass__(cls, **kwargs: t.Any) -> None: + super().__init_subclass__(**kwargs) + + if "methods" not in cls.__dict__: + methods = set() + + for base in cls.__bases__: + if getattr(base, "methods", None): + methods.update(base.methods) # type: ignore[attr-defined] + + for key in http_method_funcs: + if hasattr(cls, key): + methods.add(key.upper()) + + if methods: + cls.methods = methods + + def dispatch_request(self, **kwargs: t.Any) -> ft.ResponseReturnValue: + meth = getattr(self, request.method.lower(), None) + + # If the request method is HEAD and we don't have a handler for it + # retry with GET. + if meth is None and request.method == "HEAD": + meth = getattr(self, "get", None) + + assert meth is not None, f"Unimplemented method {request.method!r}" + return current_app.ensure_sync(meth)(**kwargs) # type: ignore[no-any-return] diff --git a/venv/Lib/site-packages/flask/wrappers.py b/venv/Lib/site-packages/flask/wrappers.py new file mode 100644 index 0000000..bab6102 --- /dev/null +++ b/venv/Lib/site-packages/flask/wrappers.py @@ -0,0 +1,257 @@ +from __future__ import annotations + +import typing as t + +from werkzeug.exceptions import BadRequest +from werkzeug.exceptions import HTTPException +from werkzeug.wrappers import Request as RequestBase +from werkzeug.wrappers import Response as ResponseBase + +from . import json +from .globals import current_app +from .helpers import _split_blueprint_path + +if t.TYPE_CHECKING: # pragma: no cover + from werkzeug.routing import Rule + + +class Request(RequestBase): + """The request object used by default in Flask. Remembers the + matched endpoint and view arguments. + + It is what ends up as :class:`~flask.request`. If you want to replace + the request object used you can subclass this and set + :attr:`~flask.Flask.request_class` to your subclass. + + The request object is a :class:`~werkzeug.wrappers.Request` subclass and + provides all of the attributes Werkzeug defines plus a few Flask + specific ones. + """ + + json_module: t.Any = json + + #: The internal URL rule that matched the request. This can be + #: useful to inspect which methods are allowed for the URL from + #: a before/after handler (``request.url_rule.methods``) etc. + #: Though if the request's method was invalid for the URL rule, + #: the valid list is available in ``routing_exception.valid_methods`` + #: instead (an attribute of the Werkzeug exception + #: :exc:`~werkzeug.exceptions.MethodNotAllowed`) + #: because the request was never internally bound. + #: + #: .. versionadded:: 0.6 + url_rule: Rule | None = None + + #: A dict of view arguments that matched the request. If an exception + #: happened when matching, this will be ``None``. + view_args: dict[str, t.Any] | None = None + + #: If matching the URL failed, this is the exception that will be + #: raised / was raised as part of the request handling. This is + #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or + #: something similar. + routing_exception: HTTPException | None = None + + _max_content_length: int | None = None + _max_form_memory_size: int | None = None + _max_form_parts: int | None = None + + @property + def max_content_length(self) -> int | None: + """The maximum number of bytes that will be read during this request. If + this limit is exceeded, a 413 :exc:`~werkzeug.exceptions.RequestEntityTooLarge` + error is raised. If it is set to ``None``, no limit is enforced at the + Flask application level. However, if it is ``None`` and the request has + no ``Content-Length`` header and the WSGI server does not indicate that + it terminates the stream, then no data is read to avoid an infinite + stream. + + Each request defaults to the :data:`MAX_CONTENT_LENGTH` config, which + defaults to ``None``. It can be set on a specific ``request`` to apply + the limit to that specific view. This should be set appropriately based + on an application's or view's specific needs. + + .. versionchanged:: 3.1 + This can be set per-request. + + .. versionchanged:: 0.6 + This is configurable through Flask config. + """ + if self._max_content_length is not None: + return self._max_content_length + + if not current_app: + return super().max_content_length + + return current_app.config["MAX_CONTENT_LENGTH"] # type: ignore[no-any-return] + + @max_content_length.setter + def max_content_length(self, value: int | None) -> None: + self._max_content_length = value + + @property + def max_form_memory_size(self) -> int | None: + """The maximum size in bytes any non-file form field may be in a + ``multipart/form-data`` body. If this limit is exceeded, a 413 + :exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it + is set to ``None``, no limit is enforced at the Flask application level. + + Each request defaults to the :data:`MAX_FORM_MEMORY_SIZE` config, which + defaults to ``500_000``. It can be set on a specific ``request`` to + apply the limit to that specific view. This should be set appropriately + based on an application's or view's specific needs. + + .. versionchanged:: 3.1 + This is configurable through Flask config. + """ + if self._max_form_memory_size is not None: + return self._max_form_memory_size + + if not current_app: + return super().max_form_memory_size + + return current_app.config["MAX_FORM_MEMORY_SIZE"] # type: ignore[no-any-return] + + @max_form_memory_size.setter + def max_form_memory_size(self, value: int | None) -> None: + self._max_form_memory_size = value + + @property # type: ignore[override] + def max_form_parts(self) -> int | None: + """The maximum number of fields that may be present in a + ``multipart/form-data`` body. If this limit is exceeded, a 413 + :exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it + is set to ``None``, no limit is enforced at the Flask application level. + + Each request defaults to the :data:`MAX_FORM_PARTS` config, which + defaults to ``1_000``. It can be set on a specific ``request`` to apply + the limit to that specific view. This should be set appropriately based + on an application's or view's specific needs. + + .. versionchanged:: 3.1 + This is configurable through Flask config. + """ + if self._max_form_parts is not None: + return self._max_form_parts + + if not current_app: + return super().max_form_parts + + return current_app.config["MAX_FORM_PARTS"] # type: ignore[no-any-return] + + @max_form_parts.setter + def max_form_parts(self, value: int | None) -> None: + self._max_form_parts = value + + @property + def endpoint(self) -> str | None: + """The endpoint that matched the request URL. + + This will be ``None`` if matching failed or has not been + performed yet. + + This in combination with :attr:`view_args` can be used to + reconstruct the same URL or a modified URL. + """ + if self.url_rule is not None: + return self.url_rule.endpoint # type: ignore[no-any-return] + + return None + + @property + def blueprint(self) -> str | None: + """The registered name of the current blueprint. + + This will be ``None`` if the endpoint is not part of a + blueprint, or if URL matching failed or has not been performed + yet. + + This does not necessarily match the name the blueprint was + created with. It may have been nested, or registered with a + different name. + """ + endpoint = self.endpoint + + if endpoint is not None and "." in endpoint: + return endpoint.rpartition(".")[0] + + return None + + @property + def blueprints(self) -> list[str]: + """The registered names of the current blueprint upwards through + parent blueprints. + + This will be an empty list if there is no current blueprint, or + if URL matching failed. + + .. versionadded:: 2.0.1 + """ + name = self.blueprint + + if name is None: + return [] + + return _split_blueprint_path(name) + + def _load_form_data(self) -> None: + super()._load_form_data() + + # In debug mode we're replacing the files multidict with an ad-hoc + # subclass that raises a different error for key errors. + if ( + current_app + and current_app.debug + and self.mimetype != "multipart/form-data" + and not self.files + ): + from .debughelpers import attach_enctype_error_multidict + + attach_enctype_error_multidict(self) + + def on_json_loading_failed(self, e: ValueError | None) -> t.Any: + try: + return super().on_json_loading_failed(e) + except BadRequest as ebr: + if current_app and current_app.debug: + raise + + raise BadRequest() from ebr + + +class Response(ResponseBase): + """The response object that is used by default in Flask. Works like the + response object from Werkzeug but is set to have an HTML mimetype by + default. Quite often you don't have to create this object yourself because + :meth:`~flask.Flask.make_response` will take care of that for you. + + If you want to replace the response object used you can subclass this and + set :attr:`~flask.Flask.response_class` to your subclass. + + .. versionchanged:: 1.0 + JSON support is added to the response, like the request. This is useful + when testing to get the test client response data as JSON. + + .. versionchanged:: 1.0 + + Added :attr:`max_cookie_size`. + """ + + default_mimetype: str | None = "text/html" + + json_module = json + + autocorrect_location_header = False + + @property + def max_cookie_size(self) -> int: # type: ignore + """Read-only view of the :data:`MAX_COOKIE_SIZE` config key. + + See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in + Werkzeug's docs. + """ + if current_app: + return current_app.config["MAX_COOKIE_SIZE"] # type: ignore[no-any-return] + + # return Werkzeug's default when not in an app context + return super().max_cookie_size diff --git a/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER b/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/LICENSE.txt b/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/LICENSE.txt new file mode 100644 index 0000000..7b190ca --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2011 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/METADATA b/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/METADATA new file mode 100644 index 0000000..ddf5464 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/METADATA @@ -0,0 +1,60 @@ +Metadata-Version: 2.1 +Name: itsdangerous +Version: 2.2.0 +Summary: Safely pass data to untrusted environments and back. +Maintainer-email: Pallets +Requires-Python: >=3.8 +Description-Content-Type: text/markdown +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 +Classifier: Typing :: Typed +Project-URL: Changes, https://itsdangerous.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://itsdangerous.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/itsdangerous/ + +# ItsDangerous + +... so better sign this + +Various helpers to pass data to untrusted environments and to get it +back safe and sound. Data is cryptographically signed to ensure that a +token has not been tampered with. + +It's possible to customize how data is serialized. Data is compressed as +needed. A timestamp can be added and verified automatically while +loading a token. + + +## A Simple Example + +Here's how you could generate a token for transmitting a user's id and +name between web requests. + +```python +from itsdangerous import URLSafeSerializer +auth_s = URLSafeSerializer("secret key", "auth") +token = auth_s.dumps({"id": 5, "name": "itsdangerous"}) + +print(token) +# eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg + +data = auth_s.loads(token) +print(data["name"]) +# itsdangerous +``` + + +## Donate + +The Pallets organization develops and supports ItsDangerous and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +[please donate today][]. + +[please donate today]: https://palletsprojects.com/donate + diff --git a/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/RECORD b/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/RECORD new file mode 100644 index 0000000..e937872 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/RECORD @@ -0,0 +1,22 @@ +itsdangerous-2.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +itsdangerous-2.2.0.dist-info/LICENSE.txt,sha256=Y68JiRtr6K0aQlLtQ68PTvun_JSOIoNnvtfzxa4LCdc,1475 +itsdangerous-2.2.0.dist-info/METADATA,sha256=0rk0-1ZwihuU5DnwJVwPWoEI4yWOyCexih3JyZHblhE,1924 +itsdangerous-2.2.0.dist-info/RECORD,, +itsdangerous-2.2.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +itsdangerous/__init__.py,sha256=4SK75sCe29xbRgQE1ZQtMHnKUuZYAf3bSpZOrff1IAY,1427 +itsdangerous/__pycache__/__init__.cpython-310.pyc,, +itsdangerous/__pycache__/_json.cpython-310.pyc,, +itsdangerous/__pycache__/encoding.cpython-310.pyc,, +itsdangerous/__pycache__/exc.cpython-310.pyc,, +itsdangerous/__pycache__/serializer.cpython-310.pyc,, +itsdangerous/__pycache__/signer.cpython-310.pyc,, +itsdangerous/__pycache__/timed.cpython-310.pyc,, +itsdangerous/__pycache__/url_safe.cpython-310.pyc,, +itsdangerous/_json.py,sha256=wPQGmge2yZ9328EHKF6gadGeyGYCJQKxtU-iLKE6UnA,473 +itsdangerous/encoding.py,sha256=wwTz5q_3zLcaAdunk6_vSoStwGqYWe307Zl_U87aRFM,1409 +itsdangerous/exc.py,sha256=Rr3exo0MRFEcPZltwecyK16VV1bE2K9_F1-d-ljcUn4,3201 +itsdangerous/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +itsdangerous/serializer.py,sha256=PmdwADLqkSyQLZ0jOKAgDsAW4k_H0TlA71Ei3z0C5aI,15601 +itsdangerous/signer.py,sha256=YO0CV7NBvHA6j549REHJFUjUojw2pHqwcUpQnU7yNYQ,9647 +itsdangerous/timed.py,sha256=6RvDMqNumGMxf0-HlpaZdN9PUQQmRvrQGplKhxuivUs,8083 +itsdangerous/url_safe.py,sha256=az4e5fXi_vs-YbWj8YZwn4wiVKfeD--GEKRT5Ueu4P4,2505 diff --git a/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/WHEEL b/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/WHEEL new file mode 100644 index 0000000..3b5e64b --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.9.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/Lib/site-packages/itsdangerous/__init__.py b/venv/Lib/site-packages/itsdangerous/__init__.py new file mode 100644 index 0000000..ea55256 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/__init__.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import typing as t + +from .encoding import base64_decode as base64_decode +from .encoding import base64_encode as base64_encode +from .encoding import want_bytes as want_bytes +from .exc import BadData as BadData +from .exc import BadHeader as BadHeader +from .exc import BadPayload as BadPayload +from .exc import BadSignature as BadSignature +from .exc import BadTimeSignature as BadTimeSignature +from .exc import SignatureExpired as SignatureExpired +from .serializer import Serializer as Serializer +from .signer import HMACAlgorithm as HMACAlgorithm +from .signer import NoneAlgorithm as NoneAlgorithm +from .signer import Signer as Signer +from .timed import TimedSerializer as TimedSerializer +from .timed import TimestampSigner as TimestampSigner +from .url_safe import URLSafeSerializer as URLSafeSerializer +from .url_safe import URLSafeTimedSerializer as URLSafeTimedSerializer + + +def __getattr__(name: str) -> t.Any: + if name == "__version__": + import importlib.metadata + import warnings + + warnings.warn( + "The '__version__' attribute is deprecated and will be removed in" + " ItsDangerous 2.3. Use feature detection or" + " 'importlib.metadata.version(\"itsdangerous\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("itsdangerous") + + raise AttributeError(name) diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a9ffacddc4ba70d9f5bd0310230f17dbc1f8ab42 GIT binary patch literal 1446 zcmaKsL2nyH6o7Yj?e*GTJC2hkOTOXym_-ltJUz}`Tftov&t; z$oA--<1X+%-FLjBZe|Dc!0|5dAw2}X#Si%5PZfHL`$MmH`w}bBBW082d10BAMQ%3C^{f#KTMj2|=00PBDc1>-`4(g5WNTdIO_ z&|=Vra+)(c*BG<|`cs*SYql%p!ONM{0_Qb`LQAIPD;Kl%@Y})tftnUt+UFSzw@!*& zY#Kt0;e59+Jij*GnT%!G?8{hJ>{MLy+glm`6``!;EE8p5tS&<>U?=(8s_hT* z)tjf!MRK%ZK1!IiT8`&dB+?{Y%(R#=D|o_k zo}5dilCem%$chEnWS%^-=9uMEp^LdmKIwnjPo5c(oCygyD;nM<3d{n-*vW(p;N zwbS$_L^iu1B7$$l_lY0)B>X$BMZW$J26`2jmEHr^`zUZu?{?Wp)6+R#3^-q`TN&Er z42n<^TSo5Mfubuo0~fgc5ZzgSBs5N`Dt>^kMkPobD}-7Vf%s5B>H*1$WNp3M)M;Xy-F2%}xwU70 zLo0FNC$U#f+_;hxGd4m2v69Dcc6Mjpyq#(|++w)C{rFjY1^knFymK$K-=g)OSmjC0!DTs}GnRALW- zVktbTuqiRu$+`c}3H@~im5}QJ2IEzHvc!-JwX%&a*MS`! zmdhNK`LhU>ic`=X*z{S2R|$zbdv zRhmYtOqjeVUbrozYN3U#v`P3H_SUeUc(iM!Iu*^_>OPimfov8vp3o6`dPs)4=*)o7 z12o(CnGj9HBiMz1nrl;x6Teke2Xu+f>K>XT>@pMX(zmFMQ`V&C`dLRZiQ6W;S0adsfL-^p@ZT>6U)WoF;;*8S1UieBj8Qdpn>hf|aq} tX@fI`wKb{le43I@n9wfkqMSOg?dks&aQ&wp-Dfm^hV+bBpZ8(c`vv0hkQ@w1}?7S(d9$D~wN@5bz- zSj8umLT#qqlNyM@DCq7hF15$AUe1!NuWe9kmkW^#sa?TUAu?StRy`YC zoqi}IWA~7;S3pqK$Wr8xirUB;(-9fbBYFTjh>^5gw)PX5WKzXhj|)r$I6Pz*`0Z;T@6# zfC%8MBQmxbIU)d#4!^w{OTMullz4(TK$5e5kZ+aj2Du0nf2!7kM^AVlxGYkI`zE3w z7TgculkpZ0R2~$P2T2xu`NaddW;XuBSZ=kP(_p*CnVu~~ibtaE#(L=(vW1QZEh6UO zgdwpUW?`ut<-w>lUI3NE{CUm{5eV=cd|=yIym&v`jnjk$B`vEZ zE%12ZwX|E+l59KWnOFq#b99%1>ZwQj1O72S6zzW%r$s3Xyd8XE3N+#Wm4)(-OJF3i z*qhE0p|nosh)=v9=u2@0ltX(px+PXnqPIUEURG=O;xvuBDS!Of;N83#sA2%iBvYH4 z+Rlm{ZG(I+mFFtC373_XJSJ!L+cpITG3!PIha;7jZ$-Q5Sv_5aWB)`_pR|1&3?vp! zlZe+~(p3|n68{PXI&WE3mu zpwM4O8zgX|(Cd??@4%i3Y=MZI<}QH|2uw4khZiY@nr6J}F~4 zsWrr0L$gV(i3-n^THJ4~Xg`X!3TUT1iUd++3ahLWY|90-WmoMFun7~TZT_@pYROch zd7-8#bi-74lyapwwrH00q4SApxG|iGP9J%k4xYqa4mUnDH-{#*8|%@jBL};txB+Lh z(~Bk86`4`Jfq`KL4OU_uP`5nl5&Y|vI`x}X I@$IJb7XVtyF8}}l literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/exc.cpython-310.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/exc.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..84f9916a094d704104c720c50bdbe2768bb5d3bb GIT binary patch literal 3380 zcmaJ@NpIUm6y{LWzF5uDI_bu4Kvg8_g7hK;MUp0HQxrzfltUsyZL9J(`haG1JiJa@*`7nWm< zYxr0@t>59|E>HOUx1V^RrEr5Ccbf|rALDMkQNF~M4eY6*7 z7r$gr;tefZF0P$exjYPA`P7$Fb9C(U#J!*ByFA*JDxPUq$ekdPde}0r42SN%R2pV- zA*2|M+%JY-er>kW`b28YcV$}9iSlh@W;W2glHE+diAiETvwmlX>?OcEWQUe#3+thE z#5M+%w5n%QsnUk$1yPWAo~q)7ft@;9hC4<$VrkR!{E%zyc}Ldqo!g_$2P%Fn{X}ox zzVFAADUWc(&3zf|Z~ho;Z|WeC*Qeb78OPI`L83)&-{&xXI6Y8Je58k46Fwc*V)OpO z*~9!GX-&ywrXru=k^G`L-QN`W1_Rd+eihtj&|!B3ud0fHtE z)!2Nk_@>z#*hYgQ#h9<*FPqoNKM|iu&r4gLH;Kh8qSK&2$t+ri&P3ww*fAuo z8WMM4>HZJY2ki2;deWTeqn z*-u+Tvs;3;@K4&N{OJ)Nq&Qy7lWC6iB8S2{3&}CN32RMo<+-9X-}wOaElr+u}AePC~F3~H-TS4Q=Fr2lOiR)PEuHYk`hr3?buuG?(= zKSk?jfTqNAi|nS*E(2|fQzh*$k-YbG8lVu&RTvmKs1ii(*4BhS z^^i2Twz3*xAj2~S#8h`iNTLSZ6YR+WhKpB`y|BIrt@mNaP29%M4XeQvt(>ttPdoav zQ+5-b>#n}F?m$zkC9kJGLIulLLrIblI+{G*~yB-P5vqKw8-cwqdn z$EQ;nX;VkFQ~{!3j^daV(LKH|;iiQ~Q&)E>sXYmjJu1US!xDkll-I9rye3X+nf|mF zt6(>Xc<4!`Vs+fVm)G<>W)?n!!z_#D6gL&(4)JkS%p5w19JEw^|J@&ml1KEyDn zo(`Yo)~Xo#0k4?!)vMQ>mNUPwgrf+b$_C=6u`+&)ZpxqL+v+2lof}CmcJJWUXeF~q zYS|32^sl0wb2+6b#iG}0;b;%CO2cm;(8_FCRG<=17Suq;6@o>E7u1|lyR~>Yj~psp zV49G6ilziD+mdQaGF)@fm}PG(Ngv_vB+#Z38ZFy0c_L+Gv=_XCDx-EuL^_j%s*D&# zmuGYsLdL#7Q%DQ0n5iJzb?I#Mk5B3(rj0mq(MpbZlBm&Et}5dg!rN$iO-oqL?Zg&? zm=D%1FPblIrmjLbTayjV$~D<&T2YfFUe9Z?Grw5ix8&B@io874`8=X*yh*C52{UE4 zp=fQ;q0Vb(Mr{l_DH}^^EjgINfhkKAS(P@IVQBg%Q?5-aH+jtv(da6bf@LyH8#lAI c?i;Yr($p$+tcKlT{YrnW-|MgUYnQJ65536W2mk;8 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/serializer.cpython-310.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/serializer.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..135e9da034274c870816dc74f13aae2a977b19af GIT binary patch literal 11712 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 Hohtng29fFA literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/signer.cpython-310.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/signer.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6311932b2a597bdbcea18b138ddf9d2e8ce24d8 GIT binary patch literal 8991 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 C-u7nz literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/__pycache__/timed.cpython-310.pyc b/venv/Lib/site-packages/itsdangerous/__pycache__/timed.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a8a1b2595df52a6284d829e7b4b74c7933e5e13a GIT binary patch literal 6551 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/itsdangerous/_json.py b/venv/Lib/site-packages/itsdangerous/_json.py new file mode 100644 index 0000000..fc23fea --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/_json.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +import json as _json +import typing as t + + +class _CompactJSON: + """Wrapper around json module that strips whitespace.""" + + @staticmethod + def loads(payload: str | bytes) -> t.Any: + return _json.loads(payload) + + @staticmethod + def dumps(obj: t.Any, **kwargs: t.Any) -> str: + kwargs.setdefault("ensure_ascii", False) + kwargs.setdefault("separators", (",", ":")) + return _json.dumps(obj, **kwargs) diff --git a/venv/Lib/site-packages/itsdangerous/encoding.py b/venv/Lib/site-packages/itsdangerous/encoding.py new file mode 100644 index 0000000..f5ca80f --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/encoding.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +import base64 +import string +import struct +import typing as t + +from .exc import BadData + + +def want_bytes( + s: str | bytes, encoding: str = "utf-8", errors: str = "strict" +) -> bytes: + if isinstance(s, str): + s = s.encode(encoding, errors) + + return s + + +def base64_encode(string: str | bytes) -> bytes: + """Base64 encode a string of bytes or text. The resulting bytes are + safe to use in URLs. + """ + string = want_bytes(string) + return base64.urlsafe_b64encode(string).rstrip(b"=") + + +def base64_decode(string: str | bytes) -> bytes: + """Base64 decode a URL-safe string of bytes or text. The result is + bytes. + """ + string = want_bytes(string, encoding="ascii", errors="ignore") + string += b"=" * (-len(string) % 4) + + try: + return base64.urlsafe_b64decode(string) + except (TypeError, ValueError) as e: + raise BadData("Invalid base64-encoded data") from e + + +# The alphabet used by base64.urlsafe_* +_base64_alphabet = f"{string.ascii_letters}{string.digits}-_=".encode("ascii") + +_int64_struct = struct.Struct(">Q") +_int_to_bytes = _int64_struct.pack +_bytes_to_int = t.cast("t.Callable[[bytes], tuple[int]]", _int64_struct.unpack) + + +def int_to_bytes(num: int) -> bytes: + return _int_to_bytes(num).lstrip(b"\x00") + + +def bytes_to_int(bytestr: bytes) -> int: + return _bytes_to_int(bytestr.rjust(8, b"\x00"))[0] diff --git a/venv/Lib/site-packages/itsdangerous/exc.py b/venv/Lib/site-packages/itsdangerous/exc.py new file mode 100644 index 0000000..a75adcd --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/exc.py @@ -0,0 +1,106 @@ +from __future__ import annotations + +import typing as t +from datetime import datetime + + +class BadData(Exception): + """Raised if bad data of any sort was encountered. This is the base + for all exceptions that ItsDangerous defines. + + .. versionadded:: 0.15 + """ + + def __init__(self, message: str): + super().__init__(message) + self.message = message + + def __str__(self) -> str: + return self.message + + +class BadSignature(BadData): + """Raised if a signature does not match.""" + + def __init__(self, message: str, payload: t.Any | None = None): + super().__init__(message) + + #: The payload that failed the signature test. In some + #: situations you might still want to inspect this, even if + #: you know it was tampered with. + #: + #: .. versionadded:: 0.14 + self.payload: t.Any | None = payload + + +class BadTimeSignature(BadSignature): + """Raised if a time-based signature is invalid. This is a subclass + of :class:`BadSignature`. + """ + + def __init__( + self, + message: str, + payload: t.Any | None = None, + date_signed: datetime | None = None, + ): + super().__init__(message, payload) + + #: If the signature expired this exposes the date of when the + #: signature was created. This can be helpful in order to + #: tell the user how long a link has been gone stale. + #: + #: .. versionchanged:: 2.0 + #: The datetime value is timezone-aware rather than naive. + #: + #: .. versionadded:: 0.14 + self.date_signed = date_signed + + +class SignatureExpired(BadTimeSignature): + """Raised if a signature timestamp is older than ``max_age``. This + is a subclass of :exc:`BadTimeSignature`. + """ + + +class BadHeader(BadSignature): + """Raised if a signed header is invalid in some form. This only + happens for serializers that have a header that goes with the + signature. + + .. versionadded:: 0.24 + """ + + def __init__( + self, + message: str, + payload: t.Any | None = None, + header: t.Any | None = None, + original_error: Exception | None = None, + ): + super().__init__(message, payload) + + #: If the header is actually available but just malformed it + #: might be stored here. + self.header: t.Any | None = header + + #: If available, the error that indicates why the payload was + #: not valid. This might be ``None``. + self.original_error: Exception | None = original_error + + +class BadPayload(BadData): + """Raised if a payload is invalid. This could happen if the payload + is loaded despite an invalid signature, or if there is a mismatch + between the serializer and deserializer. The original exception + that occurred during loading is stored on as :attr:`original_error`. + + .. versionadded:: 0.15 + """ + + def __init__(self, message: str, original_error: Exception | None = None): + super().__init__(message) + + #: If available, the error that indicates why the payload was + #: not valid. This might be ``None``. + self.original_error: Exception | None = original_error diff --git a/venv/Lib/site-packages/itsdangerous/py.typed b/venv/Lib/site-packages/itsdangerous/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/venv/Lib/site-packages/itsdangerous/serializer.py b/venv/Lib/site-packages/itsdangerous/serializer.py new file mode 100644 index 0000000..5ddf387 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/serializer.py @@ -0,0 +1,406 @@ +from __future__ import annotations + +import collections.abc as cabc +import json +import typing as t + +from .encoding import want_bytes +from .exc import BadPayload +from .exc import BadSignature +from .signer import _make_keys_list +from .signer import Signer + +if t.TYPE_CHECKING: + import typing_extensions as te + + # This should be either be str or bytes. To avoid having to specify the + # bound type, it falls back to a union if structural matching fails. + _TSerialized = te.TypeVar( + "_TSerialized", bound=t.Union[str, bytes], default=t.Union[str, bytes] + ) +else: + # Still available at runtime on Python < 3.13, but without the default. + _TSerialized = t.TypeVar("_TSerialized", bound=t.Union[str, bytes]) + + +class _PDataSerializer(t.Protocol[_TSerialized]): + def loads(self, payload: _TSerialized, /) -> t.Any: ... + # A signature with additional arguments is not handled correctly by type + # checkers right now, so an overload is used below for serializers that + # don't match this strict protocol. + def dumps(self, obj: t.Any, /) -> _TSerialized: ... + + +# Use TypeIs once it's available in typing_extensions or 3.13. +def is_text_serializer( + serializer: _PDataSerializer[t.Any], +) -> te.TypeGuard[_PDataSerializer[str]]: + """Checks whether a serializer generates text or binary.""" + return isinstance(serializer.dumps({}), str) + + +class Serializer(t.Generic[_TSerialized]): + """A serializer wraps a :class:`~itsdangerous.signer.Signer` to + enable serializing and securely signing data other than bytes. It + can unsign to verify that the data hasn't been changed. + + The serializer provides :meth:`dumps` and :meth:`loads`, similar to + :mod:`json`, and by default uses :mod:`json` internally to serialize + the data to bytes. + + The secret key should be a random string of ``bytes`` and should not + be saved to code or version control. Different salts should be used + to distinguish signing in different contexts. See :doc:`/concepts` + for information about the security of the secret key and salt. + + :param secret_key: The secret key to sign and verify with. Can be a + list of keys, oldest to newest, to support key rotation. + :param salt: Extra key to combine with ``secret_key`` to distinguish + signatures in different contexts. + :param serializer: An object that provides ``dumps`` and ``loads`` + methods for serializing data to a string. Defaults to + :attr:`default_serializer`, which defaults to :mod:`json`. + :param serializer_kwargs: Keyword arguments to pass when calling + ``serializer.dumps``. + :param signer: A ``Signer`` class to instantiate when signing data. + Defaults to :attr:`default_signer`, which defaults to + :class:`~itsdangerous.signer.Signer`. + :param signer_kwargs: Keyword arguments to pass when instantiating + the ``Signer`` class. + :param fallback_signers: List of signer parameters to try when + unsigning with the default signer fails. Each item can be a dict + of ``signer_kwargs``, a ``Signer`` class, or a tuple of + ``(signer, signer_kwargs)``. Defaults to + :attr:`default_fallback_signers`. + + .. versionchanged:: 2.0 + Added support for key rotation by passing a list to + ``secret_key``. + + .. versionchanged:: 2.0 + Removed the default SHA-512 fallback signer from + ``default_fallback_signers``. + + .. versionchanged:: 1.1 + Added support for ``fallback_signers`` and configured a default + SHA-512 fallback. This fallback is for users who used the yanked + 1.0.0 release which defaulted to SHA-512. + + .. versionchanged:: 0.14 + The ``signer`` and ``signer_kwargs`` parameters were added to + the constructor. + """ + + #: The default serialization module to use to serialize data to a + #: string internally. The default is :mod:`json`, but can be changed + #: to any object that provides ``dumps`` and ``loads`` methods. + default_serializer: _PDataSerializer[t.Any] = json + + #: The default ``Signer`` class to instantiate when signing data. + #: The default is :class:`itsdangerous.signer.Signer`. + default_signer: type[Signer] = Signer + + #: The default fallback signers to try when unsigning fails. + default_fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] = [] + + # Serializer[str] if no data serializer is provided, or if it returns str. + @t.overload + def __init__( + self: Serializer[str], + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None = b"itsdangerous", + serializer: None | _PDataSerializer[str] = None, + serializer_kwargs: dict[str, t.Any] | None = None, + signer: type[Signer] | None = None, + signer_kwargs: dict[str, t.Any] | None = None, + fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] + | None = None, + ): ... + + # Serializer[bytes] with a bytes data serializer positional argument. + @t.overload + def __init__( + self: Serializer[bytes], + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None, + serializer: _PDataSerializer[bytes], + serializer_kwargs: dict[str, t.Any] | None = None, + signer: type[Signer] | None = None, + signer_kwargs: dict[str, t.Any] | None = None, + fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] + | None = None, + ): ... + + # Serializer[bytes] with a bytes data serializer keyword argument. + @t.overload + def __init__( + self: Serializer[bytes], + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None = b"itsdangerous", + *, + serializer: _PDataSerializer[bytes], + serializer_kwargs: dict[str, t.Any] | None = None, + signer: type[Signer] | None = None, + signer_kwargs: dict[str, t.Any] | None = None, + fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] + | None = None, + ): ... + + # Fall back with a positional argument. If the strict signature of + # _PDataSerializer doesn't match, fall back to a union, requiring the user + # to specify the type. + @t.overload + def __init__( + self, + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None, + serializer: t.Any, + serializer_kwargs: dict[str, t.Any] | None = None, + signer: type[Signer] | None = None, + signer_kwargs: dict[str, t.Any] | None = None, + fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] + | None = None, + ): ... + + # Fall back with a keyword argument. + @t.overload + def __init__( + self, + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None = b"itsdangerous", + *, + serializer: t.Any, + serializer_kwargs: dict[str, t.Any] | None = None, + signer: type[Signer] | None = None, + signer_kwargs: dict[str, t.Any] | None = None, + fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] + | None = None, + ): ... + + def __init__( + self, + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None = b"itsdangerous", + serializer: t.Any | None = None, + serializer_kwargs: dict[str, t.Any] | None = None, + signer: type[Signer] | None = None, + signer_kwargs: dict[str, t.Any] | None = None, + fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] + | None = None, + ): + #: The list of secret keys to try for verifying signatures, from + #: oldest to newest. The newest (last) key is used for signing. + #: + #: This allows a key rotation system to keep a list of allowed + #: keys and remove expired ones. + self.secret_keys: list[bytes] = _make_keys_list(secret_key) + + if salt is not None: + salt = want_bytes(salt) + # if salt is None then the signer's default is used + + self.salt = salt + + if serializer is None: + serializer = self.default_serializer + + self.serializer: _PDataSerializer[_TSerialized] = serializer + self.is_text_serializer: bool = is_text_serializer(serializer) + + if signer is None: + signer = self.default_signer + + self.signer: type[Signer] = signer + self.signer_kwargs: dict[str, t.Any] = signer_kwargs or {} + + if fallback_signers is None: + fallback_signers = list(self.default_fallback_signers) + + self.fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] = fallback_signers + self.serializer_kwargs: dict[str, t.Any] = serializer_kwargs or {} + + @property + def secret_key(self) -> bytes: + """The newest (last) entry in the :attr:`secret_keys` list. This + is for compatibility from before key rotation support was added. + """ + return self.secret_keys[-1] + + def load_payload( + self, payload: bytes, serializer: _PDataSerializer[t.Any] | None = None + ) -> t.Any: + """Loads the encoded object. This function raises + :class:`.BadPayload` if the payload is not valid. The + ``serializer`` parameter can be used to override the serializer + stored on the class. The encoded ``payload`` should always be + bytes. + """ + if serializer is None: + use_serializer = self.serializer + is_text = self.is_text_serializer + else: + use_serializer = serializer + is_text = is_text_serializer(serializer) + + try: + if is_text: + return use_serializer.loads(payload.decode("utf-8")) # type: ignore[arg-type] + + return use_serializer.loads(payload) # type: ignore[arg-type] + except Exception as e: + raise BadPayload( + "Could not load the payload because an exception" + " occurred on unserializing the data.", + original_error=e, + ) from e + + def dump_payload(self, obj: t.Any) -> bytes: + """Dumps the encoded object. The return value is always bytes. + If the internal serializer returns text, the value will be + encoded as UTF-8. + """ + return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs)) + + def make_signer(self, salt: str | bytes | None = None) -> Signer: + """Creates a new instance of the signer to be used. The default + implementation uses the :class:`.Signer` base class. + """ + if salt is None: + salt = self.salt + + return self.signer(self.secret_keys, salt=salt, **self.signer_kwargs) + + def iter_unsigners(self, salt: str | bytes | None = None) -> cabc.Iterator[Signer]: + """Iterates over all signers to be tried for unsigning. Starts + with the configured signer, then constructs each signer + specified in ``fallback_signers``. + """ + if salt is None: + salt = self.salt + + yield self.make_signer(salt) + + for fallback in self.fallback_signers: + if isinstance(fallback, dict): + kwargs = fallback + fallback = self.signer + elif isinstance(fallback, tuple): + fallback, kwargs = fallback + else: + kwargs = self.signer_kwargs + + for secret_key in self.secret_keys: + yield fallback(secret_key, salt=salt, **kwargs) + + def dumps(self, obj: t.Any, salt: str | bytes | None = None) -> _TSerialized: + """Returns a signed string serialized with the internal + serializer. The return value can be either a byte or unicode + string depending on the format of the internal serializer. + """ + payload = want_bytes(self.dump_payload(obj)) + rv = self.make_signer(salt).sign(payload) + + if self.is_text_serializer: + return rv.decode("utf-8") # type: ignore[return-value] + + return rv # type: ignore[return-value] + + def dump(self, obj: t.Any, f: t.IO[t.Any], salt: str | bytes | None = None) -> None: + """Like :meth:`dumps` but dumps into a file. The file handle has + to be compatible with what the internal serializer expects. + """ + f.write(self.dumps(obj, salt)) + + def loads( + self, s: str | bytes, salt: str | bytes | None = None, **kwargs: t.Any + ) -> t.Any: + """Reverse of :meth:`dumps`. Raises :exc:`.BadSignature` if the + signature validation fails. + """ + s = want_bytes(s) + last_exception = None + + for signer in self.iter_unsigners(salt): + try: + return self.load_payload(signer.unsign(s)) + except BadSignature as err: + last_exception = err + + raise t.cast(BadSignature, last_exception) + + def load(self, f: t.IO[t.Any], salt: str | bytes | None = None) -> t.Any: + """Like :meth:`loads` but loads from a file.""" + return self.loads(f.read(), salt) + + def loads_unsafe( + self, s: str | bytes, salt: str | bytes | None = None + ) -> tuple[bool, t.Any]: + """Like :meth:`loads` but without verifying the signature. This + is potentially very dangerous to use depending on how your + serializer works. The return value is ``(signature_valid, + payload)`` instead of just the payload. The first item will be a + boolean that indicates if the signature is valid. This function + never fails. + + Use it for debugging only and if you know that your serializer + module is not exploitable (for example, do not use it with a + pickle serializer). + + .. versionadded:: 0.15 + """ + return self._loads_unsafe_impl(s, salt) + + def _loads_unsafe_impl( + self, + s: str | bytes, + salt: str | bytes | None, + load_kwargs: dict[str, t.Any] | None = None, + load_payload_kwargs: dict[str, t.Any] | None = None, + ) -> tuple[bool, t.Any]: + """Low level helper function to implement :meth:`loads_unsafe` + in serializer subclasses. + """ + if load_kwargs is None: + load_kwargs = {} + + try: + return True, self.loads(s, salt=salt, **load_kwargs) + except BadSignature as e: + if e.payload is None: + return False, None + + if load_payload_kwargs is None: + load_payload_kwargs = {} + + try: + return ( + False, + self.load_payload(e.payload, **load_payload_kwargs), + ) + except BadPayload: + return False, None + + def load_unsafe( + self, f: t.IO[t.Any], salt: str | bytes | None = None + ) -> tuple[bool, t.Any]: + """Like :meth:`loads_unsafe` but loads from a file. + + .. versionadded:: 0.15 + """ + return self.loads_unsafe(f.read(), salt=salt) diff --git a/venv/Lib/site-packages/itsdangerous/signer.py b/venv/Lib/site-packages/itsdangerous/signer.py new file mode 100644 index 0000000..e324dc0 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/signer.py @@ -0,0 +1,266 @@ +from __future__ import annotations + +import collections.abc as cabc +import hashlib +import hmac +import typing as t + +from .encoding import _base64_alphabet +from .encoding import base64_decode +from .encoding import base64_encode +from .encoding import want_bytes +from .exc import BadSignature + + +class SigningAlgorithm: + """Subclasses must implement :meth:`get_signature` to provide + signature generation functionality. + """ + + def get_signature(self, key: bytes, value: bytes) -> bytes: + """Returns the signature for the given key and value.""" + raise NotImplementedError() + + def verify_signature(self, key: bytes, value: bytes, sig: bytes) -> bool: + """Verifies the given signature matches the expected + signature. + """ + return hmac.compare_digest(sig, self.get_signature(key, value)) + + +class NoneAlgorithm(SigningAlgorithm): + """Provides an algorithm that does not perform any signing and + returns an empty signature. + """ + + def get_signature(self, key: bytes, value: bytes) -> bytes: + return b"" + + +def _lazy_sha1(string: bytes = b"") -> t.Any: + """Don't access ``hashlib.sha1`` until runtime. FIPS builds may not include + SHA-1, in which case the import and use as a default would fail before the + developer can configure something else. + """ + return hashlib.sha1(string) + + +class HMACAlgorithm(SigningAlgorithm): + """Provides signature generation using HMACs.""" + + #: The digest method to use with the MAC algorithm. This defaults to + #: SHA1, but can be changed to any other function in the hashlib + #: module. + default_digest_method: t.Any = staticmethod(_lazy_sha1) + + def __init__(self, digest_method: t.Any = None): + if digest_method is None: + digest_method = self.default_digest_method + + self.digest_method: t.Any = digest_method + + def get_signature(self, key: bytes, value: bytes) -> bytes: + mac = hmac.new(key, msg=value, digestmod=self.digest_method) + return mac.digest() + + +def _make_keys_list( + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], +) -> list[bytes]: + if isinstance(secret_key, (str, bytes)): + return [want_bytes(secret_key)] + + return [want_bytes(s) for s in secret_key] # pyright: ignore + + +class Signer: + """A signer securely signs bytes, then unsigns them to verify that + the value hasn't been changed. + + The secret key should be a random string of ``bytes`` and should not + be saved to code or version control. Different salts should be used + to distinguish signing in different contexts. See :doc:`/concepts` + for information about the security of the secret key and salt. + + :param secret_key: The secret key to sign and verify with. Can be a + list of keys, oldest to newest, to support key rotation. + :param salt: Extra key to combine with ``secret_key`` to distinguish + signatures in different contexts. + :param sep: Separator between the signature and value. + :param key_derivation: How to derive the signing key from the secret + key and salt. Possible values are ``concat``, ``django-concat``, + or ``hmac``. Defaults to :attr:`default_key_derivation`, which + defaults to ``django-concat``. + :param digest_method: Hash function to use when generating the HMAC + signature. Defaults to :attr:`default_digest_method`, which + defaults to :func:`hashlib.sha1`. Note that the security of the + hash alone doesn't apply when used intermediately in HMAC. + :param algorithm: A :class:`SigningAlgorithm` instance to use + instead of building a default :class:`HMACAlgorithm` with the + ``digest_method``. + + .. versionchanged:: 2.0 + Added support for key rotation by passing a list to + ``secret_key``. + + .. versionchanged:: 0.18 + ``algorithm`` was added as an argument to the class constructor. + + .. versionchanged:: 0.14 + ``key_derivation`` and ``digest_method`` were added as arguments + to the class constructor. + """ + + #: The default digest method to use for the signer. The default is + #: :func:`hashlib.sha1`, but can be changed to any :mod:`hashlib` or + #: compatible object. Note that the security of the hash alone + #: doesn't apply when used intermediately in HMAC. + #: + #: .. versionadded:: 0.14 + default_digest_method: t.Any = staticmethod(_lazy_sha1) + + #: The default scheme to use to derive the signing key from the + #: secret key and salt. The default is ``django-concat``. Possible + #: values are ``concat``, ``django-concat``, and ``hmac``. + #: + #: .. versionadded:: 0.14 + default_key_derivation: str = "django-concat" + + def __init__( + self, + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None = b"itsdangerous.Signer", + sep: str | bytes = b".", + key_derivation: str | None = None, + digest_method: t.Any | None = None, + algorithm: SigningAlgorithm | None = None, + ): + #: The list of secret keys to try for verifying signatures, from + #: oldest to newest. The newest (last) key is used for signing. + #: + #: This allows a key rotation system to keep a list of allowed + #: keys and remove expired ones. + self.secret_keys: list[bytes] = _make_keys_list(secret_key) + self.sep: bytes = want_bytes(sep) + + if self.sep in _base64_alphabet: + raise ValueError( + "The given separator cannot be used because it may be" + " contained in the signature itself. ASCII letters," + " digits, and '-_=' must not be used." + ) + + if salt is not None: + salt = want_bytes(salt) + else: + salt = b"itsdangerous.Signer" + + self.salt = salt + + if key_derivation is None: + key_derivation = self.default_key_derivation + + self.key_derivation: str = key_derivation + + if digest_method is None: + digest_method = self.default_digest_method + + self.digest_method: t.Any = digest_method + + if algorithm is None: + algorithm = HMACAlgorithm(self.digest_method) + + self.algorithm: SigningAlgorithm = algorithm + + @property + def secret_key(self) -> bytes: + """The newest (last) entry in the :attr:`secret_keys` list. This + is for compatibility from before key rotation support was added. + """ + return self.secret_keys[-1] + + def derive_key(self, secret_key: str | bytes | None = None) -> bytes: + """This method is called to derive the key. The default key + derivation choices can be overridden here. Key derivation is not + intended to be used as a security method to make a complex key + out of a short password. Instead you should use large random + secret keys. + + :param secret_key: A specific secret key to derive from. + Defaults to the last item in :attr:`secret_keys`. + + .. versionchanged:: 2.0 + Added the ``secret_key`` parameter. + """ + if secret_key is None: + secret_key = self.secret_keys[-1] + else: + secret_key = want_bytes(secret_key) + + if self.key_derivation == "concat": + return t.cast(bytes, self.digest_method(self.salt + secret_key).digest()) + elif self.key_derivation == "django-concat": + return t.cast( + bytes, self.digest_method(self.salt + b"signer" + secret_key).digest() + ) + elif self.key_derivation == "hmac": + mac = hmac.new(secret_key, digestmod=self.digest_method) + mac.update(self.salt) + return mac.digest() + elif self.key_derivation == "none": + return secret_key + else: + raise TypeError("Unknown key derivation method") + + def get_signature(self, value: str | bytes) -> bytes: + """Returns the signature for the given value.""" + value = want_bytes(value) + key = self.derive_key() + sig = self.algorithm.get_signature(key, value) + return base64_encode(sig) + + def sign(self, value: str | bytes) -> bytes: + """Signs the given string.""" + value = want_bytes(value) + return value + self.sep + self.get_signature(value) + + def verify_signature(self, value: str | bytes, sig: str | bytes) -> bool: + """Verifies the signature for the given value.""" + try: + sig = base64_decode(sig) + except Exception: + return False + + value = want_bytes(value) + + for secret_key in reversed(self.secret_keys): + key = self.derive_key(secret_key) + + if self.algorithm.verify_signature(key, value, sig): + return True + + return False + + def unsign(self, signed_value: str | bytes) -> bytes: + """Unsigns the given string.""" + signed_value = want_bytes(signed_value) + + if self.sep not in signed_value: + raise BadSignature(f"No {self.sep!r} found in value") + + value, sig = signed_value.rsplit(self.sep, 1) + + if self.verify_signature(value, sig): + return value + + raise BadSignature(f"Signature {sig!r} does not match", payload=value) + + def validate(self, signed_value: str | bytes) -> bool: + """Only validates the given signed value. Returns ``True`` if + the signature exists and is valid. + """ + try: + self.unsign(signed_value) + return True + except BadSignature: + return False diff --git a/venv/Lib/site-packages/itsdangerous/timed.py b/venv/Lib/site-packages/itsdangerous/timed.py new file mode 100644 index 0000000..7384375 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/timed.py @@ -0,0 +1,228 @@ +from __future__ import annotations + +import collections.abc as cabc +import time +import typing as t +from datetime import datetime +from datetime import timezone + +from .encoding import base64_decode +from .encoding import base64_encode +from .encoding import bytes_to_int +from .encoding import int_to_bytes +from .encoding import want_bytes +from .exc import BadSignature +from .exc import BadTimeSignature +from .exc import SignatureExpired +from .serializer import _TSerialized +from .serializer import Serializer +from .signer import Signer + + +class TimestampSigner(Signer): + """Works like the regular :class:`.Signer` but also records the time + of the signing and can be used to expire signatures. The + :meth:`unsign` method can raise :exc:`.SignatureExpired` if the + unsigning failed because the signature is expired. + """ + + def get_timestamp(self) -> int: + """Returns the current timestamp. The function must return an + integer. + """ + return int(time.time()) + + def timestamp_to_datetime(self, ts: int) -> datetime: + """Convert the timestamp from :meth:`get_timestamp` into an + aware :class`datetime.datetime` in UTC. + + .. versionchanged:: 2.0 + The timestamp is returned as a timezone-aware ``datetime`` + in UTC rather than a naive ``datetime`` assumed to be UTC. + """ + return datetime.fromtimestamp(ts, tz=timezone.utc) + + def sign(self, value: str | bytes) -> bytes: + """Signs the given string and also attaches time information.""" + value = want_bytes(value) + timestamp = base64_encode(int_to_bytes(self.get_timestamp())) + sep = want_bytes(self.sep) + value = value + sep + timestamp + return value + sep + self.get_signature(value) + + # Ignore overlapping signatures check, return_timestamp is the only + # parameter that affects the return type. + + @t.overload + def unsign( # type: ignore[overload-overlap] + self, + signed_value: str | bytes, + max_age: int | None = None, + return_timestamp: t.Literal[False] = False, + ) -> bytes: ... + + @t.overload + def unsign( + self, + signed_value: str | bytes, + max_age: int | None = None, + return_timestamp: t.Literal[True] = True, + ) -> tuple[bytes, datetime]: ... + + def unsign( + self, + signed_value: str | bytes, + max_age: int | None = None, + return_timestamp: bool = False, + ) -> tuple[bytes, datetime] | bytes: + """Works like the regular :meth:`.Signer.unsign` but can also + validate the time. See the base docstring of the class for + the general behavior. If ``return_timestamp`` is ``True`` the + timestamp of the signature will be returned as an aware + :class:`datetime.datetime` object in UTC. + + .. versionchanged:: 2.0 + The timestamp is returned as a timezone-aware ``datetime`` + in UTC rather than a naive ``datetime`` assumed to be UTC. + """ + try: + result = super().unsign(signed_value) + sig_error = None + except BadSignature as e: + sig_error = e + result = e.payload or b"" + + sep = want_bytes(self.sep) + + # If there is no timestamp in the result there is something + # seriously wrong. In case there was a signature error, we raise + # that one directly, otherwise we have a weird situation in + # which we shouldn't have come except someone uses a time-based + # serializer on non-timestamp data, so catch that. + if sep not in result: + if sig_error: + raise sig_error + + raise BadTimeSignature("timestamp missing", payload=result) + + value, ts_bytes = result.rsplit(sep, 1) + ts_int: int | None = None + ts_dt: datetime | None = None + + try: + ts_int = bytes_to_int(base64_decode(ts_bytes)) + except Exception: + pass + + # Signature is *not* okay. Raise a proper error now that we have + # split the value and the timestamp. + if sig_error is not None: + if ts_int is not None: + try: + ts_dt = self.timestamp_to_datetime(ts_int) + except (ValueError, OSError, OverflowError) as exc: + # Windows raises OSError + # 32-bit raises OverflowError + raise BadTimeSignature( + "Malformed timestamp", payload=value + ) from exc + + raise BadTimeSignature(str(sig_error), payload=value, date_signed=ts_dt) + + # Signature was okay but the timestamp is actually not there or + # malformed. Should not happen, but we handle it anyway. + if ts_int is None: + raise BadTimeSignature("Malformed timestamp", payload=value) + + # Check timestamp is not older than max_age + if max_age is not None: + age = self.get_timestamp() - ts_int + + if age > max_age: + raise SignatureExpired( + f"Signature age {age} > {max_age} seconds", + payload=value, + date_signed=self.timestamp_to_datetime(ts_int), + ) + + if age < 0: + raise SignatureExpired( + f"Signature age {age} < 0 seconds", + payload=value, + date_signed=self.timestamp_to_datetime(ts_int), + ) + + if return_timestamp: + return value, self.timestamp_to_datetime(ts_int) + + return value + + def validate(self, signed_value: str | bytes, max_age: int | None = None) -> bool: + """Only validates the given signed value. Returns ``True`` if + the signature exists and is valid.""" + try: + self.unsign(signed_value, max_age=max_age) + return True + except BadSignature: + return False + + +class TimedSerializer(Serializer[_TSerialized]): + """Uses :class:`TimestampSigner` instead of the default + :class:`.Signer`. + """ + + default_signer: type[TimestampSigner] = TimestampSigner + + def iter_unsigners( + self, salt: str | bytes | None = None + ) -> cabc.Iterator[TimestampSigner]: + return t.cast("cabc.Iterator[TimestampSigner]", super().iter_unsigners(salt)) + + # TODO: Signature is incompatible because parameters were added + # before salt. + + def loads( # type: ignore[override] + self, + s: str | bytes, + max_age: int | None = None, + return_timestamp: bool = False, + salt: str | bytes | None = None, + ) -> t.Any: + """Reverse of :meth:`dumps`, raises :exc:`.BadSignature` if the + signature validation fails. If a ``max_age`` is provided it will + ensure the signature is not older than that time in seconds. In + case the signature is outdated, :exc:`.SignatureExpired` is + raised. All arguments are forwarded to the signer's + :meth:`~TimestampSigner.unsign` method. + """ + s = want_bytes(s) + last_exception = None + + for signer in self.iter_unsigners(salt): + try: + base64d, timestamp = signer.unsign( + s, max_age=max_age, return_timestamp=True + ) + payload = self.load_payload(base64d) + + if return_timestamp: + return payload, timestamp + + return payload + except SignatureExpired: + # The signature was unsigned successfully but was + # expired. Do not try the next signer. + raise + except BadSignature as err: + last_exception = err + + raise t.cast(BadSignature, last_exception) + + def loads_unsafe( # type: ignore[override] + self, + s: str | bytes, + max_age: int | None = None, + salt: str | bytes | None = None, + ) -> tuple[bool, t.Any]: + return self._loads_unsafe_impl(s, salt, load_kwargs={"max_age": max_age}) diff --git a/venv/Lib/site-packages/itsdangerous/url_safe.py b/venv/Lib/site-packages/itsdangerous/url_safe.py new file mode 100644 index 0000000..56a0793 --- /dev/null +++ b/venv/Lib/site-packages/itsdangerous/url_safe.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +import typing as t +import zlib + +from ._json import _CompactJSON +from .encoding import base64_decode +from .encoding import base64_encode +from .exc import BadPayload +from .serializer import _PDataSerializer +from .serializer import Serializer +from .timed import TimedSerializer + + +class URLSafeSerializerMixin(Serializer[str]): + """Mixed in with a regular serializer it will attempt to zlib + compress the string to make it shorter if necessary. It will also + base64 encode the string so that it can safely be placed in a URL. + """ + + default_serializer: _PDataSerializer[str] = _CompactJSON + + def load_payload( + self, + payload: bytes, + *args: t.Any, + serializer: t.Any | None = None, + **kwargs: t.Any, + ) -> t.Any: + decompress = False + + if payload.startswith(b"."): + payload = payload[1:] + decompress = True + + try: + json = base64_decode(payload) + except Exception as e: + raise BadPayload( + "Could not base64 decode the payload because of an exception", + original_error=e, + ) from e + + if decompress: + try: + json = zlib.decompress(json) + except Exception as e: + raise BadPayload( + "Could not zlib decompress the payload before decoding the payload", + original_error=e, + ) from e + + return super().load_payload(json, *args, **kwargs) + + def dump_payload(self, obj: t.Any) -> bytes: + json = super().dump_payload(obj) + is_compressed = False + compressed = zlib.compress(json) + + if len(compressed) < (len(json) - 1): + json = compressed + is_compressed = True + + base64d = base64_encode(json) + + if is_compressed: + base64d = b"." + base64d + + return base64d + + +class URLSafeSerializer(URLSafeSerializerMixin, Serializer[str]): + """Works like :class:`.Serializer` but dumps and loads into a URL + safe string consisting of the upper and lowercase character of the + alphabet as well as ``'_'``, ``'-'`` and ``'.'``. + """ + + +class URLSafeTimedSerializer(URLSafeSerializerMixin, TimedSerializer[str]): + """Works like :class:`.TimedSerializer` but dumps and loads into a + URL safe string consisting of the upper and lowercase character of + the alphabet as well as ``'_'``, ``'-'`` and ``'.'``. + """ diff --git a/venv/Lib/site-packages/jinja2-3.1.6.dist-info/INSTALLER b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/Lib/site-packages/jinja2-3.1.6.dist-info/METADATA b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/METADATA new file mode 100644 index 0000000..ffef2ff --- /dev/null +++ b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/METADATA @@ -0,0 +1,84 @@ +Metadata-Version: 2.4 +Name: Jinja2 +Version: 3.1.6 +Summary: A very fast and expressive template engine. +Maintainer-email: Pallets +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Typing :: Typed +License-File: LICENSE.txt +Requires-Dist: MarkupSafe>=2.0 +Requires-Dist: Babel>=2.7 ; extra == "i18n" +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/jinja/ +Provides-Extra: i18n + +# Jinja + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +## In A Nutshell + +```jinja +{% extends "base.html" %} +{% block title %}Members{% endblock %} +{% block content %} +

+{% endblock %} +``` + +## Donate + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + +## Contributing + +See our [detailed contributing documentation][contrib] for many ways to +contribute, including reporting issues, requesting features, asking or answering +questions, and making PRs. + +[contrib]: https://palletsprojects.com/contributing/ + diff --git a/venv/Lib/site-packages/jinja2-3.1.6.dist-info/RECORD b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/RECORD new file mode 100644 index 0000000..ad1fdcf --- /dev/null +++ b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/RECORD @@ -0,0 +1,57 @@ +jinja2-3.1.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jinja2-3.1.6.dist-info/METADATA,sha256=aMVUj7Z8QTKhOJjZsx7FDGvqKr3ZFdkh8hQ1XDpkmcg,2871 +jinja2-3.1.6.dist-info/RECORD,, +jinja2-3.1.6.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82 +jinja2-3.1.6.dist-info/entry_points.txt,sha256=OL85gYU1eD8cuPlikifFngXpeBjaxl6rIJ8KkC_3r-I,58 +jinja2-3.1.6.dist-info/licenses/LICENSE.txt,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +jinja2/__init__.py,sha256=xxepO9i7DHsqkQrgBEduLtfoz2QCuT6_gbL4XSN1hbU,1928 +jinja2/__pycache__/__init__.cpython-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=_zYctNKzRqlk_murTNlzrju1FFJL7Va_Ijqqd7ii2lU,1958 +jinja2/async_utils.py,sha256=vK-PdsuorOMnWSnEkT3iUJRIkTnYgO2T6MnGxDgHI5o,2834 +jinja2/bccache.py,sha256=gh0qs9rulnXo0PhX5jTJy2UHzI8wFnQ63o_vw7nhzRg,14061 +jinja2/compiler.py,sha256=9RpCQl5X88BHllJiPsHPh295Hh0uApvwFJNQuutULeM,74131 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=CnHqCDHd-BVGvti_8ZsTolnXNhA3ECsY-6n_2pwU8Hw,6297 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=9nhrP7Ch-NbGX00wvyr4yy-uhNHq2OCc60ggGrni_fk,61513 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=5PF5eHfh8mXAIxXHHRB2xXbXohi8pE3nHSOxa66uS7E,31875 +jinja2/filters.py,sha256=PQ_Egd9n9jSgtnGQYyF4K5j2nYwhUIulhPnyimkdr-k,55212 +jinja2/idtracking.py,sha256=-ll5lIp73pML3ErUYiIJj7tdmWxcH_IlDv3yA_hiZYo,10555 +jinja2/lexer.py,sha256=LYiYio6br-Tep9nPcupWXsPEtjluw3p1mU-lNBVRUfk,29786 +jinja2/loaders.py,sha256=wIrnxjvcbqh5VwW28NSkfotiDq8qNCxIOSFbGUiSLB4,24055 +jinja2/meta.py,sha256=OTDPkaFvU2Hgvx-6akz7154F8BIWaRmvJcBFvwopHww,4397 +jinja2/nativetypes.py,sha256=7GIGALVJgdyL80oZJdQUaUfwSt5q2lSSZbXt0dNf_M4,4210 +jinja2/nodes.py,sha256=m1Duzcr6qhZI8JQ6VyJgUNinjAf5bQzijSmDnMsvUx8,34579 +jinja2/optimizer.py,sha256=rJnCRlQ7pZsEEmMhsQDgC_pKyDHxP5TPS6zVPGsgcu8,1651 +jinja2/parser.py,sha256=lLOFy3sEmHc5IaEHRiH1sQVnId2moUQzhyeJZTtdY30,40383 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=gDk-GvdriJXqgsGbHgrcKTP0Yp6zPXzhzrIpCFH3jAU,34249 +jinja2/sandbox.py,sha256=Mw2aitlY2I8la7FYhcX2YG9BtUYcLnD0Gh3d29cDWrY,15009 +jinja2/tests.py,sha256=VLsBhVFnWg-PxSBz1MhRnNWgP1ovXk3neO1FLQMeC9Q,5926 +jinja2/utils.py,sha256=rRp3o9e7ZKS4fyrWRbELyLcpuGVTFcnooaOa1qx_FIk,24129 +jinja2/visitor.py,sha256=EcnL1PIwf_4RVCOMxsRNuR8AXHbS1qfAdMOE2ngKJz4,3557 diff --git a/venv/Lib/site-packages/jinja2-3.1.6.dist-info/WHEEL b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/WHEEL new file mode 100644 index 0000000..23d2d7e --- /dev/null +++ b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.11.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/Lib/site-packages/jinja2-3.1.6.dist-info/entry_points.txt b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/entry_points.txt new file mode 100644 index 0000000..abc3eae --- /dev/null +++ b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2=jinja2.ext:babel_extract[i18n] + diff --git a/venv/Lib/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/venv/Lib/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/jinja2/__init__.py b/venv/Lib/site-packages/jinja2/__init__.py new file mode 100644 index 0000000..1a423a3 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/__init__.py @@ -0,0 +1,38 @@ +"""Jinja is a template engine written in pure Python. It provides a +non-XML syntax that supports inline expressions and an optional +sandboxed environment. +""" + +from .bccache import BytecodeCache as BytecodeCache +from .bccache import FileSystemBytecodeCache as FileSystemBytecodeCache +from .bccache import MemcachedBytecodeCache as MemcachedBytecodeCache +from .environment import Environment as Environment +from .environment import Template as Template +from .exceptions import TemplateAssertionError as TemplateAssertionError +from .exceptions import TemplateError as TemplateError +from .exceptions import TemplateNotFound as TemplateNotFound +from .exceptions import TemplateRuntimeError as TemplateRuntimeError +from .exceptions import TemplatesNotFound as TemplatesNotFound +from .exceptions import TemplateSyntaxError as TemplateSyntaxError +from .exceptions import UndefinedError as UndefinedError +from .loaders import BaseLoader as BaseLoader +from .loaders import ChoiceLoader as ChoiceLoader +from .loaders import DictLoader as DictLoader +from .loaders import FileSystemLoader as FileSystemLoader +from .loaders import FunctionLoader as FunctionLoader +from .loaders import ModuleLoader as ModuleLoader +from .loaders import PackageLoader as PackageLoader +from .loaders import PrefixLoader as PrefixLoader +from .runtime import ChainableUndefined as ChainableUndefined +from .runtime import DebugUndefined as DebugUndefined +from .runtime import make_logging_undefined as make_logging_undefined +from .runtime import StrictUndefined as StrictUndefined +from .runtime import Undefined as Undefined +from .utils import clear_caches as clear_caches +from .utils import is_undefined as is_undefined +from .utils import pass_context as pass_context +from .utils import pass_environment as pass_environment +from .utils import pass_eval_context as pass_eval_context +from .utils import select_autoescape as select_autoescape + +__version__ = "3.1.6" diff --git a/venv/Lib/site-packages/jinja2/__pycache__/__init__.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..412a4db88369044e8fcb101ad9cf24d73b82bb0d GIT binary patch literal 1586 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/bccache.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/bccache.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c793cc3cc89d0ca4022ad2f8b37e02122c69855f GIT binary patch literal 13952 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/compiler.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/compiler.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1eac1a24031a684bb8e6fe931a03feccc6b2b0a3 GIT binary patch literal 55232 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/defaults.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/defaults.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..004e19c33f898368e28d0de1d30314f25e0b0964 GIT binary patch literal 1322 zcmY*ZTW{Mo6qaOLzQ^}8X}c_I76b-5@A5PZ#W2-TIya8v*-F=P9t727JUO};B;C#` z@=y8)_Lum&z2t4cUb;T*P_bH11P?!c{BU@9c&M;kE-3K*_4mK-KWRnzQ%*L&JWPIo z&fbB!P_TkkPeCfyaLUvC6iTZK;Ix9#>@G6l#j6vxPS^V&H^r?VvKWuOQ@tO zM4gxESFzVERKa;v#RXKuMO4Qn)WBub#9OF^RkRJtJ7i~9A-lNp2%?pW$tM2dmI_pzq>sSH(4a_iYY60FMV`2khsU zTD;Rexzku_-@k#=D}x&qY+DuClV0C4M`M5)%N$!0=-t)SBNR|`Mgl?|PHZov#JAlA zTl!M*G<0DBP=4U}ge{yYiL^I|k#=|u=HsdrZEUD*4q(g}pmDiX%bVhj8Tu;gGoH{m}{zf1pQH9fGFoo20JVIVOtEe$& zVbH-#f5GfaSmJINM70g_>NWUdQ5F1j+^@e4I-I(`Pq3&Snu!hTdz`J6@Wd?EAbcp?S2Br3M;1VP9hNiy4}@{fRu5ZIX= zhc!m!zKnCYp3;&Ov`$TPXj>zr2X*ztUe^~b!y4U^f?ATdfpKPzhep?=4d6xk80t-= zj9~SLLieRyn)inB-DF6^Ihk@cF?zC`oB$%4yduG?N#A`rVNl;+z^l1(W`s@VZZLPg z`4W>$XX$a)S*%3vkssovN4}TulsyGgP)b@!O`5Ld)O0GXCatd4V_X8OWi&Nu`g^UI LU`^G5-+T8TftYg) literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/environment.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/environment.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4983b33da4784a36ae50c62ddf03a1bdb7cea0a GIT binary patch literal 53430 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/exceptions.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..618119e2fdff935855b118021e0da8a75fcf49b6 GIT binary patch literal 5521 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/filters.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/filters.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b47d155e75d55c0130dbe7aa2a8e2c02434f5a4 GIT binary patch literal 52104 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/lexer.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/lexer.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..14d4ef5d39560c125554de8c008cf3bb210899bc GIT binary patch literal 20429 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 HPTT(nS_{pK literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/meta.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/meta.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..966aab8363add00d16df416eed8948224ff1e4a8 GIT binary patch literal 3802 zcmcgv%W@mX6`dylf*|#lB_(mS6)6-F8A>_MA}Wg&MXxY%F)oQtDhQoXW4b{MG?;<9 z8+-|@Qi`hbZ%9l3L_Q%OFdHwDjdxilUf4OeXGlOXvki*V)6;$X_I;jvnJ+Ch9sK_B z&wnTHpK+YO)5rX;hL2z3(SOAtoUS8W;iYcZ6TS#ie-v~B*TFnY!%?kUv-29}^={pD zq&IA+_pn<}7e>u)({&y>q9GPuIbuQ94*l+;Xv$#N5{sZ*lFOH!ejr+6=~d7@h4s^T z&fqzV=UhMNo(I*1@2+2Vx)$GG{1t8 zob|6x<>joxUq-qW-GY4ManRA`l^Lw;-C(wg!9F%N0vU7HxGKpF4rmU4R`<6&E%X zkDoz_w*=eo{6Rwh2`pj;lEo03Z5>H{g1W_`oJp@JQi__AW}>q7*qc*?<{=&*kN!Re z;~YA#-KmRMa^Lu_W4uFW>O{V9Af30qf9j*R?7G?xicn9+QWcFTO0vX6QQ>Q8ii?v> z$T;OniYO7VVbV{e(rvE@v`qV|fy=&di-jlxk=9Z4h4b~DySFz#QTY%))0=nK;(Rpb z89cwa4Q)0*PI{X`kbiJ%Bn|J3_tYX-o+U-dJEz^2yZ`Qq#_SwR z>h6BW8WbS+j+h9SwBB#s&$AW7b`);!tSclVR@E|I>KqNUP!4o`5n95Fm};_b;Ou|z zWH~Hd5%wwK-V%gdLP}V-P>3D5<&En)Q+MbM{nx=8=f3mV#ZB+P!{Z+WQ(w5xJau&% zOr2pktZloPJHz_x#x$UF&s#Rt{QYZ>6HlNI_7gB+F@y}3$)-yMUC66EW$B| z?&=hlXZBO?;PWyb-NMlFE_vtSOYddy&Hj5Q+i^~?c3@?&A+Hv2e!bnK(9B?Bgl02} zM!A@zG;c-G^9fI@oq80BJdPq8MF?S|)Oj3NEYlQpGpAee$@8j?fxa{u ztlUCIQpZG5s5Cp2-3$k zk2T{AdB*a-_2M>H3Gbyq#U!&3arena38Xs-oZOQv>ncu*sP9RX%Dm{7NW+S9c8q zbR;{B{Yu81um+2igh>k6!46Bzie_1E*t0C($=J>yi3gU`?E0hYa9l7U$1)Ry z$bfIUZOqNUPUNnPCl=KWe+H?!v3x2KZvK}}KoM5LmlU0*rCeQIwTiG31XwZ}=gP1- z(7wqC7>X;uiHB>L7HF zSoMJiPz)0Ft1f`WJY(@hDVPFEmseMfK`h5*KcLwTa)krlOq9&IytQC``G0!uDvoI?oy~Jix8|xFn7);|gwO;JF&_Ol41YoDzIKfJ#sxC} znG$#ibTqT%`1wQS+e(WqQZU6>OQ(5n)>$Kr_XCge9OYedqAg$ z2q-uWAVPtmWFK8t79+W5jTixn-390XJ#2JY94K>^@yt3JCBk-DEZ?qb4{;oDCCE23 zhUoo_%rs(2-vpe+>BP>#MwEPPs%zY|)@Nc50G`C48%We=4$+wH{;FdX=9C;a+0*qr zlTTR%P?pnPB2(P_|AG{&7mRr|<$GvlI|DOHuM)OgFV#d+4`hmF3Ttx!lP22Sn4!$f z1<|n!EXtIS^rw|M?khJLY5iFVB-Sy<8CDvmKS^!>SVA>C3xugUP#E|jOeuFcff7sl z3{tZlo|RY)+)%PNNm2n=S4LAQB8TqC9<^zV_bJIaE5$hu7dTwAc)+tQGCY-`_4abN zVbc&%h7t`0BeOS_x@a6JSTvE)nYQj#K2oh4-a~kpBHWljwj%f#Hd0hRi*TE#6REDz zyuL=!BV-Ypl8j6Vq##5n0{S07{g{|79o=L5gp(oSB(tyLu>KH(_j376&24#Sy{6mp zTW$mWO2ha5Ra^A1+H{wLrW<_Q^a)73m)S-7Tftb#?O zs=P`O&e%SrSlamGCl8`~zkYDFVR7LyWwfD%gRZu22B z6(t6hvZ$4Bb&I&S>3sMpHQU`r^jP*cVE>}AR%L9__??AZo?4;j3}m|?3#5&Y%Fb$) dx*L57L*O)mme)e_R>OGOzjE&Ur7P|=|3BybEY|=4 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/nativetypes.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/nativetypes.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3da517de9c802f5c419eff4082a569535a6a840e GIT binary patch literal 4997 zcmb_g&2JmW72nxiQcH@YC0Vv*CoMK^^T9M#Qm0K**KzImBT<_usTI`i!o_;MGqjdj z?lLnY%OYqXiF|E<_Aj7;Ui{be(A!?yLvt(8Q-dPz@6D1lDJw?Nc7=I-Gw;pJn>X)k zlsB7o3*TS={?9}%S=L{uGy7H0xqznr2EZ-OQY&EiwNtz21P-Gy2e=!!26xj+uNqVh zUID%kEEv3+dc9gu1HQn$wBB0`77boY8@*=GGy4>V$5GTFgze(}eq}B27z1#zvM!oW2 z>a4#(hItEThLZ9f-QU5Xbv}EJbu;Zt8@8}17XT&MOJU8K4N+aREwq2E0^G(0|F(LM zX^RZy_x2|+iHFuhRydICpIQE!2%}l69jqua=ijm#)_Pdqw>m-8fA?|2&C8a;;i}=SsG) zyq@)QX&y(ZIuB&RUASZ4bbD~HcC&DaBq?$gPAamZp1?Sijm&M}NUZ$hF(BM%j`#Wr_NTHZQHEwR{yn9^)M!11Y?Xg_j%}DKvN?CVM1_Va~3$l z<+iAFtK1=o;OFwneRrP)@Kua>G>QP}h#F`Jy$FnTUK5Mm2Cs`IUlgtGk?~xQH+U2D zr6=jU)$#Zdz69#>H&B=P3aBf*h46W_IJ#*(doJP)qKhz2F8te*FzN4ZC-F8MG~+_5 z*)X8|TYKqC5p~ zj*w>n24@eIx{My=VoIA7beNm9?ZTz>B%i}V)KMbs$X96TX(%YIeonGkRcl(-E=8Axkz+y1l7{c$bDda&U9tS zw9~C3tsNh_Th`DSEezcUP_%p*Q`=tPAq^WP1RjNyFxGeFB1n?lFQ{+y`nggGLay{_ zpfO3ymm?jaARzU%D{`G?yhh*$1kMn6oxlM#9iOUcJ4tz{^K$R=U{wu(hT|bpG!QZB z_Tc5GDs66o%{dj3x~3}nHabJ6YjJCb$>z`+G3q^FD5qdh)Ql<+G)Dv$MiCNO+EWek^+1-u#NS<&VH7 z-z0EA!pCM=?v*S3OR$YaY>|jhH_hoZ1UN4YGuUMq7WFVh-q}eBZ-n6|J5f5OEQBG? zV;V;C(MjABD06thbm3gg_T=}ldErg6c$t}ACArDk3irxg_!`x7vIY<|!>i*cw2^gk z`9mW7gaEnL%u&sujpx4v0vVsW0N`0jIPMxd<=76T9vh8*UAz9Z-JBhe3S^2N*85Tq^H?Hk8562 z)MQT-ifVncU6UrVy@kysxsCi80g^OWrA!k}Gffz$ky7#k(J8?e)v}&ARB>0p@M|m~nO9WpVwKhYxF0H5KV*tNP$)if&8efBx}BS*UjjBo{9 zl~HwE?N~^`FvT-?Pi`Rpyo+2kHcD^r^{G)>quPv7x|@SHKf>#8MiKrl9&A)D#!{H~ zo4Mdf?Pak~j{@-0ONp<~9f6`3dCi~c2o%9SWAyeJc&{5#u3PkI#{0*4W0Dw+BHOk+ z0p@~b2lQ%ctsyN&%CSE^Wer|ERFR>P?9d{sF3E=rFHt2^(YUsg>7-Y_N#xH#l$2QI zdj!Z_$pF?K8^AM9k^7LiOR=$( z=ptcn6WAhfoxo%3Gnh$E?|=}ZO=Ik{w!b84wEav`#iI~azof+q2XE?`7g`M!iPU+X zD)WLXpLrGYiY(uC%a~NAP;dON18Fj7nM+MMM=F+7V#y|fO9V*f!u!BDS(KV2VKRw{ w`$n*!HHm&>XFcRxg?Fwj2F@dHTIw`_XEoUL4~b;*ciLGxz4XG;Lj61c1_M*;vj6}9 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/nodes.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/nodes.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..445d040a6ccc828893cc8e825a9f628eb9cd1883 GIT binary patch literal 40288 zcmbt-378wlbtbyeI0kcYIHV|&q9~Z6#2JZ#BubWLTB13kNQn|PqDkr+Q3f#Ro*{-9 zU|5YIIqjh$i&{(e`beBuP9h(q<2a_{9CotVWS!07Y&ORx*~E#nN$1)xalYN`x0`sE z-)?NhviAR9^#Nc2P)dgELcgx+I$phc_1>#j)s5ldTmpaJ_~y5(>t0MG{)Ry7A7l68 za`&VYiN_LlVme_PwplZ#4f!>vP5DhuTlh`ZQVZ$nw81o1EwhlF&f-30r)vWXx#^sd zaIE=3RggD^riT%esf{eGnO-9?qjuIFuyY?Wr`I~>{1~ovxYpyk3fBf)SL51f4}Kyu zy$LyOcCJCV1=qE_=1hjO=B5NMXRAGQPXg($!*lQ&m`$TqnqCGF>w5ks4?nyk5u-DsH;mxa@o6aYvZw9=< zz8dh=&Q8F$0N!YC0=((GIbED!R}`0m&AAE8-?!oCUc zO#|e0e20A};5!9=2jI8b zcLBaj;CleS&AuD(-2&eW`0e&?z`F(B1Na^GJ%H~K_?>|7wf6wtBk*3p@3i*<-Yf7V z;7R*F!1oD!AK-oV{ebTmcpu>X_5r{Lr2Y2;e!zYZ@Pp3%X#ak|2knOdKP2!0zz^Gp z03Q1wIJ)UG@>cN1TJm?;*fP?PGwC3H&hN$Lz-eKQ8bg zz)#pu0)A59M*u%%PXnG7cr)Oq?c;!tJ9i+zDZtOz?*{yCfgc52v`c_XQvP9kGbrU< zb{Uj1(?T=W{2@S(;JOwgffSEBUg78fO;v0gG<5_yI`%AboRu7p0-m!^06yUyMGcMt zuG;f}=LLQY@JYJ{xF+cz2fSd{0oMh70&v4#1iUEllYmdz3a}FRDZsAX1l$yO8t{^R z8t`d>p9Xx!J`4D)wD>sSW!nSnIr~wcX8^y)UIDx!@Vfz@v!4b0tiVOU-(x=q_&I?~ zfZuCB5BPb3%Ye_@F93c);2FUG%6=c<_X%79{G$C5;Fkop0l(k=0N@V@>;V4P_6Grf z&^dtm%mV(9{b9f#7I+TuN9^wf{JjF70Q`OSM*)9S+E)eq{q_$4{sDpK0e{SX8Su*j zp9K7I`v(F4pujc2KV<(f;2#!v0q`sKCjfs!;5y(>+MfdaDS;b+KW$$Cd_mwvz!&Y$ z0RD`Oh*N-n#QrSc&kC#n|ET>rz@HP?1^i?7=K+6S;3nWp_7?zuLEt68KW={!@E4sW zwEwjI6QnEqCnels{}d?er|mC+vd%bXoipbR=a%B~(`Rh+Xky~a-lr#z9?RDow&UjK zoVugRO~=m9Ea#giocv;0xsJ-uHk5#s#=>H?=BPq`vgS7Oi>h(DiuZDEW5JOu@=fJ9 z`4diUk#8;4E6r-7?&g==Rautnc0SB5U#;gG%63$)*+4dGppzk7ll4YZtwB+06oEfn zTq?)$6%)_GKi>1&hs(_(SV^oHrxQmLmyD@PiA$z$lzn4HU4Zgw%s?HpDt4-&Y#d76jdCqZ<&sXd7xp^1_T= z-g67Gyv(Ar3AGtNZ=hPn(S%wb)~Wwk@ZdIlyBA@-<)esTXRi9 zHQd$Yu3Gm~KoovPij{@M77XF#wm*WQfSOg&y7aI0b zjrT*v;;E%_Er`h!i*}<@EXJLp?*swcQNdlTsf8e$$Qp`ID!AJIi1)p?+>Hp@8)PS6 zNI(d)FwjzdmLzy=dC~DcH<@2>n&nCjbSr~PdK678H}fYjGHMu$9DcJ^2TkQrDL0#{ zIn9vlZI;I$t+vc6 zoGD`f+Ib8>7F;<|t=WYN+a3Nv)J$Y>330!S8{4=)@yzaX)?DITY9)0c(KJ>}wPwY% zA?VeOrnzF-$@9sJhLK?GO7cRIPcE8#(zI60^AJZ*8t(W*5Jt}%&l`A#-=@_>Zs(2j z#!1t?3vp?QOY_+Za=0gP%0ylnJ7Z@rn#d`8VSvMI1y3&$&jV8vrk@tfxqdFfo2y87 zx^vkqFTb!mpRH9ZO+Qjsr=7D+ z2aF{%hn__5`02(B2eO~U13y`F>VDG2U8=c+Li`-=ZD+QOLVc?VBJe?ab>~d6IFa^~ z3l-P5NRr@BW!G_i100LFPG{U^Lt*f@5U9}R-Dtx*aJgd$vW8`BHO7qf#uj757&1l; zZ!{#fLQu@qL`vO=v}y-~aRw6%m{CjS@$kPH5qIHo+4I?iWei!0&sNbtsbViK_XL7g z`X4au5j$yHb_x?w%1J{YO4}wTBlO)1sp*WJ#`CPha6k(|R5dV7EH_;Pb<-tbHcJmG%ErG5Z^?b96NvT>VhmZ#tDf^ayP4J6wFuD0k zxt^bK_@rDp;n*@ZFb1kk&Pme4E@!ZUL`?aE;LG9kqKFrNxalk`)-Y!Zukq8$X)dXH zC5gE3kN3Rx;c{<70BTJXIbMu;i~@`qxv~6^@+nkN0GF(ipzIl9*3iFfO8+GM!-Xp#&Sl1-#a(G!~aH8R`Ji zb=Jdj%cq^k5h3#y${uYkn~{DJUAk%}i!jOQDJ&d3hSe}v400FZOjjnU9z-fXRjD~; zjL@Y;8$C5)>Iyy5rB>*_(~fFB#d?uV*ymF8JB33wI0qR!%wU%_O%lu%>DmfQ_DVKNX73!0lxoQG7ff@{K@@(yCs ztv6&ukp*&;<*P14LKidgPMIcHH2A2?S?e@LQ9T++`IdoFm`G~c9Xhg9=cwGTR6|Ma z(G^diTIxv#F(QOeN5Myn9M8p|^mikXJAxo<*h#TUWNl0+aq94BLhPNL-tMf8m7m!#>s3-fc1@bz=MGK4D~=b%$mSTl7=l~49s!u zo5{u2Z?3b~8m7mLzn`;Y=KTK*kC>6z0u%3+Ci-8 ztD)6q&`#!bjlBg=Ho_{Abq4LN&L(-Dh8-l2@;BQ%#r|PUUt?d7yn!8O6W-cx-+;Ha zIM>c^o!*LPnOW1m(cXb)*9FkHJpm|h4}k!0QCak7`70pb`!FNMq7v9&{vpc2AWrBW z;OfU941xG~$ z#NC;%Ro$k$Q|f@$m(@%af{t3wb2=3)1@98engc|lB3jr~OBIMI`LYX1d~T^$R+1CM z_^M`u$Z>8-@U^pb_C@-W&hi=PesKcSb3f){X%Ey0xqNoi#&KQW}J;ratq%k;E#2Vm(i^)}w!$mpb3 zUGy`c3rdcPJ?AH(;$rT_r084F(bYs+3l;2YNuhR2G7!GHm(SW~HxUWmi|6i52+{^6 z_%S053Eng|Lguhu$9!)xTyKqFy4!$wT(5h>Q=sffH1jjg*-C8*0)W-1*OuGFPBwf1 z7pF#d9>IHscF;eCbLd6efFLjebq&AnR)Gz*0exwlHURUvp>_c_PbbtaFSn9}z>2;; zZ_b*JCyc}siBp!9XqwBn8-R?&Q-IIlvPFC-Ph;9Wb^@abitdR9GwRTm)bODzD2B`c^IY+%~L#bHktP$G^?QC8px(Adx}7_XR9 z&=oq5C1DowR`9$;7NTE3#qR_AjdF5Nh)n}b88ikIAv-FKxSI`Z0MrcwErbY}f=DHl zMs>m;nG6*gAz`QrB5=_>ZX1_;ziUnpUVx7nxh~<(_9Zl5`yRJ z=5yA!v^rU6d8Lii$fQ!Zgw&4UhNN~uwDOB&7S{#L!dL;DId7`1D;8M@*o3i?YJ#NH zmX#EV>eSdNur{RPvl~||d6v;6<^AQsRWxK*6;y6*t_V0rz^=)Xfo37WkoF>cchX=? za?;Ow(hC^>GFVAyi_GT_>d8P-#M#Y4@(@gN`Y6j}D%8mC*K-53Ng*-Lvyx#*oqC?o zeQK#HaOdZ$pdGTi3Whab5K$;(Fj24i1{5qbZNWF{ot-->JtbD0qEz4#G7frp{Xh=v zCI@C?K4*S6m*lVYs&|zzmd^M50lr?OIMk+DB#|Gleav?=0^Q#iX%kAFgEniTH;+Qx zrY_-C4^%OziH+H+0s@#P(Ob^XRblEBX$zeNP|4NYb3%R3_fBP!D28-WFRb}e$>oe7+*RuWij!;V=G9+BNiC3=?w@j7_+L;U*eoh185@{F!9F3EfQ2rHVvD>)?z_;zJ{qXM;6k=VJxi-8JXS{@1?!iqHTP~Utx>jjMA|UKSD8*)a}x<2hWrIspg@`-3kqk!U@b)0 zLA1QWd51D5)P*@&xrpl4g!sT2fh}s0*E!pip@cNRnF8fF<|BfCa0@E_egICd-opnSy_YT)om0i)HxVV%0OW6Dqv;Jw^>tjEQpW0% zo|AlmBA9JlNW7Ssg%b`|e6lYk<}nYRhb{6Ps`?V{=95&hRt$uxPg-DN>8V%Eo%vOn zW{?*Z5cfs|oL!z7IR`T_UIRv6qWR7^pM=ho6u}5GI{D$N#=Te`RjdcBuV&#kbv14k z$Cz%Yn9~OCA!CD8V$Zh`;S$^CbK9{F5;Rgdi>g>G+*517!tCxTM9E(@#$PoiCM@5Y zZ&d3Ys!@PdERCo+w@6#3pjOJ3Vqpx{Ie#lGbu$#1NR82PZFm547g`YHQgXFH2lGHA zLa|T*$*@Abn0V2E1qsUwFATjuF_(D10sH{f^liFey#(Y7n)PU%L^f@Ou=jYa!{vSr z0ST^&$=e-y3D)>#l39heWIjP9LS;GuWm>OmCFaxac8L|3N-P!Md8}DMB)A(=FQ+&| z-6?4-=GV+ZiGmD#E{PJ8%>jrD&q!=aVlz;%`0g5c7Zjt8#P6bk78`i#0eP0@8>c4l z6z|9#>lxK#A~WTs^?+B;qlM}`gJ%)EYCQd_@ytX{Pye*D`DvPzxJ=Fti8Wv8ZGXUl zSxwY8Au@FmZ-9r85BkZfeb!Hj@av~UELID64r@oMXfG`+`pKE4*_aH~_agE&TrSPg zTzB9|OG6x`{%he^|KkYc-H?$pM+|SH5GnJE2~@IIGzz0GR66|BV_ZBJX<2b)0Uj<0 z;&Jd1f{g2ICOMv=b=*eSG%)3}P-Qcf^dekHxuD z2yzKn9Y$$;$f6fELnnmM{k0L({O(B7d~Lmz`;NJ>^+^^_wyre?wsEBbs}y5{8%J`3 z>j%>55p&Ff_r*Z8>|H_jZGRm0dvRT+Q$lF456$&<5`!5Wh~`)CS{j7)q%OiP=C#-! zTR1j#sry1F4Lx0({Aol`q`XWGfQv0zQ}J2PDymN*7S%~>L$0@z)JZ)BRzuNQxOiC3 zyU>}cv(-u{qCp<4Eu((w0$%T0lg}U`kD929OhIT>9g=vs7uV(O2tqV1n31^3H655EC_Tl7XEGCcS>#h{9kLb_TY>}c zAc?+YkQ(h}^*K~@>e9d!hKzM6RCLwLc%dtLK8uJO*->O!q)p2jGZdfpM3dVaO?DD! z%IKvZP8ELo(WU0%Qqy~RQWKY!{AF1=@)c+?>*vp$s6tbvbpU9p!&Zdh3PBsTLe&_KR19>*bHM&{wdOjkOhtO`0AI4Gn* zC}}&@24QUwaab{wxD22i(qNU)9sn`r{Nw?+ZJb4&j-gk{rfQAGB8FETV zQj!@|WuYV)%tFCJqtItr%Uk#g)@h(~mYkaF6ieU`Vz`RlV}nqTWF~1!`56d~CsCj_ z;_y8^iUK1JY^<1n$SS~Auuu$!(_EHZ3wjL;_=0r^&SVN>NY{?ZqGhzuRYSC1Sczf# zfjPNZ+Nl-eGw>73w_1ZZpfz%pO@bmD@>9|tD7$#mAAs#}35z<=R=>;?Q8M*21b>#n zzd@pyn@s7i5bQR?ehCrp1Yu|$kz6dBg+4tZ|3J2k?@hK3BDN3N z#^f3hFPDb95wD#AnZl><;H-Bix=iCzh*B*%Af||HuvQAsOq$W9PXmf5z<3}C3ej4f zEw-SIPu0I=KvYgm^x^*95ZDcEKZlIoMYN6JMwq{;k%lRNasLvO*;Xr(Y&a?bWgOE) zCs+JiM|SFwaz!<~Ux5!FZ}hr_taEa>(NG@^ZUUhpP7(vLRFqlpoV4+Ol8ue1aaNb_G8()X2|BSJd4Td)2+5p0mIMjYdM_YG(3ZcK{4F2H-1Gc5dPvYb=x zH1MDC2M-AE*FCUlijpQL89gJwJgPm(3$Ql`$AN<w^vaSc2-kBLbfNXSC8%S$-tw!f4=1EQ+NMlUEDoSpMC)RXWo?@LGFiwDjm z<#Kt6l&fdnLLn@vY{Lh5_wM|z!rKdX2>rsJFcTP|{j^>e(+ol+wqFBo)PG>WPU*|$ zV_qQS*OMW96&bCtlZJ33osn5;nigPg|=e$T!jUC0`xQkMn_)fNp z|7}{57I8X}R)NR%oS^NF!fl1y1Z{cz3D)VDjx|cV6s!-jrv%I45Z7z7nx&f_#(0`zJj-#xxZVh-I=vdj8U$8Zb`!cIYSY6{4;IyPJ$h8Y(akyX9GEC76sw z5h1O~oF4gugZUoA!)RX;eBkOXox4R>lboWoRF$v^$%RKDeGs6iLH%!F^-!FIL_*N( zb~c=}^EaU;H_;M6wjdd~OEYdoRTtsCfa!b&TYN%RO*4X;Ez91HNw~D1WfLJkoGvTz zf6^X&Vbh>d0|7E2*o`G9eP|~p5AYJ0ck<)fNWrEC>zp*%sHQuCifHo$G75A|c)Q^J z1(|q5Lxi4pDwvPOk4B51p|FBqQ5cON#JV!dE z^a$M&xtIy#XXyR{S+`uPazlhjyJyg=4rVQK*N`!!q&xMvkB7qVfhy(M6g<3cB(Q_Q zID-iWI~jBcLLcQb85tkrUC-h1yNJEiHbo6)jcw$Zj9E2Ql6bfm*X8a8l+fZ3+UjWA z5i{rHWGoh-vuIo71T56pt{GEPx2xC*pkQHPS8G>?0zB_&qnDsBKg=?fV56Ii{`-ji z3faL=GZ~?tZIpGZe(XRG`atGOPs)n7w@v?YxU?$#7)`KDLPmC)l>5HS(xpAp&k@+S zWzr~VUbmKg4OxDboIaKah0pVO?>16v>et2&%|mOD*i{c%Fbi3Cz#r7@)#ApMsPWo_ zLbeq*JE@!fD=4x>?vl3uDxXCWQ~gx3n9#*E@p-=46Dfa$*xzm=Z&9=%^fP7^TDXd~ z7ni#SL5P;fZl4o3c@g`u3n(?8g(EmrYq*II0M20n5626@>3dKt{DU%+ds|m~e8+Ym z7Z!9stM>);+Vg9IdUOSG(M ztO~?Z?XJR|Q4UB#{!$9j-{R9K2xt*)nH^T8x1$Fx1siU@j{6qPjO$HUICd%4xy?Tz z$zjG%qzAoWGQmO^)3$KSt_{0acIX5?Rd?^xu28euB`53!8BM}v#4Qs&MPE*Cas&d2 zrNRMSqldcGEr$*&mgVig4QmJQur*S=#Yw%+$1$Q8`~C~>WQzII_L7B9 z5j8Rv{*dL8J8@mgGPjsx!}7fMFq}f-a2U3(3k8Z0?b~7+)GCmjp3Y)h9e5PPvq9|q zfQU9fgh7feco?L^qP-6Lnft1Bw%WU4_0YtAY3>w=M-UuLOgJvW>&Q>3>fDK@`ZG4? z&k?{k2+NmcI4)rsOvqspPupBUEIeR?_u+Eu2)Hs&w>mmP%wumH+{45T9rqTU#hNLO zZ|$7EDRIv9;M9y1nG0F&2E*NezB_L|f%SPS(abI1Wx(CcNO;(5dLFCi&%>eMT=LoE z8RJagOfo2&iuA6f0T=V@Oyew^N&W)WnI04Qtl}(U?CH(ou28vJi&))M>0D{=!2YvL ziDl_afoBa{?HjYcv1&^v-@45H-iRAG*IDN4BW4=g@=Sx>YiH7`v$4F{Ih}%~a;?gQQRDXcdwamb-dt)?bBjuZ>xq()i zJ8uM(HbCe2{*;!BDNX$)Yb!Liww1<~a2`QGV(X&z2#Ng_!T-vjqsJIK2xi)al*VFG z8jH}`PE6`=+Ow3*g%ZAjh$QnJ!A(pFLI;dnMF&Yd+>7h-oe28SLBN(E6L4D?B?JqM zNyH^Fa+U`{e}|xPLE~7=m$lx8*;!VDAUK8|!CZHzMxd1hDj6mCBx1o#TfxNo6->LF zXoJVI4Q~17)SyjVEgQc|Qi5#xqPJyBL7WI#luVA?BUYk!C{pqV6AT3Q_GoecXFScz zLB3*7)+T-3CW;Z!P6+HW3eM1I**hTRK^~?Usy9x?cfw79spyaQoK*MWl6eID5DrzR z#c^zk=SN{t>;N3T;A)OtTnOn<4Tm26=$JgBOafg(is>>>D$ZczC`BE@4SXDl|9E=r zyY?>}NqAHLx$rB<`5F@55N?F;>>yz2)OyL z0$r3+Hv7lL=^9QzK6Ry}w7q&NdW?Sqdm%%qkSHR(o-U&MKKhvMOBju5Ra?HT{@kwYtFc z-OBz4M7#rKYsVBqtIFe?y%$&8DFCek2#x<@_#e@l02kI|`)nF~UY`g65oEv`uyCjs zyb54a$MQOCM?**laRgi;F-u`(_yufC!<<0pmQgz<2DEn)i!zbuu19iU(P{PZ z4z>_P5}=qnT$qh&dMJlEz*&Ji5ilN0r8{?(WYN7;+BVy0Yy-e`AgpNzyhN-ZASvyV zy#P)jqm+iEnsYYbDp7v4dr&%WpmvYKCn%~kv@#=-NoCcF7dTCIZh|^}qS{A2Wza zLuLccG7sZbEw&rph;&6fmmKFi??m8T-7JJV;-B6hAEEF}#X@ZOM@^C!$C5jaK#rtK zEEwl3Y^H!O$a65_Wo+R4<@}TLE}f5e;DCxGbIq=z7^O(5@XqpKH?%p)6|QPxk~1lKqJAeHn%~ zM3LPr?y>4=G#6_v+0gW;{tq6ASPB2FWTocnK8!G`PKF(DCmqA=V zxf7!KCL$(Tvu(K13p_bvi)kiJ3$BFUvEW>og>xaG@0h94e^N}pirGX)hr1EmTdaWi z2!TSc;gi+Gj7FG8&L0Xrm3Pf#Ob4NB5{5MVy08!dcQ*QBsc)gge#0nK(kz)x}XtShkH5H$)>OH!z?#1Pn5OnLk)^%bm z8tC&G^jQm9OXZ0PJX>Md&dR|tp`tJX6=DqpNSwz20dmv^T0ztwLowbj(!q%CO|Xyi zKrKppG@QzgPrbKBtOM|YqI?#4{Do`unpbeeHnQAvu~vKq_%!s#qV!V=q|U$t6sDL! z#FO1v!gRo+WzyiQ6d1)W5lED>1C&z<2~Sc(p-y3*UU0R*_bM7BcP)fiXw#z66_FGD zk8hmCgRkRG(~FG#_X2zm@)g64`P{~Y_ZD$qizqYp?vAz}z|cR< zT;9q+8XiwYoU_ECTS{IIbIxH4wKvk9bA|Fy_{XM2<8&(kPIvISACSgjACfzSZ3Mu{ zso+qusRPF#5lZQQisZPbUMrsNEyF?D$oO+8K%{N%w8Z|(*I_jp)%A(xVr@2B+gop! zu_w0L4h=pYkEHB3Y)@k125S~6Zz)<8ZiH*L43Smil!CxsTrL}XrEw~}HU*E!TqxFI ze8Qp>@!TQJbHzmt%bnPwDC<9g{22*HrP9;4@67MonZM(iQVAp^NN3PO>#Ineoyp5( zXU3%7M(Eg9(TmJEovg!kc>{tuP{T8sn9RS=JjG2$S0PHh#^AdQg!JSYd)?37!^Uv- z-t$4A_%g1<29Wn5Jncr^QEHIVH1m^Y_M+?$u-7Om<#0fNH{6z~CZpjuM@BIPJR$YL@iX3oM+{z1H_fKrr%r6}d!geF}-YlS}L}GmwHzk108roXOC0Bp6-+OXtc! zYI;bR_@Hmyk29q_hjhc!r9eDbB`w4CKN8a4b2~g&KsUSdcb+Si0?A*vX0!qs`LPOD z4O#Z#auv>4Vl&t5RwcnLe2dSeWBQREyM?5u{s}=p9;&{>RGfNyl93{VdT}y90)&Tl8=yt z*m5Bu9B5%CEjh|+)ExP)Vv63xbQrI7Bc^tHc0f*{te1h*m&1iqhPsoJ!Wq~JZ_6al zp9#*DfQhB5k2)nb-2%2SB@wFyZ_Ec*)}TlV`*4Y9+amg5_nO`bj%g;T?`B)}Ez0

YG6KNRg(}|%Km>eM+Od)X5BsV}f)W#2RKsjWUK|k}bJ~_zS)4dbXb?pZW1v)dH zQ$a3=g1s7K*S@*_-Gb({_b&x0Y{0uke&z^pB5H{D~{d$CUrNKn7TJim^hO`s!XQQjbOT_$N0 z;~=N=;D|tr;r8DaEd~c0fyDyFhI*TaBc^a#09kH|>I!$uK9Q)8Vg)cQD}b;nVFfUQ z6+lZ?0Ig>&t^neVtmN=CRuYpuU3Na(ngi`l9ORM@jya-rs%t($x{1_ky($zq72dsq zXsJ?2@&`bzO5?_R3%&KRD6JRGTlA$!qFklvkg42(>x~K^Ut?if0O?g&B!J|!_xS*- z=BH2M5MkL$u#9DM-05QlgzL#l6)D)y*@)FC)uVV2Y$VpI;xDk8W5mf{^G@h1Qu6%a zw(9&I$&FdAheVSzO>8R<7fEwT6M}Nq8hH&wh(*(EQeA~id-Jeokxl#j*5+^>yMP*CxZ-eU$$33;E(Wpkv_2%y zEj&LNKA*Rk-pk^=vQ#y(lK!mm;cyZ5s^#+-q!2PONKx)eMh1K4*$f9e2IurTIaVEG zYPnu1ij!r%dEl6w7d^3|!yJdB#H%h&&63RrG;RAC4Y)Yy8H~+UyV!HmeH`S|SuwW4 z9PMW?iP7)J&mspYOm@@z8F=GkS3uKW2N%AE!d?^X{tnmugl_#blE7(GcRt6{^pN(z z(K#xPF6;yqn*`bEbOz^b6c@@kp^h0%XX*dSA=yGw@c@gtIJ5qH4y(Ix0|O$g#`HhP znnMW30A+DD7>}S$Cr5BT7`Ntg3%`k{;n4FouVV8Zlh0_Us!BttcHw@RcOuBdwhvOP z);J1`HgAvO!CvD?HddXT0FW9;!d{8Eol)D649QV79_%R{Y4QaabJsqWPOX)@e#OkX_2IZLc5Dcu|yrqNPCcHOo;e4gf6$@)M{bb zGP?4lTB!v|VI6CsS+Zq}WX4S)kP2ej{_sv>FRpL}*P`T9z7>x{N+QRRmFzc6N7z{I zHuV`|?DrNbdP79i%|8&kUW72%*`-n_nSjdN6QE1&k0^v$Ys}#=1RA^pmMQjk;hKd! z+t#uiC(e9K8Xjzp{mA6c!M5bmUg2l~M@3mqvl6ed;p8c7zJ~og*hqJv*V*^NOr(S2 zKziwVAbER8D{>^nfa0@NeS|d9NABaeY3CNU$z!GiGX-@Wt^I_X%JsHVZjqr>pyNX6 zK|_(lu*k{M@f^??d+psF?kN?mmSP}BA((x5;BHAvTj8ct=`5sJtsswwFNgdiP(lOS z81|TR@RumSQ4-T9VMdR_QsQmi4i>YqjZJeJ`qIMV0t`s|#-O&sWr9UmiC~8#_woYh z1Rk8B#s_WgCMmEq!Cb8wox*66-`B&^+Ww>s?(K|{&S)ABunc_q*D(CI(6m& zr%8jTw-+}gqj)z9@X8qd*^o8@lTOF7IXq`%u3$3m%MuRkpo)4;svYdb@Ht*0LG(BP zVfZ|>IAZvrx2HD>L6qmcVRiIEI`HMp=(~%dFK2f776^9~{VYxxE2ao{=CdY+I~?Kz zKU6HPVG4ed73F-P1v)LdVP^qb6U$Xf+Cy>bQgC+U*ZIm{Gl+XvI+oMPL3XSN7+i2Q zO-M-Vjr8jyNv(ExqXE9#=a)-;jP)^r#bQuwZY7iHZLHtUnSnKmbZ{}?WGU(}?w?I) zfq4$+)XTO>G2UF6&`4We{ml@X_{<3xE4!6y^%kC&Biw3cnX?3!LpzBvBH&=a2V^PE zTGHi0mJ#@d$dXv@Lzctc$g+hXZ|Y5u>IQT}cVg5hYe{WMS^CUq5~3O>f9XPeWp?!# zQc#v5@okQUa1tI-(@uD5qCKmaOQFt*C)!9y%r`&=(Y&@Q4g6m5SVaj>ZW$B z7u#`c99-6$IBLj-$qIMSt+^&Dih`V72U8Z#Ne8{*&^YZx9Qu?DdTG662{N!kA)Qj|?6Bk!5Ck#dDv2*P*1u@EPdhC9#_|mN#XQ5py zc#jAuwlqm<5V0&M)=GK%q(q0F> z$kAOo7bUWzHZP+#T35WC14OgFgcL7~b5_>gj=_SspWfG~SISNAN&V~8!wsi;fgGY*Y~03~Tl%AWI00uC z3!t=&;8IH$u@L5Xc*r_l zHMr?eF|=1|i=$KvJ%am|usxv^FR=?9c4`uja@$YcE*%fgHo*=d<^tA+y$yGwwSFc@ zquvUr7ptX@K_owV09p*0(K4$AiSS&~yAH&fgjdf-m@2Js2^MG06M}|CQz3R0!MN$} z;K~5~eO6NElBd=_0XZ_T@85&T9Cr7>zMmH9GW~2?b_(5x;$d|YKFAhm2E*E`ZHV&C z#-do}MC`)BLtI z7#h00(Vz#TfpJCZjcGQfSJRhQbVY8X;T!$M0<@{y&QH;#ZG7_0_15N?RmBZ-z5*%+sHASx1cuTv}kf`xXg z*4t6&&#+n-nS#)jt95_%w0AuMeza9|IjK|dt=HBX4YEdRH!|&7_B#;q3sKpGu2^>V z0DLbMD=>5A3P|4uGn-UYc#7WDu8&zj$8(DmzKbZIs#UOZ!$UrFmsF(3KJ~|_PW&?J zujL&Yhb_ftS8PRc(zdxgGInG6si4M?xX%zeh|DgtxQpx* zLT{irXU@C!5gfMCQCPmtD&V}!EAohkP|mlbQVCtT)WZ$eySrPd+LDV8XpGnCeorH> zd?h2sf;*wEz%w646<&`jL}*oo5Ua8NpK*g-GwWM68cl{DNb){%2=+jcZzI!D{Hf%Y zuM~6`7qqo{b_t)Y3JeKY$#v@bcs8+H@o*YyUOdewUzQ&^)JL|2;Z$I~qo}2Xz}57B zvR`*2n7Y*G#Hv_M=|+=>fR8M6qi9jdvTzy=pZ3Jkm1MAJLoPU8+8^5=wl>D(xAc}P zEl9-3Jct$is?rd z52TCqk+~2ZpaA2WgkZ~%ZrCNX9U>zUBzAM7((cl;vhKgDfVviE-?GW1$& zV*(?iiBB{|b=GVBj{?`a7u6%Y?bb_Eh*-;BigZ0J1)!h!yk|eDfSQMR<_M30;{KBI zjd%qMy3q+BvTG0`kva$`WRxrXoFUY>0~nXi*)n%5=1a1T zEi7()M`f;B(4QLK!OyShjC5a>^mDx!6uK?MLke$dnE~cHkOj3I*~*9lb&D_-v3lLW z83_wjPxn{YPZg-8_@*&-+{jnSkqnK%7ozG8(qX9S?YeBK*gWLR71+QRoRSrEmVwR^ zv=A%mT|nGa?=Cuw=&fx7DUA-|)1Dn9I~7AAqSXeZm2r3p=-uo%j#Pw)p@WcH6+R6t z9ZBV4Qp>V@ruTsJ04&ga*nFVssm2nwn(}-Z-RDez!^GH&deszPD#vGxYVdT###Fu? zY=h)N>p>rVnY$mwKUtOd&uokJu`rjFyi~#KPDEh`5u) zM60U)Ua@yoYwt&&afIS3ZRmHJdprX$->wYi2%0_7$zVoI#pkQ&Hxz>1yOCWV`fcAr zN&CFEUJeFZD6@~0)ybpS$m;Fpo%EFcLap6mx1?8!oW3{CIPRS0zG}X(v{;%T8pVRb zpq~tg#%b|+Gj3wj55G(~ANXH$kB>Hb1Y&cN=!)$#y~O58dgd`Rl_b z9-N(wz9R3v_n+N?k6*FIeCod{wo;PF7Mo{U&JhMk%p;F(XUjW{3(Uz~-BFWPyL(Q4IxWTIDwV`vW3LQGkc_Q9Yz=GwF9_a!|@sy7f~pK>!Hz#Q&bSy_J?O$kiH%_SQmp$Q5+Ye!!yc{fVZ|ve>g^8e^NVS z=Mcc?>c~mH{fq?JTifl62As)uhQ}Y2O8QHC5H)ami%brBw|#Van@~$wY$zOt0v)VD z{LFkIs5ee-n3;!zWk7uo;sg*a)oEErW;ZQLXo)7wHkzEYJOz0}N(}`JnXBOrgB=E? z-NA`5^nsd`1H;BSy^rre1ACfdjj4 zsX3>enq)G0_#k>k4w$Qyv8alX4C$}s5EU(`NN=+WSbAkakk7#15ht1GkA8-}IU>15 z2is^VZA#q$TI)KgJqdC=P9Av^QxLi$!Crm8qrh$KPKpez63&2Ab4(3 z@53i=zg_a_gRrN7qI!fdLRUl>_u9aEy5w;EK;NOCr6zp}!rq6(nW!K_S1c%XAB4M; z-mv_Z#|@H9E{8FFIBlqlt*h9)Q^&eK8K*s zG4aPy%J-nmSeZ6)_9dV8EZg0QEPGGRn1=Nspe@T9{?MaXx2=_zrMl%GL-wdKznqC@ zy1HU10T&I-43TinF<8TBa=-w1Hdv@RNgIVagr%K;-4xkTZ-_i1FhU{ks)oG;_X+A3 zHGIDs*_72x6{ckz;}>!_99e+lj3u`+NOdNx5>6OF@!TPaGwt{SB>xsjhDE?`^N<4czM2Q~s~Gn_T__arRXk~P?C2VcD7R}rR|xBy&|CUA*66P9`Kui>`R7T6 zSq8B%5vvW)AoKT;{vtEMaF(B3;PajosD1?5f%@0&$8^9k47!2QUaTtR9Nr87J;M?4 z^c)E0Xwm%3IO~YC1nox1C;lcmO#aNr)-zW*vc*K+p|QuOAdB6}X<;`_DZ8;c%uHQy zHtvPj=neZ6DfL04@oADiSCYX7F3b#4VW6UNd z(6{VuoaB-T-cm(i<_&(fK-@fKrLbG4y&D<*P#e?8qRaU7ie0uzIDp-yao%tC>@|^z z6vsDCl7cqHL&_C6#1jpLBl21&DQR;!QFSRqA7v%tl?dq37{7)KcB&^(e>HQM^P| ze=C?@tm;>AEE3ok>nfkscGWRS7iZrxASvDZL&e#0mA1no3?5%%-B?7{P-VX8>9fSI zVGX&2f)7qDFCwx28s%UGrwRt&Em3@JI=3MDF7;qhF8mbs`rv4%6yF^__SE71#eEO% z-}msrsRyRl>a2<}|MyLFG5uEMYm|P5UxIwRtmI>meiG-^;7c8#bGv#PUqI(#EANt6 zJws%}#>$k90GwQ(Q<`C=X;Z*PTX0LBho!X%lDKcd(p2QjuAjuBfm+96w=lSt0UZ&1 z6N|6c$qioJ#>e8WxSe-zV{kJASxXXqSk|zF>wTILpJDJ>247(ClMKGZ;O7|pJcD0k z@XHLo%HY=-{3e6nVeq>QexJeD82k|fVYEK)zQKT}2FmGp>K}ObO$PtS;A0Fl8zvrP zY;&8c{+{MlyyNB|^{Y&GJ@0;rcl4rF^oJC;FmcimPY3Y|kTrP4#c)|mR$SOsTwYaN z_*7hplm#+bGm@1Y#f261Faz4^#SkpUMzPq4JwP-xk$M#+IYlW;Q4SJ;MCJtHGJ2Sh zD*Yk2F*d7};4m&)cUq`Anr8g$J)nxEn!_3<-2aXsoyg+jOEgIjVZ&D%&LuCXt&z1`t{xjqe9!2v(KP-I;9q8RY&17IjMyx~ z5qUO-r&-)xJ-TUh1DFG!p6%7&&8aGTT{uvAszX zO;4%B3I2u_ap{TwXRdJRjVqN3yywX#WLAFbu|3b<_uluO9u6}C&!2z&UD60sr%PosS0E)heq`jm)OnVf|AKm-rS=o0^(60*H7nV!39Rvk;joT;Q`PP0-g z>$q}kqHDqAYhFrbHLP*RD#z@;Uci^Tl1;dE z=vp&*!0QEfa-3y%WXTs+E?qF8;a;U3UhFtHy#+GCZPXfxvuFn9=XcUJ1z zysE+XKKGy}6=3rK^upa@hmw_Cp;MvH{GD?3jw~F>4uR#EeXb3#l&{{Z0bG74z+m%ggW5ewiN!b%?>@=eSRPrXAqo;G!mkR=;fGUkgcv49({Kd0>hb= zKvyt(1e_m3@TJT=eCxCU*BIX=f3v8i9cM2=9c1ezB#w+|8!IiOg~_EYm~0!aEJjHh zSX$Xs2bIxk1~}RX54J1ve96S%qD_o+3!};a-JU)n{G%E-VAyxTIMUD)`Xq4lS>VEH z)CAAshCHXwQAPCl2V{q7laC$SuYff~9TN}v1rD0cr9o7XHU^qT;5Caj51S&H0wHk|5CexnidmBQ{#*_3XGNB?9 zD^CYG%p>3eQ68E;xbfUxU2|iMhgQ7)?teM{-r$v67gC97#o<$l{imS~DWJVz;n?TTa zh=O1~hi|88L#BNZ{2YD@j3NSOv!jnF!QVsR`hXmII|{dqub7}sprWPH9r2YmwhK^a zJKU2>nyM_klG+UPNhP@8vdN_yzn$R^G!7qKi z8V58Ag4a=aeC<317qG)^SODftG+q#+43nt%Ocq6(6$SKeXeBTo6vdAVUUxg@E&KqB z$_&t;SlV!19eG6U@Dyt)>w1Y7%gWq9gGu!Q#W#ylNI@n+7}4P0vk_eV$DbJu+g?!! zT>^x}9bt3tZtJdZdp-x+7?eyq+YKeK>8}`t-DE?6z*&(>Rm#G(L74JI-lR zD|T&4BKv*+otb?QlzUD|?d9A%_cj0h-}k>)t>NKZ0)M{q7eA@}i_a$#-)5%wPa2s= z@biCwS&d{l8UAZ2TR9DT$tgR@1fElk)RJ3v<=Mq^x}28hbR)BrEobF9 zgXdg1mrQt8eMmnjJ>|>8Xq{~omPX1We6wCuInSwY!EbcKE^n2RA>_vJy9OoW-nNB= z%KvKeT(Z1fo`>c6TD;$Z-*x!y#P53iZouzGRrs7+E@8}@yj@6dM%gXbCg!bc6W*w-?|ZskYc&sP-O_lo zHF|UOY_nZG6BQTG`<$*F_bM_6R_rgScxRT`e&|s1R86;asWC29|d+u$o?AHQHsT(PnM|PYZa$Tf3?h+O8ZA z?4^}PV5{1xAT!@+wKSjXrIreYDofS&VntvzNBH&r8A0X|{QR${5(zH>3`~?Ql`Pv? zi+|O5VxmfUj&i+J-SwPn6Dl1)W#Xr-vgY$Dr-oimmD9@dGV_+oqt&p~$f8vNPdP7x zr>q*8&j?ne%0oT^6x>oH#i=y;+-6r+3-j;bs?NryJ)+k2pnUS}(K8ANUs2lO_8kI%sag=St zzj^*^=m3x?shcqC?b%)G6Edgx3)Riu7HJpNchxQGR`hml+}jTHbRD6Aer{8{(a%o2 z+giUK`Pa`_y}ozX!eC zi?y}@p@-FbQF5QuxF0nhQTvg9K=SWE{!w)R`3EKc5b_7rA>`jF`F9~dr5;0mulMfy zzU)@@^XhTT?XY@6-oFPkd{RC2ata*lVX5(Sb{E!vm%3o;q3&kuIvqluEB0Re-hLC9d^5hYQY9E2*GEX0nCzm7lKFxy%VrD<1R?+r@@n~D?&tqNJ)JITS7D|5r z{e-R6&k9uIJaG0!^-+BJhkDu*FJ4mT(EgZkqs_P%XBzPeN4>0GK_4&3_k0+0`k4AS zT31BM{zBl2l+~SAW)^FH3I9>X&OwmrYNNCa30!L~l;#>$-!DB;Yu2l!72oqGOQpl@ z-9Fw!lq!hmTnT?fl=+aWeyQ42rMYUeblhWo){wqD)LGV^Zz@4<`)$Z$icNn~x|j}z zysj+O83>)g^;;`?&I_Dob;*O=f{d4^lr#=p?X_2QbIwKG=nva5e*izffn+tgny4UM zPp(?4_Nud*+Hg?9=R}41M8#38ld2_Yva5-@yPDpY zIC;~_oytPp+yYw+Nt#y)s4n*OlosVko`#Bn_Z5I+?gZXKoiGEM zl8H><_+DdP=Av)r!13t%Hkn^MQedN_*An^x6kmMr{=LVJ=vLjEYx~FcAEtU$ZDMtg zox&0wd#ZN)n2&khwTw}#3!ZTs|9QDwSHZ)~;0Ef$sdBNZib5(v`oK z?Oe*+xnv<}U3M)unM-y?3?@!St)Q6r3xV6HHN9plu)Q;LouRfjIo*2JEVzRXqd$Cx zZekXkpSKGAWF?`wT#0(JZmn^tx%i1Xmb~tqb6!eLSe^9)ttLLF<~8TM(vsI+Yynpn ztL>7m)_~`wS&TYcqQWI~u>{n`x9$lMUr?5q&pDQwD@(_rnF6c1K7D0XzjWGbG)nka z$ZJJwEMAFdOj3#aiE!Hx1ACsHNa+cDPmrE7jr2Q_>#pF0qwi(GLrmn=yZFQfGznoJ zIP=w7<0(A(q>4-~mmE!YhC+DrT`)3$HIs9_;3YlrC-n3UJhT(%5^K(?1-i(srBDz<-evshT=)Ja7!?p7tP6lCwdPA;Q(`>PW(wNhHvt>e|>jkBets=@-7nx5Y#8(pe_Mo)N? zAtwd1HKXudo9q|8>?s{zX_vyqMi$LO)Ua_~- zxqAkswPw@POD&+Im>UMSo1UjIR*T%(a3Avhi^hdyxqVL8F{ca=^8H0nQJH3wy5i!U_APiFE`hIy!{8g23_CY$Ns6m9y zSBRCBm3GBz&6m-e&n{gz1H^Uk7i{FR<7BK%vS2w@5hWJ>x|dO!$|ar4uEUZHYh4~s zb+$&VrKi_98$CvU_#E?(F=T)yA~fpY85=f`Bnc$3I)9619G1NXgaCLAuLFvN2tmQu zixQBNGr9&o0$_9Kn9zlzR7;C1OK1RzpnxtAjx(hPTwqGc5f$zCquEWWw%Q9AOXxyrF1dV(^%PzIjIY)_PVv23~AAZ)V2FS{H~{JCxFhf{M|_jRgeY- z$gu<&v{I!p?@vxoIDyjujmd}WgBV@l5b?+-SRklL)nV?$Akt-IP$1kVP|+v9%K_np zS4OfassVm^1{#vcTD{VM9ZrBnfA~y{I)I>z)ts%u0UP9Vq%ZOfypfs$TxW_k)A zO}U}>B7y#Jb{VQu8+@ozZJwR53{Fh5!T>ttYkoz>VSk@N1)qMZ0_X{}20DvrskdI= z*Z0v9!M&Pr@ck_t?J|GV)E!KM|PWOsxx_9;?M*El)2gc-Ka_45XF%1DbJE^%Ifi+)yEh)=Ck=De_ zz-=9;aucL074%-IXwE#y_cjikY517*gLn}Uk|2%GS|Fkf>nglRF5x z@raoEE6!0@jQ)@b#gcXp-WUW~ORhqRQOOgwE`l}L09XU@9 z9GH9HLJ0%*fP#u$3ewMFT}0R!B}-%<8BCBmRc)+z!B#5U#+0d?@XnrwYt0YR3myvF zIN*e>OLZ#V)!=OHE_u8qQju2pSTKT94ZiWwpydqf~rn^ zE@Z66y&MZvC_)g&bBms$><&%heiN20kGIrW_7X!Hk{^S$OVx&NjNuag2#_HBL)Eba zrK(ER0SXj=1r82#hNHxa1jd~^0grY~W4cDih-VfmPf!Ek52wObEzPwWt)|%8{)yVM zjBgr2e4qlp)O3&PP${MFxVy+}ZuCNm7~1UNj18pFR}ZjeQODSo{w#WrJL-b#2&^CF z%~!Du0^aIeyLKvc;A0`A%-FyEk}d9vkR^F=@_dF%nkyj!P(B-xWd`H7ff>B>CaI z1M0=k0Tn}zL`s>A+T}ZbpCF;37rQ{_k%<&B*urph)S-AO>jGpjo?)mdm{a-&kXmXZ zv1YejuwSv5m2&}PhVqO`fix*fvdTr<^aX1zg?c&EgF6Ls+qppPCDYk)M5qypExbdu zg=UvXCTCtt1_kQA6|!I-t^>G4OfCH!<~Th8vy`v)Vs)@=5ZHv-#E@RbKZYHP{M~B4 z-82P9)MS`Ts1ppCH=@EAf$r{?X#{!m#&BD$rLuYTD7RuJT%#08|E}pWaA>)dhe8fb zWbKfj zNM20KbFDd%)S_p;h;N!gc|ppx2Y%**Jxo}^uwcYotEmWWsXZMvpVuEny9w)Jj27tq zV%FeD!}rQAFhvH`FQej&K7~|pX(Dg%rO5&=sy@x+3=_eWkm5L|@kk0z7)-0MmW?E^ zr|>nQfh)bNveco64r(L43`i~b1Tr98~5aup20RR0W>{2P!^G0cOY9h#{&aU~C2 zDMC0GtnGvNf`)zg;-CZm1YfA9w?W5PI}6Gogk{|!bvubQPR^coj+Ygm+uK zl%Pwvq(SdV(4~bR)grN%lm#>3y-`> z)%0Iu^4FP=Ob6F4XV!o-+XB2%wHeK<#e& zhoBu2(3Y<~I@enE)FA)|9_=<1kEdH88Eg&=z&l7kX{-}%z}i|C-$@soK}x!MEp!nG zR0muHA>-`MWfy@-0XCK#1JLq=DoVFzALxh_G4g-qDxeb-2k^ksU69wwU;8@WJ4R@n%Z>pA@_?8_1OueZSsQCzjKYzRKlPqjxS zKmp9Fj{To=@LcKlNl$t9baHPvA{byjy)7ZVbHNJXg58482vrzgmZUQ*Yp1=FD>XQb z^>wVUEE*u#@?+s;GJhOjq2a9=hiTTS@ZBLI?*aSxIk0H+rr+GU2Fl&svuR`W3_ zS+fCh(!#VvTj0uzVo`R@$_JTv(M8B~T?nx_((7mxxX%a-5T*myTk^@O%^(}+^@T;+9kUnH$ng(Gd90yKFvyq|^^>zh^9yS~r{EG&_Mhm4x2&En$fzKC>3?z18j(Q9OV`qidqmgz;@dQUTHg>dV zCt}+Whk`tNsDur%Z3|Pg7a{cDXCDJpGG;(#BE+J$PZM$+IAcS|E9r@!L9Y}!YQ)=u5fF8JNNetk6s@#sz}quZl08_ z=V?H0P+?3PWD1!m>__mi5Wd(yqg!w0dt_Bp6T>08g^_lfp_~47)HF~RQ2z$9<$~$7 z5_YG*#5%vpSs|aBf+-|~>6*yIs{<-l!ORA;=ulS$OlVIV^zjIn zTnLxEQ39e}w_XJ80|!xyx)Ueo7tn(N$9YJYNc(*?aXv|2a~?q%ERVpkz!D%cP2$-)0XogEVya zm1d?HeGP0UNYR_yThAl<@tozqqDJs#W9PXkUF;_Xi2>ES0jxRv`u-4t+`9E2MS`)w z4!;U!n?}pkgw@~5Eac# zh3+szS|a~QT0Q*iA2KVHMScZ!QZ&hXS;_(}aS0*2k2oc9sDPIcVYcX?87=_@Ck_NLD%zP>B2VK_TQ9`{?|ZSV@w&4SEUbN`%@K;QW5`*T^S1T<+&DG~2F)M#+qc=*XTW{|gDpUZgi|qDF;6rld)yMD zi-WM>+ouuxB#hV_At&gx2#we}L@yl`+&2QTNBX-3DME+DJ7K;;>*sC}2?y>fn!+$= z>4ZS<7T!AYgkGa@{B7YFLZA}wk)1|l5s=4g>h}?NM4TB-q|v{~gw|~5u4b!wSLD%# zk8y=sVi;-R$FA<^1){qhJo`Z=Y~uGgB)S@)AOIaU-K7>=%!E>y{T@Rszr#xZ4hb|v zy)Y>EM-1xI!&*fEoc9?nH{D8+4NHrb4kdD92miti`G1PJ!5y5ac^_jYJ0Uh6z|W_b zWpg#$lS0>$Su5CBV5sON4;D9xG8{RG_V+}oDqw|1xbn&s z8x3YgR~OL#7E|a9!xS(fIou=Ybu};_+QVB6nPv~5Xj6r~yAWSPr#gnQS%YSoHhCU^bX@x3ZCNzwLplH6|?9)cJTj*c^DG;D+yD9|u{l-Ng{>^a1 zkUo|!ETA}vY%^X*q4r+GSdLzp@6Vaa+p@^De^mXG$7FBr5aR#hYb0j7wMv>Xj z`3f3!ir6-NiVpZ{<3rrjd*KSB5B&@8dY#lmey{%~9)yENH2j}g!0rb4s_YVcjT02= zv%~GI2vhrZoMvB z6Mzieilq-Xi?AtYA;dLBcRf<v?LuA5uMck^juIg{ZXH$!Yk`Erl@Szj1OvaveLMeAZrglqS5 zqxT6!@;(Mrm66}B&RBUfE z_|P%!qaMLmr6+&Hgd|Rzealxcu;6*0AuM?MM|{N{X8nguh|A^70RZO^T-niX6>FNf z=zFmzi5K^of9@ZI6Z^;#PE;my+#|NlKCxqsQn~+$lj&R|=o5*kXdU{XMnM=!jn^sN z-xg}OMNn%?cqTUlv7LNGz!-hYfofgN{sCkRVCk6#f{x=Th4Di> z*nQy=h<`>F0WMPH;E@L7h(Ccdluzh&Gk1NUR(Wslaoidz7@=AI*XZ^nD*eO-1DZe*QO+^dgQSQtoI=WCDrTp}F5q!C!S-n@S1% zQ&2GA?Q3Uf&>M8zF#a`=G*B-mq)c~!)PXrE#2?n#WyXVB0L}o-@Kgy_MV<`67L0;>E`g0gAy|f93n}UwZCjLTE?qhO4Btew6MM78#}=dy znC89gMXzxpc5B%WauVoPVHEh_>M|PT9LK(p59T-#gTdzXip#vNIa<1AofqNx3p_%hqsH_X=djkXfZn-7esS=l0 zgt?>{z|6qngynBRuq@(v#6dHOD0`e+77WhP<$`!ULMk5oO0PU;Se|Y_g*F z^-Zo<-1>BqydCaiJ@4vSPlN5fF_>4ycdT)Q?~E}&ZNAh*sv7g0@Q-Zol_phYD#wPm z5bXl{2%3GHWdk4lCdwAN3&cesQcF1Fn@(y#0ATQ7NNgkH%jr{txlA#BpGPK!MugQi zqMIQ50Ervg60JzOk0c*?_9`62h(~mq+ffgDv--Zidh}|@Mp;~sG6O~|V;@Zv6rPg$ zQRIf`Af~(#>=y&2lPHa z!S7;R19UJFi(5kg;ju`~AQPd#0OA*0;5?9noPCO=a3wOZl(F_wN)dfG>tN@$|7}tq ziI!ER(fg5}-YI@FRBhHEZ#|qN6C)2Zd=?%RV{>HGh&bB}1T9rW{jUQJ3qZ~Yu9n(@(%(nvNL-r0H9Vv3 zEo9*0b-|Zz(Cb2_V{7L{QTa>8NS8Cm#v#VYp%sMvAD%DG4x0A^_Wczn6?zSR>^tEg z#60{l@GMAR5dw{RTpQ*%%>)kIA)3<*QuB0!1Pbxl+Z2{;lLJSD{)B; z&QvwMX)*$-2|;|6PiOcl++X8)lIU60=Gw;ZK(jP*Gql-co(YK@J412~==jRK8E1g#$wkhQ z=m{tgJjk5n?v!%hf|vM)(QoGvAB_C=u|IXjbKfT;rsq_c@ue}_Ddb+~IYnnE_akoj zAgVZ>y9iw_m3tGZy`51X{n0a#2j#zVibbqZ-vtUel#*)`KK|E{U{BlV!U)*VTUC-L z_+;nZ!p=K13+%k7n?pV2W=CGiq3PrFz*-jCen#KPo)8Ub_T_oBhbIs2ZEjo5U;`52Lpus~Lscg20>{|HJ3O@m02+GxZGF7zThC z_KeDb$cIE{&sYBhANGNk##x&NPn*sfhD}awLcwR{yeZ<9<1<) zRf3n0M{*Dni+?vimpH=%oU?Lo6@6`UI6H%u-BTxW;3&L?z?}T$c_I)ZZ@56O+FMv@ zRB;G4o@f-|$HPcff5=4{uWC%j~qF9Evu$|ddr!=v6@mBEw72VGLUS`2qUn7Baz$SkqO zr{KHj0mXJ1<2I`}2)3|-h2*No1HzdsPTSHVSeYDD!l^iVX6b5%+Y(quHy!)vt{Nab z#pBIqs&JWhoyFw7JX?VXhLvSrD;CZ1xYs`I;UL&7YM4V!v$dx9p?T0seA@h=#co%# zgPvqgE%E}+erPurU~jFt`-R!r9?+oGm26j5pNzlvAiP9ZV5Qpd-GG0G6w!>q(ucJ4 zBSuUdpFVifZoamFE$tG5>Eyf|0J^l??m6~OI^y)JVWezN-vAgEuS-*JA29Peqe{sz z&f`Sl1#S-_n2{c8gn*DKi&h+YM}(&f=N7~7F1-I5nD*=VB@q90(~;f-K89}McvRsq zbEw$&$3|`|e)?K)U=Do&84Ud~mdQx}Eq;o}{07F;JJ4V%40yp~qORC=MXq(txYuR3`ac3M1&=8c+iw=K}y&cb_B(bE`sAFz7*F! zMTptUl1>R5*<24Y(Lr$o$}qlF8VWq+v(BLUuj8})2l0@*&Pi^!##0Vr7cF?#8N|SN z748&@58_8op2yK0*Z3;ndq=G6fI=+b@3YDHov{^i3K=Si=7g0wVHi2qRvoZhgyq? zT6zvMio!%?6QpZ_$YKaen6-L}cN`GRk(Et7BS?YYE`T_n8*L;Zk|9Y-CWHZn&Y zaPJPD3UB0H_;UJ>I8e9g0~{5B*qQe^WZo8VkNgzC-2|XHqU9POIR<=E2K{=ShYAtZ z1E-%2kYS(lr-E198B&)H4TL(361=W}*E7I9fms{$58WCA5EkGHmW^!w!_aNMm@YA z@;-$yyt@IH3=Db%Z_~s??JVmuFJ`fW5}Zv!pg?bHn&s5be-8RR5#i_u4ri^jI$ zMA&RT{DwTgj^7|be#6rS*SW4;38)!q+}Q?|!9GWF_%@UQbuE&!IOHZiBB3Qg7a=IDKMq7ZX6}LL*~}KHx)r0yOLA@_d`alhO%dTR z$FY0<24}Go)uU6&;yEHaD(9#sn}hlKcUf&SZ|f~=N?g1THF9Ye16O%1SnMy9o*sg@aFq!J4R6w+d_I-bQ+#83CH^(x zD2i%^v*H+*Zgl4om$Uu+Kzjr)M(un1YNzXPRsl1Uwd5zMJR*LIq6P|M7lTAl_ioII zf+Q#+81peSF#47tOX?`R99|UfD|KZ>H}e(l!LmNiWxWG^zrrqY z{A>KetivZ*{4VjPc`R;4-r=Fu)oVM=LvigP)cy=}jDN=r>CF2~Z|kroE`O9-S)Kui z$Hm&O83o03ZKm z83((I!Z&f+nySz|ebqP(KwsFD?CTk;28$B^;WNpP=7ji;R}=ma2ExTl2>4G%Oi>u4 zHQl+h)-;C;<8diQ(}&@9nm9>` zCAY_1?%dWV<|Nvck<#5Te$%WQify)W-eTvsFj-;pWhPXUXZnJLzRKcQdof&CXzDlk zLfm{`;?qGUL}vZVOhU30TZRM{xF--nCE?p=5l+OXoWheSEf0s@0B+R6hQAx&);BYe z0YvmqGAm#RHK_PQ7YUC?F-Shc6Ak9?c8j-afkJJl;$X96$lP9+e{IkzsF@1mj^qw7 zXxzc`$N62bGW%I^JVc+uK(fu?cK|>C9wdgjk$tXP8;N7i8ieRt3RggY@TeHb)eiJ< zr(gvmu226RG=i+@YKHgo;{}SQ&(Iy%V05$BQ``wM@<>$X zhk9x{=87y+yPn#XcqqIxgVKw&{ZibPdgNmOBlr%$2oSEN*D_56yRW72gEYeN2b73p zfX0NIQFwDk1~b6DDHqafpzK(RS<&1ve@c(cwBAjS3CfF>{xCjNs9x}X3buE5MYq za!43nWik5a&gj4f?k2l}^fAXPdI!Ec(p&{Tfc!dSG32AD#8v2gQ3fw+RhEuY3)+`SN%QI{g7j7&?c$;{MdxbiT`-{`TlgiRHS4n4wb zo(Xkexl2R;F<<-%l1|!q0R7HxUSf)%N;*O37J0-xc7xO8fisqes9-0F&!SiH6*l}T zd+g$*;He`hN&OkkAXCiZe}^v_8G;dDhO_DZUczK}N(oD5stF?=`O5x*B zKDKakmHIao5%bLcFg|V|Lb-$M7V_tkFClcZZu!@nIm9D;u{#3KLx$sisaqN+_F-)Q36Zx6Rf^2k-Q(CY}fXSyP{737q8^bDH=RYl8k z@gzh|-wKr{-hx0P=M(Qi!2Jhuv0>(i6kITdU?RES&FnD26q!qMv0)TF8iBvh6tXxb zeh2XLcOl_1BrtR6Qi_qW!j|EIg9$;j78Y%H?vWLhy`IyHEo@jsS07;ONNy#ZoeA$k zn3XL-b5W^bswvU6f=pCHzP~(z?Hb(68voB`wi*ER9FmutiMqv(O zA2=T(nqV1%rlzOmj0$!iaQ)iUgf%%i;ml0o{>$XdL~06G$xcno>OVk7xM#kC%|Tpu zTd4&3O6BBAwLy`A9IjARtvNi{;NwBE9b}%7i)E?}{UE1B7jj_Zvk@Y|AVd)YH*#_5 z)G)UdA7;g;nS7PW3==GPLO;*s7?L3MeqKPY%PbIF(|kJ1Ho+l#pD}I{u2}74Znw{z(w#SLmV==XHYZf!GsscS+X(0;5sHBW1~N0@<&X*#pJt8 z{)`DD4fPM1Tx9YllS@oQoC-nG-LH2sp(8pJv3#O}rSD`yUy!Efz}P_|nMJ1;PiBYx zQDzFp>38voSk!}AhB8@p6B05F|C6Ye0XM^G2$I}I3l~mUIK!QBtT!_$>y3?hUJukqatJeJPo6E~BT*8T_#2bIBS)ySGNec9xTS)5<%SN5i)} zbGy>k4UXJU%2qkF*m-?%sJOK_THI1}izO??a{1w1F^|9L(L0L8;t1ZRoh+Qw<}GSD zR{oOXVD|VDjQU3OEu;7r^z+7;o4;t~uKqT6aeQ|%i=MOi>lU|R9=YPscy4^u+GASv zLMZCh0)&cyQ1MVPKRz-(Y$43nx?JRJZ?sCh`O*Bf-op1?8h@viaV}$9OlHmR9p7f{ zHV|QM>|R-$iQRf17w`ZRUZSIinUJdZKb)BJ!rPz7+YF7SK_TCl60QR^DRJxc7ffVL`NzCz}6TN1F8gTvZ#g5h@a*5}FWVA?*ZNoFwnS>c_Q> zvuyhpEVTFY|5J5z<#^eCn*X1wgZ%$gO=0_6^U21QR=UK>g5U4qm+WUk$_Yj)xY-&C ztGNptmpR|Wnu4m`MC5R0pJ8HzhjECyxE~kJd9X+G*OlBT4p>0CK!D_n6gbh}(#6r- F{{?PkZTbKJ literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/runtime.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/runtime.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd9256a105c4426126c02d9d32f5de9f4fbebebf GIT binary patch literal 32340 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/__pycache__/visitor.cpython-310.pyc b/venv/Lib/site-packages/jinja2/__pycache__/visitor.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..806756aaef24e1e4da3b79b2ca2c1b228692687a GIT binary patch literal 3961 zcma)9U2oi28MZz4jN^Q4Q&!!y+O7_;Qai8&X|*??1tluW60w@Cny82&i^uUflX&W} zr{{Q*W|&Awfm{9nLTV8=%TIxS;9KBg@3>uV;CWxqXQq{~cv_PIo&pu%?YeXht>*gZ>Hgyb(ap6ebabw?NwQN> zoGGz?cqFo1DzhsN`h!&URaPh|PA>Uvoc9Mwsnj_wX_sybnqrz-ynHAbSF2*Ev)F<5=|yoRn?A>F=s{YC)|YncORMRYu2E@M zdzjUA5)BfRCl>$N+yCM5W1aWlqB-6_pb$hEoIO5Q+4=Ej$;mM+Qr{ay@w4bund4rP z^`Z~Ie@=$xdUtSH`e7Ku;xI(YEkky>*67ZXC%e-nt!9JPXHj2;VHt#BKbOOl#v5Vy zd>EzG8(o8z(k{x{es-xz@7&^Qv_W&^Dh=3Y-+JHXnKx(T1Z(FRX>&%X^t$dO4<6-6 zHdMWb5@T5-i9At}xo!WA@7Lu9-ml3`Kko+P_pBb*P=)|BV<3gCr ztcYHqK$W6;6BI)+39IN1GcGy9MXXT5mw~`V1%atyCU=E6LP4*7(2p)fRU_g&N{7mb zi?bv?6Bh~epD0#?&OkNEdF6LlPe-8W&vLmN@T$k?1gW~wFfG{h)xEgGU73$ z?&kx-nkfC%(?u-!VyWS&$*7|r;Wj%Eg*6747WUY_wtLPWtv@-^xvrrWYootDVZeB8 z1}lg>gH>Euh+gG{NEXAuMv|cmQ7EP#QOq(Zj|xQIDBfn`eC72lx*{<%O{+m5{bjvM zZ>w1=8^};p6uNYa%Yo8caI)#>b^KJ}njx4b1Sr=R68bJC7>9Pe(L1eqC+;q+nPcHn zU@54CDvYFi7>uo#*4Tb!U%6vf+Cy8vJ$A?TsXcc7Y(KFQYh3$_{RjIH>v{U`4~ zRiKVi=dy3Xi+7;7+*~S=rnlhR45v%Gy8wLbsK+7tHZ&Njn85L!JNAw<+Fo>&*Q_{3 z&aq?P!C-8^v|d?1vA)=XYh=88(|Fr@4wq3w&gch3NmRC67V5{~g_3bbBeHt!BVgqu;`6 zeG8&&grUsiFl2h+Ea+`|d7A{;#9J(E%SA?E*WZ939fqcS%e8N>i^ltFJRf6f=~~0n z9&YmlVwG@|*Q-mHrKenw3+Nx@(O`0^Exq-6zH;wcSOW@Tf(ek2_3|xp`$$Kb0R;Dz zj{Xk*|363uY0NK-0P7~*B#sDa452coWdi#|D}aF|{hf+FEJSED0_jR=rH7boN*_0s0$8TFMW2x` zQ?{FIQ1M4s=Y8I4pcSZMXhfP`q)FKKfG#Swo%kD`w@EH9XD?F{8eMzkk-)B&F9@qEczf0AvWxjItO9;uK_D^)NMUSywL#mC3Rm4F>evkSb;BXqbva znrp8GSC4gfs{#|@mi?K-1-H^Vnyyo&ewKJ8v3 zF^73dTj_pA6+|&z*nJN6@8QLVxOr~^`}cx}cQnm5H&|&|$Dsu0N!D9>R33&mDmRaQ z`S_FY(a%14^z(z(r|s=(UC0$`2yIKJP^FK5Ttyz|DgR+%5@GePv!?VvuKpzbnEv7! VA|coE-3_Pi`1WmQ%YFM>{{wQ;3)%nx literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/jinja2/_identifier.py b/venv/Lib/site-packages/jinja2/_identifier.py new file mode 100644 index 0000000..928c150 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/_identifier.py @@ -0,0 +1,6 @@ +import re + +# generated by scripts/generate_identifier_pattern.py +pattern = re.compile( + r"[\w·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఄా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഀ-ഃ഻഼ാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳷-᳹᷀-᷹᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꣿꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𐴤-𐽆𐴧-𐽐𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑄴𑅅𑅆𑅳𑆀-𑆂𑆳-𑇀𑇉-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌻𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑑞𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑠬-𑠺𑨁-𑨊𑨳-𑨹𑨻-𑨾𑩇𑩑-𑩛𑪊-𑪙𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𑴱-𑴶𑴺𑴼𑴽𑴿-𑵅𑵇𑶊-𑶎𑶐𑶑𑶓-𑶗𑻳-𑻶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯]+" # noqa: B950 +) diff --git a/venv/Lib/site-packages/jinja2/async_utils.py b/venv/Lib/site-packages/jinja2/async_utils.py new file mode 100644 index 0000000..f0c1402 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/async_utils.py @@ -0,0 +1,99 @@ +import inspect +import typing as t +from functools import WRAPPER_ASSIGNMENTS +from functools import wraps + +from .utils import _PassArg +from .utils import pass_eval_context + +if t.TYPE_CHECKING: + import typing_extensions as te + +V = t.TypeVar("V") + + +def async_variant(normal_func): # type: ignore + def decorator(async_func): # type: ignore + pass_arg = _PassArg.from_obj(normal_func) + need_eval_context = pass_arg is None + + if pass_arg is _PassArg.environment: + + def is_async(args: t.Any) -> bool: + return t.cast(bool, args[0].is_async) + + else: + + def is_async(args: t.Any) -> bool: + return t.cast(bool, args[0].environment.is_async) + + # Take the doc and annotations from the sync function, but the + # name from the async function. Pallets-Sphinx-Themes + # build_function_directive expects __wrapped__ to point to the + # sync function. + async_func_attrs = ("__module__", "__name__", "__qualname__") + normal_func_attrs = tuple(set(WRAPPER_ASSIGNMENTS).difference(async_func_attrs)) + + @wraps(normal_func, assigned=normal_func_attrs) + @wraps(async_func, assigned=async_func_attrs, updated=()) + def wrapper(*args, **kwargs): # type: ignore + b = is_async(args) + + if need_eval_context: + args = args[1:] + + if b: + return async_func(*args, **kwargs) + + return normal_func(*args, **kwargs) + + if need_eval_context: + wrapper = pass_eval_context(wrapper) + + wrapper.jinja_async_variant = True # type: ignore[attr-defined] + return wrapper + + return decorator + + +_common_primitives = {int, float, bool, str, list, dict, tuple, type(None)} + + +async def auto_await(value: t.Union[t.Awaitable["V"], "V"]) -> "V": + # Avoid a costly call to isawaitable + if type(value) in _common_primitives: + return t.cast("V", value) + + if inspect.isawaitable(value): + return await t.cast("t.Awaitable[V]", value) + + return value + + +class _IteratorToAsyncIterator(t.Generic[V]): + def __init__(self, iterator: "t.Iterator[V]"): + self._iterator = iterator + + def __aiter__(self) -> "te.Self": + return self + + async def __anext__(self) -> V: + try: + return next(self._iterator) + except StopIteration as e: + raise StopAsyncIteration(e.value) from e + + +def auto_aiter( + iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> "t.AsyncIterator[V]": + if hasattr(iterable, "__aiter__"): + return iterable.__aiter__() + else: + return _IteratorToAsyncIterator(iter(iterable)) + + +async def auto_to_list( + value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> t.List["V"]: + return [x async for x in auto_aiter(value)] diff --git a/venv/Lib/site-packages/jinja2/bccache.py b/venv/Lib/site-packages/jinja2/bccache.py new file mode 100644 index 0000000..ada8b09 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/bccache.py @@ -0,0 +1,408 @@ +"""The optional bytecode cache system. This is useful if you have very +complex template situations and the compilation of all those templates +slows down your application too much. + +Situations where this is useful are often forking web applications that +are initialized on the first request. +""" + +import errno +import fnmatch +import marshal +import os +import pickle +import stat +import sys +import tempfile +import typing as t +from hashlib import sha1 +from io import BytesIO +from types import CodeType + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .environment import Environment + + class _MemcachedClient(te.Protocol): + def get(self, key: str) -> bytes: ... + + def set( + self, key: str, value: bytes, timeout: t.Optional[int] = None + ) -> None: ... + + +bc_version = 5 +# Magic bytes to identify Jinja bytecode cache files. Contains the +# Python major and minor version to avoid loading incompatible bytecode +# if a project upgrades its Python version. +bc_magic = ( + b"j2" + + pickle.dumps(bc_version, 2) + + pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1], 2) +) + + +class Bucket: + """Buckets are used to store the bytecode for one template. It's created + and initialized by the bytecode cache and passed to the loading functions. + + The buckets get an internal checksum from the cache assigned and use this + to automatically reject outdated cache material. Individual bytecode + cache subclasses don't have to care about cache invalidation. + """ + + def __init__(self, environment: "Environment", key: str, checksum: str) -> None: + self.environment = environment + self.key = key + self.checksum = checksum + self.reset() + + def reset(self) -> None: + """Resets the bucket (unloads the bytecode).""" + self.code: t.Optional[CodeType] = None + + def load_bytecode(self, f: t.BinaryIO) -> None: + """Loads bytecode from a file or file like object.""" + # make sure the magic header is correct + magic = f.read(len(bc_magic)) + if magic != bc_magic: + self.reset() + return + # the source code of the file changed, we need to reload + checksum = pickle.load(f) + if self.checksum != checksum: + self.reset() + return + # if marshal_load fails then we need to reload + try: + self.code = marshal.load(f) + except (EOFError, ValueError, TypeError): + self.reset() + return + + def write_bytecode(self, f: t.IO[bytes]) -> None: + """Dump the bytecode into the file or file like object passed.""" + if self.code is None: + raise TypeError("can't write empty bucket") + f.write(bc_magic) + pickle.dump(self.checksum, f, 2) + marshal.dump(self.code, f) + + def bytecode_from_string(self, string: bytes) -> None: + """Load bytecode from bytes.""" + self.load_bytecode(BytesIO(string)) + + def bytecode_to_string(self) -> bytes: + """Return the bytecode as bytes.""" + out = BytesIO() + self.write_bytecode(out) + return out.getvalue() + + +class BytecodeCache: + """To implement your own bytecode cache you have to subclass this class + and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of + these methods are passed a :class:`~jinja2.bccache.Bucket`. + + A very basic bytecode cache that saves the bytecode on the file system:: + + from os import path + + class MyCache(BytecodeCache): + + def __init__(self, directory): + self.directory = directory + + def load_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + if path.exists(filename): + with open(filename, 'rb') as f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + with open(filename, 'wb') as f: + bucket.write_bytecode(f) + + A more advanced version of a filesystem based bytecode cache is part of + Jinja. + """ + + def load_bytecode(self, bucket: Bucket) -> None: + """Subclasses have to override this method to load bytecode into a + bucket. If they are not able to find code in the cache for the + bucket, it must not do anything. + """ + raise NotImplementedError() + + def dump_bytecode(self, bucket: Bucket) -> None: + """Subclasses have to override this method to write the bytecode + from a bucket back to the cache. If it unable to do so it must not + fail silently but raise an exception. + """ + raise NotImplementedError() + + def clear(self) -> None: + """Clears the cache. This method is not used by Jinja but should be + implemented to allow applications to clear the bytecode cache used + by a particular environment. + """ + + def get_cache_key( + self, name: str, filename: t.Optional[t.Union[str]] = None + ) -> str: + """Returns the unique hash key for this template name.""" + hash = sha1(name.encode("utf-8")) + + if filename is not None: + hash.update(f"|{filename}".encode()) + + return hash.hexdigest() + + def get_source_checksum(self, source: str) -> str: + """Returns a checksum for the source.""" + return sha1(source.encode("utf-8")).hexdigest() + + def get_bucket( + self, + environment: "Environment", + name: str, + filename: t.Optional[str], + source: str, + ) -> Bucket: + """Return a cache bucket for the given template. All arguments are + mandatory but filename may be `None`. + """ + key = self.get_cache_key(name, filename) + checksum = self.get_source_checksum(source) + bucket = Bucket(environment, key, checksum) + self.load_bytecode(bucket) + return bucket + + def set_bucket(self, bucket: Bucket) -> None: + """Put the bucket into the cache.""" + self.dump_bytecode(bucket) + + +class FileSystemBytecodeCache(BytecodeCache): + """A bytecode cache that stores bytecode on the filesystem. It accepts + two arguments: The directory where the cache items are stored and a + pattern string that is used to build the filename. + + If no directory is specified a default cache directory is selected. On + Windows the user's temp directory is used, on UNIX systems a directory + is created for the user in the system temp directory. + + The pattern can be used to have multiple separate caches operate on the + same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` + is replaced with the cache key. + + >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') + + This bytecode cache supports clearing of the cache using the clear method. + """ + + def __init__( + self, directory: t.Optional[str] = None, pattern: str = "__jinja2_%s.cache" + ) -> None: + if directory is None: + directory = self._get_default_cache_dir() + self.directory = directory + self.pattern = pattern + + def _get_default_cache_dir(self) -> str: + def _unsafe_dir() -> "te.NoReturn": + raise RuntimeError( + "Cannot determine safe temp directory. You " + "need to explicitly provide one." + ) + + tmpdir = tempfile.gettempdir() + + # On windows the temporary directory is used specific unless + # explicitly forced otherwise. We can just use that. + if os.name == "nt": + return tmpdir + if not hasattr(os, "getuid"): + _unsafe_dir() + + dirname = f"_jinja2-cache-{os.getuid()}" + actual_dir = os.path.join(tmpdir, dirname) + + try: + os.mkdir(actual_dir, stat.S_IRWXU) + except OSError as e: + if e.errno != errno.EEXIST: + raise + try: + os.chmod(actual_dir, stat.S_IRWXU) + actual_dir_stat = os.lstat(actual_dir) + if ( + actual_dir_stat.st_uid != os.getuid() + or not stat.S_ISDIR(actual_dir_stat.st_mode) + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU + ): + _unsafe_dir() + except OSError as e: + if e.errno != errno.EEXIST: + raise + + actual_dir_stat = os.lstat(actual_dir) + if ( + actual_dir_stat.st_uid != os.getuid() + or not stat.S_ISDIR(actual_dir_stat.st_mode) + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU + ): + _unsafe_dir() + + return actual_dir + + def _get_cache_filename(self, bucket: Bucket) -> str: + return os.path.join(self.directory, self.pattern % (bucket.key,)) + + def load_bytecode(self, bucket: Bucket) -> None: + filename = self._get_cache_filename(bucket) + + # Don't test for existence before opening the file, since the + # file could disappear after the test before the open. + try: + f = open(filename, "rb") + except (FileNotFoundError, IsADirectoryError, PermissionError): + # PermissionError can occur on Windows when an operation is + # in progress, such as calling clear(). + return + + with f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket: Bucket) -> None: + # Write to a temporary file, then rename to the real name after + # writing. This avoids another process reading the file before + # it is fully written. + name = self._get_cache_filename(bucket) + f = tempfile.NamedTemporaryFile( + mode="wb", + dir=os.path.dirname(name), + prefix=os.path.basename(name), + suffix=".tmp", + delete=False, + ) + + def remove_silent() -> None: + try: + os.remove(f.name) + except OSError: + # Another process may have called clear(). On Windows, + # another program may be holding the file open. + pass + + try: + with f: + bucket.write_bytecode(f) + except BaseException: + remove_silent() + raise + + try: + os.replace(f.name, name) + except OSError: + # Another process may have called clear(). On Windows, + # another program may be holding the file open. + remove_silent() + except BaseException: + remove_silent() + raise + + def clear(self) -> None: + # imported lazily here because google app-engine doesn't support + # write access on the file system and the function does not exist + # normally. + from os import remove + + files = fnmatch.filter(os.listdir(self.directory), self.pattern % ("*",)) + for filename in files: + try: + remove(os.path.join(self.directory, filename)) + except OSError: + pass + + +class MemcachedBytecodeCache(BytecodeCache): + """This class implements a bytecode cache that uses a memcache cache for + storing the information. It does not enforce a specific memcache library + (tummy's memcache or cmemcache) but will accept any class that provides + the minimal interface required. + + Libraries compatible with this class: + + - `cachelib `_ + - `python-memcached `_ + + (Unfortunately the django cache interface is not compatible because it + does not support storing binary data, only text. You can however pass + the underlying cache client to the bytecode cache which is available + as `django.core.cache.cache._client`.) + + The minimal interface for the client passed to the constructor is this: + + .. class:: MinimalClientInterface + + .. method:: set(key, value[, timeout]) + + Stores the bytecode in the cache. `value` is a string and + `timeout` the timeout of the key. If timeout is not provided + a default timeout or no timeout should be assumed, if it's + provided it's an integer with the number of seconds the cache + item should exist. + + .. method:: get(key) + + Returns the value for the cache key. If the item does not + exist in the cache the return value must be `None`. + + The other arguments to the constructor are the prefix for all keys that + is added before the actual cache key and the timeout for the bytecode in + the cache system. We recommend a high (or no) timeout. + + This bytecode cache does not support clearing of used items in the cache. + The clear method is a no-operation function. + + .. versionadded:: 2.7 + Added support for ignoring memcache errors through the + `ignore_memcache_errors` parameter. + """ + + def __init__( + self, + client: "_MemcachedClient", + prefix: str = "jinja2/bytecode/", + timeout: t.Optional[int] = None, + ignore_memcache_errors: bool = True, + ): + self.client = client + self.prefix = prefix + self.timeout = timeout + self.ignore_memcache_errors = ignore_memcache_errors + + def load_bytecode(self, bucket: Bucket) -> None: + try: + code = self.client.get(self.prefix + bucket.key) + except Exception: + if not self.ignore_memcache_errors: + raise + else: + bucket.bytecode_from_string(code) + + def dump_bytecode(self, bucket: Bucket) -> None: + key = self.prefix + bucket.key + value = bucket.bytecode_to_string() + + try: + if self.timeout is not None: + self.client.set(key, value, self.timeout) + else: + self.client.set(key, value) + except Exception: + if not self.ignore_memcache_errors: + raise diff --git a/venv/Lib/site-packages/jinja2/compiler.py b/venv/Lib/site-packages/jinja2/compiler.py new file mode 100644 index 0000000..a4ff6a1 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/compiler.py @@ -0,0 +1,1998 @@ +"""Compiles nodes from the parser into Python code.""" + +import typing as t +from contextlib import contextmanager +from functools import update_wrapper +from io import StringIO +from itertools import chain +from keyword import iskeyword as is_python_keyword + +from markupsafe import escape +from markupsafe import Markup + +from . import nodes +from .exceptions import TemplateAssertionError +from .idtracking import Symbols +from .idtracking import VAR_LOAD_ALIAS +from .idtracking import VAR_LOAD_PARAMETER +from .idtracking import VAR_LOAD_RESOLVE +from .idtracking import VAR_LOAD_UNDEFINED +from .nodes import EvalContext +from .optimizer import Optimizer +from .utils import _PassArg +from .utils import concat +from .visitor import NodeVisitor + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .environment import Environment + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + +operators = { + "eq": "==", + "ne": "!=", + "gt": ">", + "gteq": ">=", + "lt": "<", + "lteq": "<=", + "in": "in", + "notin": "not in", +} + + +def optimizeconst(f: F) -> F: + def new_func( + self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any + ) -> t.Any: + # Only optimize if the frame is not volatile + if self.optimizer is not None and not frame.eval_ctx.volatile: + new_node = self.optimizer.visit(node, frame.eval_ctx) + + if new_node != node: + return self.visit(new_node, frame) + + return f(self, node, frame, **kwargs) + + return update_wrapper(new_func, f) # type: ignore[return-value] + + +def _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed and op in self.environment.intercepted_binops # type: ignore + ): + self.write(f"environment.call_binop(context, {op!r}, ") + self.visit(node.left, frame) + self.write(", ") + self.visit(node.right, frame) + else: + self.write("(") + self.visit(node.left, frame) + self.write(f" {op} ") + self.visit(node.right, frame) + + self.write(")") + + return visitor + + +def _make_unop( + op: str, +) -> t.Callable[["CodeGenerator", nodes.UnaryExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed and op in self.environment.intercepted_unops # type: ignore + ): + self.write(f"environment.call_unop(context, {op!r}, ") + self.visit(node.node, frame) + else: + self.write("(" + op) + self.visit(node.node, frame) + + self.write(")") + + return visitor + + +def generate( + node: nodes.Template, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, +) -> t.Optional[str]: + """Generate the python source for a node tree.""" + if not isinstance(node, nodes.Template): + raise TypeError("Can't compile non template nodes") + + generator = environment.code_generator_class( + environment, name, filename, stream, defer_init, optimized + ) + generator.visit(node) + + if stream is None: + return generator.stream.getvalue() # type: ignore + + return None + + +def has_safe_repr(value: t.Any) -> bool: + """Does the node have a safe representation?""" + if value is None or value is NotImplemented or value is Ellipsis: + return True + + if type(value) in {bool, int, float, complex, range, str, Markup}: + return True + + if type(value) in {tuple, list, set, frozenset}: + return all(has_safe_repr(v) for v in value) + + if type(value) is dict: # noqa E721 + return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items()) + + return False + + +def find_undeclared( + nodes: t.Iterable[nodes.Node], names: t.Iterable[str] +) -> t.Set[str]: + """Check if the names passed are accessed undeclared. The return value + is a set of all the undeclared names from the sequence of names found. + """ + visitor = UndeclaredNameVisitor(names) + try: + for node in nodes: + visitor.visit(node) + except VisitorExit: + pass + return visitor.undeclared + + +class MacroRef: + def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None: + self.node = node + self.accesses_caller = False + self.accesses_kwargs = False + self.accesses_varargs = False + + +class Frame: + """Holds compile time information for us.""" + + def __init__( + self, + eval_ctx: EvalContext, + parent: t.Optional["Frame"] = None, + level: t.Optional[int] = None, + ) -> None: + self.eval_ctx = eval_ctx + + # the parent of this frame + self.parent = parent + + if parent is None: + self.symbols = Symbols(level=level) + + # in some dynamic inheritance situations the compiler needs to add + # write tests around output statements. + self.require_output_check = False + + # inside some tags we are using a buffer rather than yield statements. + # this for example affects {% filter %} or {% macro %}. If a frame + # is buffered this variable points to the name of the list used as + # buffer. + self.buffer: t.Optional[str] = None + + # the name of the block we're in, otherwise None. + self.block: t.Optional[str] = None + + else: + self.symbols = Symbols(parent.symbols, level=level) + self.require_output_check = parent.require_output_check + self.buffer = parent.buffer + self.block = parent.block + + # a toplevel frame is the root + soft frames such as if conditions. + self.toplevel = False + + # the root frame is basically just the outermost frame, so no if + # conditions. This information is used to optimize inheritance + # situations. + self.rootlevel = False + + # variables set inside of loops and blocks should not affect outer frames, + # but they still needs to be kept track of as part of the active context. + self.loop_frame = False + self.block_frame = False + + # track whether the frame is being used in an if-statement or conditional + # expression as it determines which errors should be raised during runtime + # or compile time. + self.soft_frame = False + + def copy(self) -> "te.Self": + """Create a copy of the current one.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.symbols = self.symbols.copy() + return rv + + def inner(self, isolated: bool = False) -> "Frame": + """Return an inner frame.""" + if isolated: + return Frame(self.eval_ctx, level=self.symbols.level + 1) + return Frame(self.eval_ctx, self) + + def soft(self) -> "te.Self": + """Return a soft frame. A soft frame may not be modified as + standalone thing as it shares the resources with the frame it + was created of, but it's not a rootlevel frame any longer. + + This is only used to implement if-statements and conditional + expressions. + """ + rv = self.copy() + rv.rootlevel = False + rv.soft_frame = True + return rv + + __copy__ = copy + + +class VisitorExit(RuntimeError): + """Exception used by the `UndeclaredNameVisitor` to signal a stop.""" + + +class DependencyFinderVisitor(NodeVisitor): + """A visitor that collects filter and test calls.""" + + def __init__(self) -> None: + self.filters: t.Set[str] = set() + self.tests: t.Set[str] = set() + + def visit_Filter(self, node: nodes.Filter) -> None: + self.generic_visit(node) + self.filters.add(node.name) + + def visit_Test(self, node: nodes.Test) -> None: + self.generic_visit(node) + self.tests.add(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting at blocks.""" + + +class UndeclaredNameVisitor(NodeVisitor): + """A visitor that checks if a name is accessed without being + declared. This is different from the frame visitor as it will + not stop at closure frames. + """ + + def __init__(self, names: t.Iterable[str]) -> None: + self.names = set(names) + self.undeclared: t.Set[str] = set() + + def visit_Name(self, node: nodes.Name) -> None: + if node.ctx == "load" and node.name in self.names: + self.undeclared.add(node.name) + if self.undeclared == self.names: + raise VisitorExit() + else: + self.names.discard(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting a blocks.""" + + +class CompilerExit(Exception): + """Raised if the compiler encountered a situation where it just + doesn't make sense to further process the code. Any block that + raises such an exception is not further processed. + """ + + +class CodeGenerator(NodeVisitor): + def __init__( + self, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, + ) -> None: + if stream is None: + stream = StringIO() + self.environment = environment + self.name = name + self.filename = filename + self.stream = stream + self.created_block_context = False + self.defer_init = defer_init + self.optimizer: t.Optional[Optimizer] = None + + if optimized: + self.optimizer = Optimizer(environment) + + # aliases for imports + self.import_aliases: t.Dict[str, str] = {} + + # a registry for all blocks. Because blocks are moved out + # into the global python scope they are registered here + self.blocks: t.Dict[str, nodes.Block] = {} + + # the number of extends statements so far + self.extends_so_far = 0 + + # some templates have a rootlevel extends. In this case we + # can safely assume that we're a child template and do some + # more optimizations. + self.has_known_extends = False + + # the current line number + self.code_lineno = 1 + + # registry of all filters and tests (global, not block local) + self.tests: t.Dict[str, str] = {} + self.filters: t.Dict[str, str] = {} + + # the debug information + self.debug_info: t.List[t.Tuple[int, int]] = [] + self._write_debug_info: t.Optional[int] = None + + # the number of new lines before the next write() + self._new_lines = 0 + + # the line number of the last written statement + self._last_line = 0 + + # true if nothing was written so far. + self._first_write = True + + # used by the `temporary_identifier` method to get new + # unique, temporary identifier + self._last_identifier = 0 + + # the current indentation + self._indentation = 0 + + # Tracks toplevel assignments + self._assign_stack: t.List[t.Set[str]] = [] + + # Tracks parameter definition blocks + self._param_def_block: t.List[t.Set[str]] = [] + + # Tracks the current context. + self._context_reference_stack = ["context"] + + @property + def optimized(self) -> bool: + return self.optimizer is not None + + # -- Various compilation helpers + + def fail(self, msg: str, lineno: int) -> "te.NoReturn": + """Fail with a :exc:`TemplateAssertionError`.""" + raise TemplateAssertionError(msg, lineno, self.name, self.filename) + + def temporary_identifier(self) -> str: + """Get a new unique identifier.""" + self._last_identifier += 1 + return f"t_{self._last_identifier}" + + def buffer(self, frame: Frame) -> None: + """Enable buffering for the frame from that point onwards.""" + frame.buffer = self.temporary_identifier() + self.writeline(f"{frame.buffer} = []") + + def return_buffer_contents( + self, frame: Frame, force_unescaped: bool = False + ) -> None: + """Return the buffer contents of the frame.""" + if not force_unescaped: + if frame.eval_ctx.volatile: + self.writeline("if context.eval_ctx.autoescape:") + self.indent() + self.writeline(f"return Markup(concat({frame.buffer}))") + self.outdent() + self.writeline("else:") + self.indent() + self.writeline(f"return concat({frame.buffer})") + self.outdent() + return + elif frame.eval_ctx.autoescape: + self.writeline(f"return Markup(concat({frame.buffer}))") + return + self.writeline(f"return concat({frame.buffer})") + + def indent(self) -> None: + """Indent by one.""" + self._indentation += 1 + + def outdent(self, step: int = 1) -> None: + """Outdent by step.""" + self._indentation -= step + + def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None: + """Yield or write into the frame buffer.""" + if frame.buffer is None: + self.writeline("yield ", node) + else: + self.writeline(f"{frame.buffer}.append(", node) + + def end_write(self, frame: Frame) -> None: + """End the writing process started by `start_write`.""" + if frame.buffer is not None: + self.write(")") + + def simple_write( + self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None + ) -> None: + """Simple shortcut for start_write + write + end_write.""" + self.start_write(frame, node) + self.write(s) + self.end_write(frame) + + def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None: + """Visit a list of nodes as block in a frame. If the current frame + is no buffer a dummy ``if 0: yield None`` is written automatically. + """ + try: + self.writeline("pass") + for node in nodes: + self.visit(node, frame) + except CompilerExit: + pass + + def write(self, x: str) -> None: + """Write a string into the output stream.""" + if self._new_lines: + if not self._first_write: + self.stream.write("\n" * self._new_lines) + self.code_lineno += self._new_lines + if self._write_debug_info is not None: + self.debug_info.append((self._write_debug_info, self.code_lineno)) + self._write_debug_info = None + self._first_write = False + self.stream.write(" " * self._indentation) + self._new_lines = 0 + self.stream.write(x) + + def writeline( + self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0 + ) -> None: + """Combination of newline and write.""" + self.newline(node, extra) + self.write(x) + + def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None: + """Add one or more newlines before the next write.""" + self._new_lines = max(self._new_lines, 1 + extra) + if node is not None and node.lineno != self._last_line: + self._write_debug_info = node.lineno + self._last_line = node.lineno + + def signature( + self, + node: t.Union[nodes.Call, nodes.Filter, nodes.Test], + frame: Frame, + extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> None: + """Writes a function call to the stream for the current node. + A leading comma is added automatically. The extra keyword + arguments may not include python keywords otherwise a syntax + error could occur. The extra keyword arguments should be given + as python dict. + """ + # if any of the given keyword arguments is a python keyword + # we have to make sure that no invalid call is created. + kwarg_workaround = any( + is_python_keyword(t.cast(str, k)) + for k in chain((x.key for x in node.kwargs), extra_kwargs or ()) + ) + + for arg in node.args: + self.write(", ") + self.visit(arg, frame) + + if not kwarg_workaround: + for kwarg in node.kwargs: + self.write(", ") + self.visit(kwarg, frame) + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f", {key}={value}") + if node.dyn_args: + self.write(", *") + self.visit(node.dyn_args, frame) + + if kwarg_workaround: + if node.dyn_kwargs is not None: + self.write(", **dict({") + else: + self.write(", **{") + for kwarg in node.kwargs: + self.write(f"{kwarg.key!r}: ") + self.visit(kwarg.value, frame) + self.write(", ") + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f"{key!r}: {value}, ") + if node.dyn_kwargs is not None: + self.write("}, **") + self.visit(node.dyn_kwargs, frame) + self.write(")") + else: + self.write("}") + + elif node.dyn_kwargs is not None: + self.write(", **") + self.visit(node.dyn_kwargs, frame) + + def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None: + """Find all filter and test names used in the template and + assign them to variables in the compiled namespace. Checking + that the names are registered with the environment is done when + compiling the Filter and Test nodes. If the node is in an If or + CondExpr node, the check is done at runtime instead. + + .. versionchanged:: 3.0 + Filters and tests in If and CondExpr nodes are checked at + runtime instead of compile time. + """ + visitor = DependencyFinderVisitor() + + for node in nodes: + visitor.visit(node) + + for id_map, names, dependency in ( + (self.filters, visitor.filters, "filters"), + ( + self.tests, + visitor.tests, + "tests", + ), + ): + for name in sorted(names): + if name not in id_map: + id_map[name] = self.temporary_identifier() + + # add check during runtime that dependencies used inside of executed + # blocks are defined, as this step may be skipped during compile time + self.writeline("try:") + self.indent() + self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]") + self.outdent() + self.writeline("except KeyError:") + self.indent() + self.writeline("@internalcode") + self.writeline(f"def {id_map[name]}(*unused):") + self.indent() + self.writeline( + f'raise TemplateRuntimeError("No {dependency[:-1]}' + f' named {name!r} found.")' + ) + self.outdent() + self.outdent() + + def enter_frame(self, frame: Frame) -> None: + undefs = [] + for target, (action, param) in frame.symbols.loads.items(): + if action == VAR_LOAD_PARAMETER: + pass + elif action == VAR_LOAD_RESOLVE: + self.writeline(f"{target} = {self.get_resolve_func()}({param!r})") + elif action == VAR_LOAD_ALIAS: + self.writeline(f"{target} = {param}") + elif action == VAR_LOAD_UNDEFINED: + undefs.append(target) + else: + raise NotImplementedError("unknown load instruction") + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None: + if not with_python_scope: + undefs = [] + for target in frame.symbols.loads: + undefs.append(target) + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str: + return async_value if self.environment.is_async else sync_value + + def func(self, name: str) -> str: + return f"{self.choose_async()}def {name}" + + def macro_body( + self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame + ) -> t.Tuple[Frame, MacroRef]: + """Dump the function def of a macro or call block.""" + frame = frame.inner() + frame.symbols.analyze_node(node) + macro_ref = MacroRef(node) + + explicit_caller = None + skip_special_params = set() + args = [] + + for idx, arg in enumerate(node.args): + if arg.name == "caller": + explicit_caller = idx + if arg.name in ("kwargs", "varargs"): + skip_special_params.add(arg.name) + args.append(frame.symbols.ref(arg.name)) + + undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs")) + + if "caller" in undeclared: + # In older Jinja versions there was a bug that allowed caller + # to retain the special behavior even if it was mentioned in + # the argument list. However thankfully this was only really + # working if it was the last argument. So we are explicitly + # checking this now and error out if it is anywhere else in + # the argument list. + if explicit_caller is not None: + try: + node.defaults[explicit_caller - len(node.args)] + except IndexError: + self.fail( + "When defining macros or call blocks the " + 'special "caller" argument must be omitted ' + "or be given a default.", + node.lineno, + ) + else: + args.append(frame.symbols.declare_parameter("caller")) + macro_ref.accesses_caller = True + if "kwargs" in undeclared and "kwargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("kwargs")) + macro_ref.accesses_kwargs = True + if "varargs" in undeclared and "varargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("varargs")) + macro_ref.accesses_varargs = True + + # macros are delayed, they never require output checks + frame.require_output_check = False + frame.symbols.analyze_node(node) + self.writeline(f"{self.func('macro')}({', '.join(args)}):", node) + self.indent() + + self.buffer(frame) + self.enter_frame(frame) + + self.push_parameter_definitions(frame) + for idx, arg in enumerate(node.args): + ref = frame.symbols.ref(arg.name) + self.writeline(f"if {ref} is missing:") + self.indent() + try: + default = node.defaults[idx - len(node.args)] + except IndexError: + self.writeline( + f'{ref} = undefined("parameter {arg.name!r} was not provided",' + f" name={arg.name!r})" + ) + else: + self.writeline(f"{ref} = ") + self.visit(default, frame) + self.mark_parameter_stored(ref) + self.outdent() + self.pop_parameter_definitions() + + self.blockvisit(node.body, frame) + self.return_buffer_contents(frame, force_unescaped=True) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + return frame, macro_ref + + def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None: + """Dump the macro definition for the def created by macro_body.""" + arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args) + name = getattr(macro_ref.node, "name", None) + if len(macro_ref.node.args) == 1: + arg_tuple += "," + self.write( + f"Macro(environment, macro, {name!r}, ({arg_tuple})," + f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r}," + f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)" + ) + + def position(self, node: nodes.Node) -> str: + """Return a human readable position for the node.""" + rv = f"line {node.lineno}" + if self.name is not None: + rv = f"{rv} in {self.name!r}" + return rv + + def dump_local_context(self, frame: Frame) -> str: + items_kv = ", ".join( + f"{name!r}: {target}" + for name, target in frame.symbols.dump_stores().items() + ) + return f"{{{items_kv}}}" + + def write_commons(self) -> None: + """Writes a common preamble that is used by root and block functions. + Primarily this sets up common local helpers and enforces a generator + through a dead branch. + """ + self.writeline("resolve = context.resolve_or_missing") + self.writeline("undefined = environment.undefined") + self.writeline("concat = environment.concat") + # always use the standard Undefined class for the implicit else of + # conditional expressions + self.writeline("cond_expr_undefined = Undefined") + self.writeline("if 0: yield None") + + def push_parameter_definitions(self, frame: Frame) -> None: + """Pushes all parameter targets from the given frame into a local + stack that permits tracking of yet to be assigned parameters. In + particular this enables the optimization from `visit_Name` to skip + undefined expressions for parameters in macros as macros can reference + otherwise unbound parameters. + """ + self._param_def_block.append(frame.symbols.dump_param_targets()) + + def pop_parameter_definitions(self) -> None: + """Pops the current parameter definitions set.""" + self._param_def_block.pop() + + def mark_parameter_stored(self, target: str) -> None: + """Marks a parameter in the current parameter definitions as stored. + This will skip the enforced undefined checks. + """ + if self._param_def_block: + self._param_def_block[-1].discard(target) + + def push_context_reference(self, target: str) -> None: + self._context_reference_stack.append(target) + + def pop_context_reference(self) -> None: + self._context_reference_stack.pop() + + def get_context_ref(self) -> str: + return self._context_reference_stack[-1] + + def get_resolve_func(self) -> str: + target = self._context_reference_stack[-1] + if target == "context": + return "resolve" + return f"{target}.resolve" + + def derive_context(self, frame: Frame) -> str: + return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})" + + def parameter_is_undeclared(self, target: str) -> bool: + """Checks if a given target is an undeclared parameter.""" + if not self._param_def_block: + return False + return target in self._param_def_block[-1] + + def push_assign_tracking(self) -> None: + """Pushes a new layer for assignment tracking.""" + self._assign_stack.append(set()) + + def pop_assign_tracking(self, frame: Frame) -> None: + """Pops the topmost level for assignment tracking and updates the + context variables if necessary. + """ + vars = self._assign_stack.pop() + if ( + not frame.block_frame + and not frame.loop_frame + and not frame.toplevel + or not vars + ): + return + public_names = [x for x in vars if x[:1] != "_"] + if len(vars) == 1: + name = next(iter(vars)) + ref = frame.symbols.ref(name) + if frame.loop_frame: + self.writeline(f"_loop_vars[{name!r}] = {ref}") + return + if frame.block_frame: + self.writeline(f"_block_vars[{name!r}] = {ref}") + return + self.writeline(f"context.vars[{name!r}] = {ref}") + else: + if frame.loop_frame: + self.writeline("_loop_vars.update({") + elif frame.block_frame: + self.writeline("_block_vars.update({") + else: + self.writeline("context.vars.update({") + for idx, name in enumerate(sorted(vars)): + if idx: + self.write(", ") + ref = frame.symbols.ref(name) + self.write(f"{name!r}: {ref}") + self.write("})") + if not frame.block_frame and not frame.loop_frame and public_names: + if len(public_names) == 1: + self.writeline(f"context.exported_vars.add({public_names[0]!r})") + else: + names_str = ", ".join(map(repr, sorted(public_names))) + self.writeline(f"context.exported_vars.update(({names_str}))") + + # -- Statement Visitors + + def visit_Template( + self, node: nodes.Template, frame: t.Optional[Frame] = None + ) -> None: + assert frame is None, "no root frame allowed" + eval_ctx = EvalContext(self.environment, self.name) + + from .runtime import async_exported + from .runtime import exported + + if self.environment.is_async: + exported_names = sorted(exported + async_exported) + else: + exported_names = sorted(exported) + + self.writeline("from jinja2.runtime import " + ", ".join(exported_names)) + + # if we want a deferred initialization we cannot move the + # environment into a local name + envenv = "" if self.defer_init else ", environment=environment" + + # do we have an extends tag at all? If not, we can save some + # overhead by just not processing any inheritance code. + have_extends = node.find(nodes.Extends) is not None + + # find all blocks + for block in node.find_all(nodes.Block): + if block.name in self.blocks: + self.fail(f"block {block.name!r} defined twice", block.lineno) + self.blocks[block.name] = block + + # find all imports and import them + for import_ in node.find_all(nodes.ImportedName): + if import_.importname not in self.import_aliases: + imp = import_.importname + self.import_aliases[imp] = alias = self.temporary_identifier() + if "." in imp: + module, obj = imp.rsplit(".", 1) + self.writeline(f"from {module} import {obj} as {alias}") + else: + self.writeline(f"import {imp} as {alias}") + + # add the load name + self.writeline(f"name = {self.name!r}") + + # generate the root render function. + self.writeline( + f"{self.func('root')}(context, missing=missing{envenv}):", extra=1 + ) + self.indent() + self.write_commons() + + # process the root + frame = Frame(eval_ctx) + if "self" in find_undeclared(node.body, ("self",)): + ref = frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + frame.symbols.analyze_node(node) + frame.toplevel = frame.rootlevel = True + frame.require_output_check = have_extends and not self.has_known_extends + if have_extends: + self.writeline("parent_template = None") + self.enter_frame(frame) + self.pull_dependencies(node.body) + self.blockvisit(node.body, frame) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + # make sure that the parent root is called. + if have_extends: + if not self.has_known_extends: + self.indent() + self.writeline("if parent_template is not None:") + self.indent() + if not self.environment.is_async: + self.writeline("yield from parent_template.root_render_func(context)") + else: + self.writeline("agen = parent_template.root_render_func(context)") + self.writeline("try:") + self.indent() + self.writeline("async for event in agen:") + self.indent() + self.writeline("yield event") + self.outdent() + self.outdent() + self.writeline("finally: await agen.aclose()") + self.outdent(1 + (not self.has_known_extends)) + + # at this point we now have the blocks collected and can visit them too. + for name, block in self.blocks.items(): + self.writeline( + f"{self.func('block_' + name)}(context, missing=missing{envenv}):", + block, + 1, + ) + self.indent() + self.write_commons() + # It's important that we do not make this frame a child of the + # toplevel template. This would cause a variety of + # interesting issues with identifier tracking. + block_frame = Frame(eval_ctx) + block_frame.block_frame = True + undeclared = find_undeclared(block.body, ("self", "super")) + if "self" in undeclared: + ref = block_frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + if "super" in undeclared: + ref = block_frame.symbols.declare_parameter("super") + self.writeline(f"{ref} = context.super({name!r}, block_{name})") + block_frame.symbols.analyze_node(block) + block_frame.block = name + self.writeline("_block_vars = {}") + self.enter_frame(block_frame) + self.pull_dependencies(block.body) + self.blockvisit(block.body, block_frame) + self.leave_frame(block_frame, with_python_scope=True) + self.outdent() + + blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks) + self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1) + debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info) + self.writeline(f"debug_info = {debug_kv_str!r}") + + def visit_Block(self, node: nodes.Block, frame: Frame) -> None: + """Call a block and register it for the template.""" + level = 0 + if frame.toplevel: + # if we know that we are a child template, there is no need to + # check if we are one + if self.has_known_extends: + return + if self.extends_so_far > 0: + self.writeline("if parent_template is None:") + self.indent() + level += 1 + + if node.scoped: + context = self.derive_context(frame) + else: + context = self.get_context_ref() + + if node.required: + self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node) + self.indent() + self.writeline( + f'raise TemplateRuntimeError("Required block {node.name!r} not found")', + node, + ) + self.outdent() + + if not self.environment.is_async and frame.buffer is None: + self.writeline( + f"yield from context.blocks[{node.name!r}][0]({context})", node + ) + else: + self.writeline(f"gen = context.blocks[{node.name!r}][0]({context})") + self.writeline("try:") + self.indent() + self.writeline( + f"{self.choose_async()}for event in gen:", + node, + ) + self.indent() + self.simple_write("event", frame) + self.outdent() + self.outdent() + self.writeline( + f"finally: {self.choose_async('await gen.aclose()', 'gen.close()')}" + ) + + self.outdent(level) + + def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None: + """Calls the extender.""" + if not frame.toplevel: + self.fail("cannot use extend from a non top-level scope", node.lineno) + + # if the number of extends statements in general is zero so + # far, we don't have to add a check if something extended + # the template before this one. + if self.extends_so_far > 0: + # if we have a known extends we just add a template runtime + # error into the generated code. We could catch that at compile + # time too, but i welcome it not to confuse users by throwing the + # same error at different times just "because we can". + if not self.has_known_extends: + self.writeline("if parent_template is not None:") + self.indent() + self.writeline('raise TemplateRuntimeError("extended multiple times")') + + # if we have a known extends already we don't need that code here + # as we know that the template execution will end here. + if self.has_known_extends: + raise CompilerExit() + else: + self.outdent() + + self.writeline("parent_template = environment.get_template(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + self.writeline("for name, parent_block in parent_template.blocks.items():") + self.indent() + self.writeline("context.blocks.setdefault(name, []).append(parent_block)") + self.outdent() + + # if this extends statement was in the root level we can take + # advantage of that information and simplify the generated code + # in the top level from this point onwards + if frame.rootlevel: + self.has_known_extends = True + + # and now we have one more + self.extends_so_far += 1 + + def visit_Include(self, node: nodes.Include, frame: Frame) -> None: + """Handles includes.""" + if node.ignore_missing: + self.writeline("try:") + self.indent() + + func_name = "get_or_select_template" + if isinstance(node.template, nodes.Const): + if isinstance(node.template.value, str): + func_name = "get_template" + elif isinstance(node.template.value, (tuple, list)): + func_name = "select_template" + elif isinstance(node.template, (nodes.Tuple, nodes.List)): + func_name = "select_template" + + self.writeline(f"template = environment.{func_name}(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + if node.ignore_missing: + self.outdent() + self.writeline("except TemplateNotFound:") + self.indent() + self.writeline("pass") + self.outdent() + self.writeline("else:") + self.indent() + + def loop_body() -> None: + self.indent() + self.simple_write("event", frame) + self.outdent() + + if node.with_context: + self.writeline( + f"gen = template.root_render_func(" + "template.new_context(context.get_all(), True," + f" {self.dump_local_context(frame)}))" + ) + self.writeline("try:") + self.indent() + self.writeline(f"{self.choose_async()}for event in gen:") + loop_body() + self.outdent() + self.writeline( + f"finally: {self.choose_async('await gen.aclose()', 'gen.close()')}" + ) + elif self.environment.is_async: + self.writeline( + "for event in (await template._get_default_module_async())" + "._body_stream:" + ) + loop_body() + else: + self.writeline("yield from template._get_default_module()._body_stream") + + if node.ignore_missing: + self.outdent() + + def _import_common( + self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame + ) -> None: + self.write(f"{self.choose_async('await ')}environment.get_template(") + self.visit(node.template, frame) + self.write(f", {self.name!r}).") + + if node.with_context: + f_name = f"make_module{self.choose_async('_async')}" + self.write( + f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})" + ) + else: + self.write(f"_get_default_module{self.choose_async('_async')}(context)") + + def visit_Import(self, node: nodes.Import, frame: Frame) -> None: + """Visit regular imports.""" + self.writeline(f"{frame.symbols.ref(node.target)} = ", node) + if frame.toplevel: + self.write(f"context.vars[{node.target!r}] = ") + + self._import_common(node, frame) + + if frame.toplevel and not node.target.startswith("_"): + self.writeline(f"context.exported_vars.discard({node.target!r})") + + def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None: + """Visit named imports.""" + self.newline(node) + self.write("included_template = ") + self._import_common(node, frame) + var_names = [] + discarded_names = [] + for name in node.names: + if isinstance(name, tuple): + name, alias = name + else: + alias = name + self.writeline( + f"{frame.symbols.ref(alias)} =" + f" getattr(included_template, {name!r}, missing)" + ) + self.writeline(f"if {frame.symbols.ref(alias)} is missing:") + self.indent() + # The position will contain the template name, and will be formatted + # into a string that will be compiled into an f-string. Curly braces + # in the name must be replaced with escapes so that they will not be + # executed as part of the f-string. + position = self.position(node).replace("{", "{{").replace("}", "}}") + message = ( + "the template {included_template.__name__!r}" + f" (imported on {position})" + f" does not export the requested name {name!r}" + ) + self.writeline( + f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})" + ) + self.outdent() + if frame.toplevel: + var_names.append(alias) + if not alias.startswith("_"): + discarded_names.append(alias) + + if var_names: + if len(var_names) == 1: + name = var_names[0] + self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}") + else: + names_kv = ", ".join( + f"{name!r}: {frame.symbols.ref(name)}" for name in var_names + ) + self.writeline(f"context.vars.update({{{names_kv}}})") + if discarded_names: + if len(discarded_names) == 1: + self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})") + else: + names_str = ", ".join(map(repr, discarded_names)) + self.writeline( + f"context.exported_vars.difference_update(({names_str}))" + ) + + def visit_For(self, node: nodes.For, frame: Frame) -> None: + loop_frame = frame.inner() + loop_frame.loop_frame = True + test_frame = frame.inner() + else_frame = frame.inner() + + # try to figure out if we have an extended loop. An extended loop + # is necessary if the loop is in recursive mode if the special loop + # variable is accessed in the body if the body is a scoped block. + extended_loop = ( + node.recursive + or "loop" + in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",)) + or any(block.scoped for block in node.find_all(nodes.Block)) + ) + + loop_ref = None + if extended_loop: + loop_ref = loop_frame.symbols.declare_parameter("loop") + + loop_frame.symbols.analyze_node(node, for_branch="body") + if node.else_: + else_frame.symbols.analyze_node(node, for_branch="else") + + if node.test: + loop_filter_func = self.temporary_identifier() + test_frame.symbols.analyze_node(node, for_branch="test") + self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test) + self.indent() + self.enter_frame(test_frame) + self.writeline(self.choose_async("async for ", "for ")) + self.visit(node.target, loop_frame) + self.write(" in ") + self.write(self.choose_async("auto_aiter(fiter)", "fiter")) + self.write(":") + self.indent() + self.writeline("if ", node.test) + self.visit(node.test, test_frame) + self.write(":") + self.indent() + self.writeline("yield ") + self.visit(node.target, loop_frame) + self.outdent(3) + self.leave_frame(test_frame, with_python_scope=True) + + # if we don't have an recursive loop we have to find the shadowed + # variables at that point. Because loops can be nested but the loop + # variable is a special one we have to enforce aliasing for it. + if node.recursive: + self.writeline( + f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node + ) + self.indent() + self.buffer(loop_frame) + + # Use the same buffer for the else frame + else_frame.buffer = loop_frame.buffer + + # make sure the loop variable is a special one and raise a template + # assertion error if a loop tries to write to loop + if extended_loop: + self.writeline(f"{loop_ref} = missing") + + for name in node.find_all(nodes.Name): + if name.ctx == "store" and name.name == "loop": + self.fail( + "Can't assign to special loop variable in for-loop target", + name.lineno, + ) + + if node.else_: + iteration_indicator = self.temporary_identifier() + self.writeline(f"{iteration_indicator} = 1") + + self.writeline(self.choose_async("async for ", "for "), node) + self.visit(node.target, loop_frame) + if extended_loop: + self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(") + else: + self.write(" in ") + + if node.test: + self.write(f"{loop_filter_func}(") + if node.recursive: + self.write("reciter") + else: + if self.environment.is_async and not extended_loop: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async and not extended_loop: + self.write(")") + if node.test: + self.write(")") + + if node.recursive: + self.write(", undefined, loop_render_func, depth):") + else: + self.write(", undefined):" if extended_loop else ":") + + self.indent() + self.enter_frame(loop_frame) + + self.writeline("_loop_vars = {}") + self.blockvisit(node.body, loop_frame) + if node.else_: + self.writeline(f"{iteration_indicator} = 0") + self.outdent() + self.leave_frame( + loop_frame, with_python_scope=node.recursive and not node.else_ + ) + + if node.else_: + self.writeline(f"if {iteration_indicator}:") + self.indent() + self.enter_frame(else_frame) + self.blockvisit(node.else_, else_frame) + self.leave_frame(else_frame) + self.outdent() + + # if the node was recursive we have to return the buffer contents + # and start the iteration code + if node.recursive: + self.return_buffer_contents(loop_frame) + self.outdent() + self.start_write(frame, node) + self.write(f"{self.choose_async('await ')}loop(") + if self.environment.is_async: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async: + self.write(")") + self.write(", loop)") + self.end_write(frame) + + # at the end of the iteration, clear any assignments made in the + # loop from the top level + if self._assign_stack: + self._assign_stack[-1].difference_update(loop_frame.symbols.stores) + + def visit_If(self, node: nodes.If, frame: Frame) -> None: + if_frame = frame.soft() + self.writeline("if ", node) + self.visit(node.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(node.body, if_frame) + self.outdent() + for elif_ in node.elif_: + self.writeline("elif ", elif_) + self.visit(elif_.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(elif_.body, if_frame) + self.outdent() + if node.else_: + self.writeline("else:") + self.indent() + self.blockvisit(node.else_, if_frame) + self.outdent() + + def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None: + macro_frame, macro_ref = self.macro_body(node, frame) + self.newline() + if frame.toplevel: + if not node.name.startswith("_"): + self.write(f"context.exported_vars.add({node.name!r})") + self.writeline(f"context.vars[{node.name!r}] = ") + self.write(f"{frame.symbols.ref(node.name)} = ") + self.macro_def(macro_ref, macro_frame) + + def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None: + call_frame, macro_ref = self.macro_body(node, frame) + self.writeline("caller = ") + self.macro_def(macro_ref, call_frame) + self.start_write(frame, node) + self.visit_Call(node.call, frame, forward_caller=True) + self.end_write(frame) + + def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None: + filter_frame = frame.inner() + filter_frame.symbols.analyze_node(node) + self.enter_frame(filter_frame) + self.buffer(filter_frame) + self.blockvisit(node.body, filter_frame) + self.start_write(frame, node) + self.visit_Filter(node.filter, filter_frame) + self.end_write(frame) + self.leave_frame(filter_frame) + + def visit_With(self, node: nodes.With, frame: Frame) -> None: + with_frame = frame.inner() + with_frame.symbols.analyze_node(node) + self.enter_frame(with_frame) + for target, expr in zip(node.targets, node.values): + self.newline() + self.visit(target, with_frame) + self.write(" = ") + self.visit(expr, frame) + self.blockvisit(node.body, with_frame) + self.leave_frame(with_frame) + + def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None: + self.newline(node) + self.visit(node.node, frame) + + class _FinalizeInfo(t.NamedTuple): + const: t.Optional[t.Callable[..., str]] + src: t.Optional[str] + + @staticmethod + def _default_finalize(value: t.Any) -> t.Any: + """The default finalize function if the environment isn't + configured with one. Or, if the environment has one, this is + called on that function's output for constants. + """ + return str(value) + + _finalize: t.Optional[_FinalizeInfo] = None + + def _make_finalize(self) -> _FinalizeInfo: + """Build the finalize function to be used on constants and at + runtime. Cached so it's only created once for all output nodes. + + Returns a ``namedtuple`` with the following attributes: + + ``const`` + A function to finalize constant data at compile time. + + ``src`` + Source code to output around nodes to be evaluated at + runtime. + """ + if self._finalize is not None: + return self._finalize + + finalize: t.Optional[t.Callable[..., t.Any]] + finalize = default = self._default_finalize + src = None + + if self.environment.finalize: + src = "environment.finalize(" + env_finalize = self.environment.finalize + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(env_finalize) # type: ignore + ) + finalize = None + + if pass_arg is None: + + def finalize(value: t.Any) -> t.Any: # noqa: F811 + return default(env_finalize(value)) + + else: + src = f"{src}{pass_arg}, " + + if pass_arg == "environment": + + def finalize(value: t.Any) -> t.Any: # noqa: F811 + return default(env_finalize(self.environment, value)) + + self._finalize = self._FinalizeInfo(finalize, src) + return self._finalize + + def _output_const_repr(self, group: t.Iterable[t.Any]) -> str: + """Given a group of constant values converted from ``Output`` + child nodes, produce a string to write to the template module + source. + """ + return repr(concat(group)) + + def _output_child_to_const( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> str: + """Try to optimize a child of an ``Output`` node by trying to + convert it to constant, finalized data at compile time. + + If :exc:`Impossible` is raised, the node is not constant and + will be evaluated at runtime. Any other exception will also be + evaluated at runtime for easier debugging. + """ + const = node.as_const(frame.eval_ctx) + + if frame.eval_ctx.autoescape: + const = escape(const) + + # Template data doesn't go through finalize. + if isinstance(node, nodes.TemplateData): + return str(const) + + return finalize.const(const) # type: ignore + + def _output_child_pre( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> None: + """Output extra source code before visiting a child of an + ``Output`` node. + """ + if frame.eval_ctx.volatile: + self.write("(escape if context.eval_ctx.autoescape else str)(") + elif frame.eval_ctx.autoescape: + self.write("escape(") + else: + self.write("str(") + + if finalize.src is not None: + self.write(finalize.src) + + def _output_child_post( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> None: + """Output extra source code after visiting a child of an + ``Output`` node. + """ + self.write(")") + + if finalize.src is not None: + self.write(")") + + def visit_Output(self, node: nodes.Output, frame: Frame) -> None: + # If an extends is active, don't render outside a block. + if frame.require_output_check: + # A top-level extends is known to exist at compile time. + if self.has_known_extends: + return + + self.writeline("if parent_template is None:") + self.indent() + + finalize = self._make_finalize() + body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = [] + + # Evaluate constants at compile time if possible. Each item in + # body will be either a list of static data or a node to be + # evaluated at runtime. + for child in node.nodes: + try: + if not ( + # If the finalize function requires runtime context, + # constants can't be evaluated at compile time. + finalize.const + # Unless it's basic template data that won't be + # finalized anyway. + or isinstance(child, nodes.TemplateData) + ): + raise nodes.Impossible() + + const = self._output_child_to_const(child, frame, finalize) + except (nodes.Impossible, Exception): + # The node was not constant and needs to be evaluated at + # runtime. Or another error was raised, which is easier + # to debug at runtime. + body.append(child) + continue + + if body and isinstance(body[-1], list): + body[-1].append(const) + else: + body.append([const]) + + if frame.buffer is not None: + if len(body) == 1: + self.writeline(f"{frame.buffer}.append(") + else: + self.writeline(f"{frame.buffer}.extend((") + + self.indent() + + for item in body: + if isinstance(item, list): + # A group of constant data to join and output. + val = self._output_const_repr(item) + + if frame.buffer is None: + self.writeline("yield " + val) + else: + self.writeline(val + ",") + else: + if frame.buffer is None: + self.writeline("yield ", item) + else: + self.newline(item) + + # A node to be evaluated at runtime. + self._output_child_pre(item, frame, finalize) + self.visit(item, frame) + self._output_child_post(item, frame, finalize) + + if frame.buffer is not None: + self.write(",") + + if frame.buffer is not None: + self.outdent() + self.writeline(")" if len(body) == 1 else "))") + + if frame.require_output_check: + self.outdent() + + def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None: + self.push_assign_tracking() + + # ``a.b`` is allowed for assignment, and is parsed as an NSRef. However, + # it is only valid if it references a Namespace object. Emit a check for + # that for each ref here, before assignment code is emitted. This can't + # be done in visit_NSRef as the ref could be in the middle of a tuple. + seen_refs: t.Set[str] = set() + + for nsref in node.find_all(nodes.NSRef): + if nsref.name in seen_refs: + # Only emit the check for each reference once, in case the same + # ref is used multiple times in a tuple, `ns.a, ns.b = c, d`. + continue + + seen_refs.add(nsref.name) + ref = frame.symbols.ref(nsref.name) + self.writeline(f"if not isinstance({ref}, Namespace):") + self.indent() + self.writeline( + "raise TemplateRuntimeError" + '("cannot assign attribute on non-namespace object")' + ) + self.outdent() + + self.newline(node) + self.visit(node.target, frame) + self.write(" = ") + self.visit(node.node, frame) + self.pop_assign_tracking(frame) + + def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None: + self.push_assign_tracking() + block_frame = frame.inner() + # This is a special case. Since a set block always captures we + # will disable output checks. This way one can use set blocks + # toplevel even in extended templates. + block_frame.require_output_check = False + block_frame.symbols.analyze_node(node) + self.enter_frame(block_frame) + self.buffer(block_frame) + self.blockvisit(node.body, block_frame) + self.newline(node) + self.visit(node.target, frame) + self.write(" = (Markup if context.eval_ctx.autoescape else identity)(") + if node.filter is not None: + self.visit_Filter(node.filter, block_frame) + else: + self.write(f"concat({block_frame.buffer})") + self.write(")") + self.pop_assign_tracking(frame) + self.leave_frame(block_frame) + + # -- Expression Visitors + + def visit_Name(self, node: nodes.Name, frame: Frame) -> None: + if node.ctx == "store" and ( + frame.toplevel or frame.loop_frame or frame.block_frame + ): + if self._assign_stack: + self._assign_stack[-1].add(node.name) + ref = frame.symbols.ref(node.name) + + # If we are looking up a variable we might have to deal with the + # case where it's undefined. We can skip that case if the load + # instruction indicates a parameter which are always defined. + if node.ctx == "load": + load = frame.symbols.find_load(ref) + if not ( + load is not None + and load[0] == VAR_LOAD_PARAMETER + and not self.parameter_is_undeclared(ref) + ): + self.write( + f"(undefined(name={node.name!r}) if {ref} is missing else {ref})" + ) + return + + self.write(ref) + + def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None: + # NSRef is a dotted assignment target a.b=c, but uses a[b]=c internally. + # visit_Assign emits code to validate that each ref is to a Namespace + # object only. That can't be emitted here as the ref could be in the + # middle of a tuple assignment. + ref = frame.symbols.ref(node.name) + self.writeline(f"{ref}[{node.attr!r}]") + + def visit_Const(self, node: nodes.Const, frame: Frame) -> None: + val = node.as_const(frame.eval_ctx) + if isinstance(val, float): + self.write(str(val)) + else: + self.write(repr(val)) + + def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None: + try: + self.write(repr(node.as_const(frame.eval_ctx))) + except nodes.Impossible: + self.write( + f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})" + ) + + def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None: + self.write("(") + idx = -1 + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write(",)" if idx == 0 else ")") + + def visit_List(self, node: nodes.List, frame: Frame) -> None: + self.write("[") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write("]") + + def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None: + self.write("{") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item.key, frame) + self.write(": ") + self.visit(item.value, frame) + self.write("}") + + visit_Add = _make_binop("+") + visit_Sub = _make_binop("-") + visit_Mul = _make_binop("*") + visit_Div = _make_binop("/") + visit_FloorDiv = _make_binop("//") + visit_Pow = _make_binop("**") + visit_Mod = _make_binop("%") + visit_And = _make_binop("and") + visit_Or = _make_binop("or") + visit_Pos = _make_unop("+") + visit_Neg = _make_unop("-") + visit_Not = _make_unop("not ") + + @optimizeconst + def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None: + if frame.eval_ctx.volatile: + func_name = "(markup_join if context.eval_ctx.volatile else str_join)" + elif frame.eval_ctx.autoescape: + func_name = "markup_join" + else: + func_name = "str_join" + self.write(f"{func_name}((") + for arg in node.nodes: + self.visit(arg, frame) + self.write(", ") + self.write("))") + + @optimizeconst + def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None: + self.write("(") + self.visit(node.expr, frame) + for op in node.ops: + self.visit(op, frame) + self.write(")") + + def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None: + self.write(f" {operators[node.op]} ") + self.visit(node.expr, frame) + + @optimizeconst + def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getattr(") + self.visit(node.node, frame) + self.write(f", {node.attr!r})") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None: + # slices bypass the environment getitem method. + if isinstance(node.arg, nodes.Slice): + self.visit(node.node, frame) + self.write("[") + self.visit(node.arg, frame) + self.write("]") + else: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getitem(") + self.visit(node.node, frame) + self.write(", ") + self.visit(node.arg, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None: + if node.start is not None: + self.visit(node.start, frame) + self.write(":") + if node.stop is not None: + self.visit(node.stop, frame) + if node.step is not None: + self.write(":") + self.visit(node.step, frame) + + @contextmanager + def _filter_test_common( + self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool + ) -> t.Iterator[None]: + if self.environment.is_async: + self.write("(await auto_await(") + + if is_filter: + self.write(f"{self.filters[node.name]}(") + func = self.environment.filters.get(node.name) + else: + self.write(f"{self.tests[node.name]}(") + func = self.environment.tests.get(node.name) + + # When inside an If or CondExpr frame, allow the filter to be + # undefined at compile time and only raise an error if it's + # actually called at runtime. See pull_dependencies. + if func is None and not frame.soft_frame: + type_name = "filter" if is_filter else "test" + self.fail(f"No {type_name} named {node.name!r}.", node.lineno) + + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(func) # type: ignore + ) + + if pass_arg is not None: + self.write(f"{pass_arg}, ") + + # Back to the visitor function to handle visiting the target of + # the filter or test. + yield + + self.signature(node, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None: + with self._filter_test_common(node, frame, True): + # if the filter node is None we are inside a filter block + # and want to write to the current buffer + if node.node is not None: + self.visit(node.node, frame) + elif frame.eval_ctx.volatile: + self.write( + f"(Markup(concat({frame.buffer}))" + f" if context.eval_ctx.autoescape else concat({frame.buffer}))" + ) + elif frame.eval_ctx.autoescape: + self.write(f"Markup(concat({frame.buffer}))") + else: + self.write(f"concat({frame.buffer})") + + @optimizeconst + def visit_Test(self, node: nodes.Test, frame: Frame) -> None: + with self._filter_test_common(node, frame, False): + self.visit(node.node, frame) + + @optimizeconst + def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None: + frame = frame.soft() + + def write_expr2() -> None: + if node.expr2 is not None: + self.visit(node.expr2, frame) + return + + self.write( + f'cond_expr_undefined("the inline if-expression on' + f" {self.position(node)} evaluated to false and no else" + f' section was defined.")' + ) + + self.write("(") + self.visit(node.expr1, frame) + self.write(" if ") + self.visit(node.test, frame) + self.write(" else ") + write_expr2() + self.write(")") + + @optimizeconst + def visit_Call( + self, node: nodes.Call, frame: Frame, forward_caller: bool = False + ) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + if self.environment.sandboxed: + self.write("environment.call(context, ") + else: + self.write("context.call(") + self.visit(node.node, frame) + extra_kwargs = {"caller": "caller"} if forward_caller else None + loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {} + block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {} + if extra_kwargs: + extra_kwargs.update(loop_kwargs, **block_kwargs) + elif loop_kwargs or block_kwargs: + extra_kwargs = dict(loop_kwargs, **block_kwargs) + self.signature(node, frame, extra_kwargs) + self.write(")") + if self.environment.is_async: + self.write("))") + + def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None: + self.write(node.key + "=") + self.visit(node.value, frame) + + # -- Unused nodes for extensions + + def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None: + self.write("Markup(") + self.visit(node.expr, frame) + self.write(")") + + def visit_MarkSafeIfAutoescape( + self, node: nodes.MarkSafeIfAutoescape, frame: Frame + ) -> None: + self.write("(Markup if context.eval_ctx.autoescape else identity)(") + self.visit(node.expr, frame) + self.write(")") + + def visit_EnvironmentAttribute( + self, node: nodes.EnvironmentAttribute, frame: Frame + ) -> None: + self.write("environment." + node.name) + + def visit_ExtensionAttribute( + self, node: nodes.ExtensionAttribute, frame: Frame + ) -> None: + self.write(f"environment.extensions[{node.identifier!r}].{node.name}") + + def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None: + self.write(self.import_aliases[node.importname]) + + def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None: + self.write(node.name) + + def visit_ContextReference( + self, node: nodes.ContextReference, frame: Frame + ) -> None: + self.write("context") + + def visit_DerivedContextReference( + self, node: nodes.DerivedContextReference, frame: Frame + ) -> None: + self.write(self.derive_context(frame)) + + def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None: + self.writeline("continue", node) + + def visit_Break(self, node: nodes.Break, frame: Frame) -> None: + self.writeline("break", node) + + def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None: + scope_frame = frame.inner() + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + + def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None: + ctx = self.temporary_identifier() + self.writeline(f"{ctx} = {self.derive_context(frame)}") + self.writeline(f"{ctx}.vars = ") + self.visit(node.context, frame) + self.push_context_reference(ctx) + + scope_frame = frame.inner(isolated=True) + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + self.pop_context_reference() + + def visit_EvalContextModifier( + self, node: nodes.EvalContextModifier, frame: Frame + ) -> None: + for keyword in node.options: + self.writeline(f"context.eval_ctx.{keyword.key} = ") + self.visit(keyword.value, frame) + try: + val = keyword.value.as_const(frame.eval_ctx) + except nodes.Impossible: + frame.eval_ctx.volatile = True + else: + setattr(frame.eval_ctx, keyword.key, val) + + def visit_ScopedEvalContextModifier( + self, node: nodes.ScopedEvalContextModifier, frame: Frame + ) -> None: + old_ctx_name = self.temporary_identifier() + saved_ctx = frame.eval_ctx.save() + self.writeline(f"{old_ctx_name} = context.eval_ctx.save()") + self.visit_EvalContextModifier(node, frame) + for child in node.body: + self.visit(child, frame) + frame.eval_ctx.revert(saved_ctx) + self.writeline(f"context.eval_ctx.revert({old_ctx_name})") diff --git a/venv/Lib/site-packages/jinja2/constants.py b/venv/Lib/site-packages/jinja2/constants.py new file mode 100644 index 0000000..41a1c23 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/constants.py @@ -0,0 +1,20 @@ +#: list of lorem ipsum words used by the lipsum() helper function +LOREM_IPSUM_WORDS = """\ +a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at +auctor augue bibendum blandit class commodo condimentum congue consectetuer +consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus +diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend +elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames +faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac +hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum +justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem +luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie +mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non +nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque +penatibus per pharetra phasellus placerat platea porta porttitor posuere +potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus +ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit +sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor +tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices +ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus +viverra volutpat vulputate""" diff --git a/venv/Lib/site-packages/jinja2/debug.py b/venv/Lib/site-packages/jinja2/debug.py new file mode 100644 index 0000000..eeeeee7 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/debug.py @@ -0,0 +1,191 @@ +import sys +import typing as t +from types import CodeType +from types import TracebackType + +from .exceptions import TemplateSyntaxError +from .utils import internal_code +from .utils import missing + +if t.TYPE_CHECKING: + from .runtime import Context + + +def rewrite_traceback_stack(source: t.Optional[str] = None) -> BaseException: + """Rewrite the current exception to replace any tracebacks from + within compiled template code with tracebacks that look like they + came from the template source. + + This must be called within an ``except`` block. + + :param source: For ``TemplateSyntaxError``, the original source if + known. + :return: The original exception with the rewritten traceback. + """ + _, exc_value, tb = sys.exc_info() + exc_value = t.cast(BaseException, exc_value) + tb = t.cast(TracebackType, tb) + + if isinstance(exc_value, TemplateSyntaxError) and not exc_value.translated: + exc_value.translated = True + exc_value.source = source + # Remove the old traceback, otherwise the frames from the + # compiler still show up. + exc_value.with_traceback(None) + # Outside of runtime, so the frame isn't executing template + # code, but it still needs to point at the template. + tb = fake_traceback( + exc_value, None, exc_value.filename or "", exc_value.lineno + ) + else: + # Skip the frame for the render function. + tb = tb.tb_next + + stack = [] + + # Build the stack of traceback object, replacing any in template + # code with the source file and line information. + while tb is not None: + # Skip frames decorated with @internalcode. These are internal + # calls that aren't useful in template debugging output. + if tb.tb_frame.f_code in internal_code: + tb = tb.tb_next + continue + + template = tb.tb_frame.f_globals.get("__jinja_template__") + + if template is not None: + lineno = template.get_corresponding_lineno(tb.tb_lineno) + fake_tb = fake_traceback(exc_value, tb, template.filename, lineno) + stack.append(fake_tb) + else: + stack.append(tb) + + tb = tb.tb_next + + tb_next = None + + # Assign tb_next in reverse to avoid circular references. + for tb in reversed(stack): + tb.tb_next = tb_next + tb_next = tb + + return exc_value.with_traceback(tb_next) + + +def fake_traceback( # type: ignore + exc_value: BaseException, tb: t.Optional[TracebackType], filename: str, lineno: int +) -> TracebackType: + """Produce a new traceback object that looks like it came from the + template source instead of the compiled code. The filename, line + number, and location name will point to the template, and the local + variables will be the current template context. + + :param exc_value: The original exception to be re-raised to create + the new traceback. + :param tb: The original traceback to get the local variables and + code info from. + :param filename: The template filename. + :param lineno: The line number in the template source. + """ + if tb is not None: + # Replace the real locals with the context that would be + # available at that point in the template. + locals = get_template_locals(tb.tb_frame.f_locals) + locals.pop("__jinja_exception__", None) + else: + locals = {} + + globals = { + "__name__": filename, + "__file__": filename, + "__jinja_exception__": exc_value, + } + # Raise an exception at the correct line number. + code: CodeType = compile( + "\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec" + ) + + # Build a new code object that points to the template file and + # replaces the location with a block name. + location = "template" + + if tb is not None: + function = tb.tb_frame.f_code.co_name + + if function == "root": + location = "top-level template code" + elif function.startswith("block_"): + location = f"block {function[6:]!r}" + + if sys.version_info >= (3, 8): + code = code.replace(co_name=location) + else: + code = CodeType( + code.co_argcount, + code.co_kwonlyargcount, + code.co_nlocals, + code.co_stacksize, + code.co_flags, + code.co_code, + code.co_consts, + code.co_names, + code.co_varnames, + code.co_filename, + location, + code.co_firstlineno, + code.co_lnotab, + code.co_freevars, + code.co_cellvars, + ) + + # Execute the new code, which is guaranteed to raise, and return + # the new traceback without this frame. + try: + exec(code, globals, locals) + except BaseException: + return sys.exc_info()[2].tb_next # type: ignore + + +def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any]: + """Based on the runtime locals, get the context that would be + available at that point in the template. + """ + # Start with the current template context. + ctx: t.Optional[Context] = real_locals.get("context") + + if ctx is not None: + data: t.Dict[str, t.Any] = ctx.get_all().copy() + else: + data = {} + + # Might be in a derived context that only sets local variables + # rather than pushing a context. Local variables follow the scheme + # l_depth_name. Find the highest-depth local that has a value for + # each name. + local_overrides: t.Dict[str, t.Tuple[int, t.Any]] = {} + + for name, value in real_locals.items(): + if not name.startswith("l_") or value is missing: + # Not a template variable, or no longer relevant. + continue + + try: + _, depth_str, name = name.split("_", 2) + depth = int(depth_str) + except ValueError: + continue + + cur_depth = local_overrides.get(name, (-1,))[0] + + if cur_depth < depth: + local_overrides[name] = (depth, value) + + # Modify the context with any derived context. + for name, (_, value) in local_overrides.items(): + if value is missing: + data.pop(name, None) + else: + data[name] = value + + return data diff --git a/venv/Lib/site-packages/jinja2/defaults.py b/venv/Lib/site-packages/jinja2/defaults.py new file mode 100644 index 0000000..638cad3 --- /dev/null +++ b/venv/Lib/site-packages/jinja2/defaults.py @@ -0,0 +1,48 @@ +import typing as t + +from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401 +from .tests import TESTS as DEFAULT_TESTS # noqa: F401 +from .utils import Cycler +from .utils import generate_lorem_ipsum +from .utils import Joiner +from .utils import Namespace + +if t.TYPE_CHECKING: + import typing_extensions as te + +# defaults for the parser / lexer +BLOCK_START_STRING = "{%" +BLOCK_END_STRING = "%}" +VARIABLE_START_STRING = "{{" +VARIABLE_END_STRING = "}}" +COMMENT_START_STRING = "{#" +COMMENT_END_STRING = "#}" +LINE_STATEMENT_PREFIX: t.Optional[str] = None +LINE_COMMENT_PREFIX: t.Optional[str] = None +TRIM_BLOCKS = False +LSTRIP_BLOCKS = False +NEWLINE_SEQUENCE: "te.Literal['\\n', '\\r\\n', '\\r']" = "\n" +KEEP_TRAILING_NEWLINE = False + +# default filters, tests and namespace + +DEFAULT_NAMESPACE = { + "range": range, + "dict": dict, + "lipsum": generate_lorem_ipsum, + "cycler": Cycler, + "joiner": Joiner, + "namespace": Namespace, +} + +# default policies +DEFAULT_POLICIES: t.Dict[str, t.Any] = { + "compiler.ascii_str": True, + "urlize.rel": "noopener", + "urlize.target": None, + "urlize.extra_schemes": None, + "truncate.leeway": 5, + "json.dumps_function": None, + "json.dumps_kwargs": {"sort_keys": True}, + "ext.i18n.trimmed": False, +} diff --git a/venv/Lib/site-packages/jinja2/environment.py b/venv/Lib/site-packages/jinja2/environment.py new file mode 100644 index 0000000..0fc6e5b --- /dev/null +++ b/venv/Lib/site-packages/jinja2/environment.py @@ -0,0 +1,1672 @@ +"""Classes for managing templates and their runtime and compile time +options. +""" + +import os +import typing +import typing as t +import weakref +from collections import ChainMap +from functools import lru_cache +from functools import partial +from functools import reduce +from types import CodeType + +from markupsafe import Markup + +from . import nodes +from .compiler import CodeGenerator +from .compiler import generate +from .defaults import BLOCK_END_STRING +from .defaults import BLOCK_START_STRING +from .defaults import COMMENT_END_STRING +from .defaults import COMMENT_START_STRING +from .defaults import DEFAULT_FILTERS # type: ignore[attr-defined] +from .defaults import DEFAULT_NAMESPACE +from .defaults import DEFAULT_POLICIES +from .defaults import DEFAULT_TESTS # type: ignore[attr-defined] +from .defaults import KEEP_TRAILING_NEWLINE +from .defaults import LINE_COMMENT_PREFIX +from .defaults import LINE_STATEMENT_PREFIX +from .defaults import LSTRIP_BLOCKS +from .defaults import NEWLINE_SEQUENCE +from .defaults import TRIM_BLOCKS +from .defaults import VARIABLE_END_STRING +from .defaults import VARIABLE_START_STRING +from .exceptions import TemplateNotFound +from .exceptions import TemplateRuntimeError +from .exceptions import TemplatesNotFound +from .exceptions import TemplateSyntaxError +from .exceptions import UndefinedError +from .lexer import get_lexer +from .lexer import Lexer +from .lexer import TokenStream +from .nodes import EvalContext +from .parser import Parser +from .runtime import Context +from .runtime import new_context +from .runtime import Undefined +from .utils import _PassArg +from .utils import concat +from .utils import consume +from .utils import import_string +from .utils import internalcode +from .utils import LRUCache +from .utils import missing + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .bccache import BytecodeCache + from .ext import Extension + from .loaders import BaseLoader + +_env_bound = t.TypeVar("_env_bound", bound="Environment") + + +# for direct template usage we have up to ten living environments +@lru_cache(maxsize=10) +def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound: + """Return a new spontaneous environment. A spontaneous environment + is used for templates created directly rather than through an + existing environment. + + :param cls: Environment class to create. + :param args: Positional arguments passed to environment. + """ + env = cls(*args) + env.shared = True + return env + + +def create_cache( + size: int, +) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]: + """Return the cache class for the given size.""" + if size == 0: + return None + + if size < 0: + return {} + + return LRUCache(size) # type: ignore + + +def copy_cache( + cache: t.Optional[t.MutableMapping[t.Any, t.Any]], +) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]: + """Create an empty copy of the given cache.""" + if cache is None: + return None + + if type(cache) is dict: # noqa E721 + return {} + + return LRUCache(cache.capacity) # type: ignore + + +def load_extensions( + environment: "Environment", + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]], +) -> t.Dict[str, "Extension"]: + """Load the extensions from the list and bind it to the environment. + Returns a dict of instantiated extensions. + """ + result = {} + + for extension in extensions: + if isinstance(extension, str): + extension = t.cast(t.Type["Extension"], import_string(extension)) + + result[extension.identifier] = extension(environment) + + return result + + +def _environment_config_check(environment: _env_bound) -> _env_bound: + """Perform a sanity check on the environment.""" + assert issubclass( + environment.undefined, Undefined + ), "'undefined' must be a subclass of 'jinja2.Undefined'." + assert ( + environment.block_start_string + != environment.variable_start_string + != environment.comment_start_string + ), "block, variable and comment start strings must be different." + assert environment.newline_sequence in { + "\r", + "\r\n", + "\n", + }, "'newline_sequence' must be one of '\\n', '\\r\\n', or '\\r'." + return environment + + +class Environment: + r"""The core component of Jinja is the `Environment`. It contains + important shared variables like configuration, filters, tests, + globals and others. Instances of this class may be modified if + they are not shared and if no template was loaded so far. + Modifications on environments after the first template was loaded + will lead to surprising effects and undefined behavior. + + Here are the possible initialization parameters: + + `block_start_string` + The string marking the beginning of a block. Defaults to ``'{%'``. + + `block_end_string` + The string marking the end of a block. Defaults to ``'%}'``. + + `variable_start_string` + The string marking the beginning of a print statement. + Defaults to ``'{{'``. + + `variable_end_string` + The string marking the end of a print statement. Defaults to + ``'}}'``. + + `comment_start_string` + The string marking the beginning of a comment. Defaults to ``'{#'``. + + `comment_end_string` + The string marking the end of a comment. Defaults to ``'#}'``. + + `line_statement_prefix` + If given and a string, this will be used as prefix for line based + statements. See also :ref:`line-statements`. + + `line_comment_prefix` + If given and a string, this will be used as prefix for line based + comments. See also :ref:`line-statements`. + + .. versionadded:: 2.2 + + `trim_blocks` + If this is set to ``True`` the first newline after a block is + removed (block, not variable tag!). Defaults to `False`. + + `lstrip_blocks` + If this is set to ``True`` leading spaces and tabs are stripped + from the start of a line to a block. Defaults to `False`. + + `newline_sequence` + The sequence that starts a newline. Must be one of ``'\r'``, + ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a + useful default for Linux and OS X systems as well as web + applications. + + `keep_trailing_newline` + Preserve the trailing newline when rendering templates. + The default is ``False``, which causes a single newline, + if present, to be stripped from the end of the template. + + .. versionadded:: 2.7 + + `extensions` + List of Jinja extensions to use. This can either be import paths + as strings or extension classes. For more information have a + look at :ref:`the extensions documentation `. + + `optimized` + should the optimizer be enabled? Default is ``True``. + + `undefined` + :class:`Undefined` or a subclass of it that is used to represent + undefined values in the template. + + `finalize` + A callable that can be used to process the result of a variable + expression before it is output. For example one can convert + ``None`` implicitly into an empty string here. + + `autoescape` + If set to ``True`` the XML/HTML autoescaping feature is enabled by + default. For more details about autoescaping see + :class:`~markupsafe.Markup`. As of Jinja 2.4 this can also + be a callable that is passed the template name and has to + return ``True`` or ``False`` depending on autoescape should be + enabled by default. + + .. versionchanged:: 2.4 + `autoescape` can now be a function + + `loader` + The template loader for this environment. + + `cache_size` + The size of the cache. Per default this is ``400`` which means + that if more than 400 templates are loaded the loader will clean + out the least recently used template. If the cache size is set to + ``0`` templates are recompiled all the time, if the cache size is + ``-1`` the cache will not be cleaned. + + .. versionchanged:: 2.8 + The cache size was increased to 400 from a low 50. + + `auto_reload` + Some loaders load templates from locations where the template + sources may change (ie: file system or database). If + ``auto_reload`` is set to ``True`` (default) every time a template is + requested the loader checks if the source changed and if yes, it + will reload the template. For higher performance it's possible to + disable that. + + `bytecode_cache` + If set to a bytecode cache object, this object will provide a + cache for the internal Jinja bytecode so that templates don't + have to be parsed if they were not changed. + + See :ref:`bytecode-cache` for more information. + + `enable_async` + If set to true this enables async template execution which + allows using async functions and generators. + """ + + #: if this environment is sandboxed. Modifying this variable won't make + #: the environment sandboxed though. For a real sandboxed environment + #: have a look at jinja2.sandbox. This flag alone controls the code + #: generation by the compiler. + sandboxed = False + + #: True if the environment is just an overlay + overlayed = False + + #: the environment this environment is linked to if it is an overlay + linked_to: t.Optional["Environment"] = None + + #: shared environments have this set to `True`. A shared environment + #: must not be modified + shared = False + + #: the class that is used for code generation. See + #: :class:`~jinja2.compiler.CodeGenerator` for more information. + code_generator_class: t.Type["CodeGenerator"] = CodeGenerator + + concat = "".join + + #: the context class that is used for templates. See + #: :class:`~jinja2.runtime.Context` for more information. + context_class: t.Type[Context] = Context + + template_class: t.Type["Template"] + + def __init__( + self, + block_start_string: str = BLOCK_START_STRING, + block_end_string: str = BLOCK_END_STRING, + variable_start_string: str = VARIABLE_START_STRING, + variable_end_string: str = VARIABLE_END_STRING, + comment_start_string: str = COMMENT_START_STRING, + comment_end_string: str = COMMENT_END_STRING, + line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX, + line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX, + trim_blocks: bool = TRIM_BLOCKS, + lstrip_blocks: bool = LSTRIP_BLOCKS, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE, + keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (), + optimized: bool = True, + undefined: t.Type[Undefined] = Undefined, + finalize: t.Optional[t.Callable[..., t.Any]] = None, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False, + loader: t.Optional["BaseLoader"] = None, + cache_size: int = 400, + auto_reload: bool = True, + bytecode_cache: t.Optional["BytecodeCache"] = None, + enable_async: bool = False, + ): + # !!Important notice!! + # The constructor accepts quite a few arguments that should be + # passed by keyword rather than position. However it's important to + # not change the order of arguments because it's used at least + # internally in those cases: + # - spontaneous environments (i18n extension and Template) + # - unittests + # If parameter changes are required only add parameters at the end + # and don't change the arguments (or the defaults!) of the arguments + # existing already. + + # lexer / parser information + self.block_start_string = block_start_string + self.block_end_string = block_end_string + self.variable_start_string = variable_start_string + self.variable_end_string = variable_end_string + self.comment_start_string = comment_start_string + self.comment_end_string = comment_end_string + self.line_statement_prefix = line_statement_prefix + self.line_comment_prefix = line_comment_prefix + self.trim_blocks = trim_blocks + self.lstrip_blocks = lstrip_blocks + self.newline_sequence = newline_sequence + self.keep_trailing_newline = keep_trailing_newline + + # runtime information + self.undefined: t.Type[Undefined] = undefined + self.optimized = optimized + self.finalize = finalize + self.autoescape = autoescape + + # defaults + self.filters = DEFAULT_FILTERS.copy() + self.tests = DEFAULT_TESTS.copy() + self.globals = DEFAULT_NAMESPACE.copy() + + # set the loader provided + self.loader = loader + self.cache = create_cache(cache_size) + self.bytecode_cache = bytecode_cache + self.auto_reload = auto_reload + + # configurable policies + self.policies = DEFAULT_POLICIES.copy() + + # load extensions + self.extensions = load_extensions(self, extensions) + + self.is_async = enable_async + _environment_config_check(self) + + def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None: + """Adds an extension after the environment was created. + + .. versionadded:: 2.5 + """ + self.extensions.update(load_extensions(self, [extension])) + + def extend(self, **attributes: t.Any) -> None: + """Add the items to the instance of the environment if they do not exist + yet. This is used by :ref:`extensions ` to register + callbacks and configuration values without breaking inheritance. + """ + for key, value in attributes.items(): + if not hasattr(self, key): + setattr(self, key, value) + + def overlay( + self, + block_start_string: str = missing, + block_end_string: str = missing, + variable_start_string: str = missing, + variable_end_string: str = missing, + comment_start_string: str = missing, + comment_end_string: str = missing, + line_statement_prefix: t.Optional[str] = missing, + line_comment_prefix: t.Optional[str] = missing, + trim_blocks: bool = missing, + lstrip_blocks: bool = missing, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing, + keep_trailing_newline: bool = missing, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing, + optimized: bool = missing, + undefined: t.Type[Undefined] = missing, + finalize: t.Optional[t.Callable[..., t.Any]] = missing, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing, + loader: t.Optional["BaseLoader"] = missing, + cache_size: int = missing, + auto_reload: bool = missing, + bytecode_cache: t.Optional["BytecodeCache"] = missing, + enable_async: bool = missing, + ) -> "te.Self": + """Create a new overlay environment that shares all the data with the + current environment except for cache and the overridden attributes. + Extensions cannot be removed for an overlayed environment. An overlayed + environment automatically gets all the extensions of the environment it + is linked to plus optional extra extensions. + + Creating overlays should happen after the initial environment was set + up completely. Not all attributes are truly linked, some are just + copied over so modifications on the original environment may not shine + through. + + .. versionchanged:: 3.1.5 + ``enable_async`` is applied correctly. + + .. versionchanged:: 3.1.2 + Added the ``newline_sequence``, ``keep_trailing_newline``, + and ``enable_async`` parameters to match ``__init__``. + """ + args = dict(locals()) + del args["self"], args["cache_size"], args["extensions"], args["enable_async"] + + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.overlayed = True + rv.linked_to = self + + for key, value in args.items(): + if value is not missing: + setattr(rv, key, value) + + if cache_size is not missing: + rv.cache = create_cache(cache_size) + else: + rv.cache = copy_cache(self.cache) + + rv.extensions = {} + for key, value in self.extensions.items(): + rv.extensions[key] = value.bind(rv) + if extensions is not missing: + rv.extensions.update(load_extensions(rv, extensions)) + + if enable_async is not missing: + rv.is_async = enable_async + + return _environment_config_check(rv) + + @property + def lexer(self) -> Lexer: + """The lexer for this environment.""" + return get_lexer(self) + + def iter_extensions(self) -> t.Iterator["Extension"]: + """Iterates over the extensions by priority.""" + return iter(sorted(self.extensions.values(), key=lambda x: x.priority)) + + def getitem( + self, obj: t.Any, argument: t.Union[str, t.Any] + ) -> t.Union[t.Any, Undefined]: + """Get an item or attribute of an object but prefer the item.""" + try: + return obj[argument] + except (AttributeError, TypeError, LookupError): + if isinstance(argument, str): + try: + attr = str(argument) + except Exception: + pass + else: + try: + return getattr(obj, attr) + except AttributeError: + pass + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj: t.Any, attribute: str) -> t.Any: + """Get an item or attribute of an object but prefer the attribute. + Unlike :meth:`getitem` the attribute *must* be a string. + """ + try: + return getattr(obj, attribute) + except AttributeError: + pass + try: + return obj[attribute] + except (TypeError, LookupError, AttributeError): + return self.undefined(obj=obj, name=attribute) + + def _filter_test_common( + self, + name: t.Union[str, Undefined], + value: t.Any, + args: t.Optional[t.Sequence[t.Any]], + kwargs: t.Optional[t.Mapping[str, t.Any]], + context: t.Optional[Context], + eval_ctx: t.Optional[EvalContext], + is_filter: bool, + ) -> t.Any: + if is_filter: + env_map = self.filters + type_name = "filter" + else: + env_map = self.tests + type_name = "test" + + func = env_map.get(name) # type: ignore + + if func is None: + msg = f"No {type_name} named {name!r}." + + if isinstance(name, Undefined): + try: + name._fail_with_undefined_error() + except Exception as e: + msg = f"{msg} ({e}; did you forget to quote the callable name?)" + + raise TemplateRuntimeError(msg) + + args = [value, *(args if args is not None else ())] + kwargs = kwargs if kwargs is not None else {} + pass_arg = _PassArg.from_obj(func) + + if pass_arg is _PassArg.context: + if context is None: + raise TemplateRuntimeError( + f"Attempted to invoke a context {type_name} without context." + ) + + args.insert(0, context) + elif pass_arg is _PassArg.eval_context: + if eval_ctx is None: + if context is not None: + eval_ctx = context.eval_ctx + else: + eval_ctx = EvalContext(self) + + args.insert(0, eval_ctx) + elif pass_arg is _PassArg.environment: + args.insert(0, self) + + return func(*args, **kwargs) + + def call_filter( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a filter on a value the same way the compiler does. + + This might return a coroutine if the filter is running from an + environment in async mode and the filter supports async + execution. It's your responsibility to await this if needed. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, True + ) + + def call_test( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a test on a value the same way the compiler does. + + This might return a coroutine if the test is running from an + environment in async mode and the test supports async execution. + It's your responsibility to await this if needed. + + .. versionchanged:: 3.0 + Tests support ``@pass_context``, etc. decorators. Added + the ``context`` and ``eval_ctx`` parameters. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, False + ) + + @internalcode + def parse( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> nodes.Template: + """Parse the sourcecode and return the abstract syntax tree. This + tree of nodes is used by the compiler to convert the template into + executable source- or bytecode. This is useful for debugging or to + extract information from templates. + + If you are :ref:`developing Jinja extensions ` + this gives you a good overview of the node tree generated. + """ + try: + return self._parse(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def _parse( + self, source: str, name: t.Optional[str], filename: t.Optional[str] + ) -> nodes.Template: + """Internal parsing function used by `parse` and `compile`.""" + return Parser(self, source, name, filename).parse() + + def lex( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> t.Iterator[t.Tuple[int, str, str]]: + """Lex the given sourcecode and return a generator that yields + tokens as tuples in the form ``(lineno, token_type, value)``. + This can be useful for :ref:`extension development ` + and debugging templates. + + This does not perform preprocessing. If you want the preprocessing + of the extensions to be applied you have to filter source through + the :meth:`preprocess` method. + """ + source = str(source) + try: + return self.lexer.tokeniter(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def preprocess( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> str: + """Preprocesses the source with all extensions. This is automatically + called for all parsing and compiling methods but *not* for :meth:`lex` + because there you usually only want the actual source tokenized. + """ + return reduce( + lambda s, e: e.preprocess(s, name, filename), + self.iter_extensions(), + str(source), + ) + + def _tokenize( + self, + source: str, + name: t.Optional[str], + filename: t.Optional[str] = None, + state: t.Optional[str] = None, + ) -> TokenStream: + """Called by the parser to do the preprocessing and filtering + for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. + """ + source = self.preprocess(source, name, filename) + stream = self.lexer.tokenize(source, name, filename, state) + + for ext in self.iter_extensions(): + stream = ext.filter_stream(stream) # type: ignore + + if not isinstance(stream, TokenStream): + stream = TokenStream(stream, name, filename) + + return stream + + def _generate( + self, + source: nodes.Template, + name: t.Optional[str], + filename: t.Optional[str], + defer_init: bool = False, + ) -> str: + """Internal hook that can be overridden to hook a different generate + method in. + + .. versionadded:: 2.5 + """ + return generate( # type: ignore + source, + self, + name, + filename, + defer_init=defer_init, + optimized=self.optimized, + ) + + def _compile(self, source: str, filename: str) -> CodeType: + """Internal hook that can be overridden to hook a different compile + method in. + + .. versionadded:: 2.5 + """ + return compile(source, filename, "exec") + + @typing.overload + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[False]" = False, + defer_init: bool = False, + ) -> CodeType: ... + + @typing.overload + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[True]" = ..., + defer_init: bool = False, + ) -> str: ... + + @internalcode + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: bool = False, + defer_init: bool = False, + ) -> t.Union[str, CodeType]: + """Compile a node or template source code. The `name` parameter is + the load name of the template after it was joined using + :meth:`join_path` if necessary, not the filename on the file system. + the `filename` parameter is the estimated filename of the template on + the file system. If the template came from a database or memory this + can be omitted. + + The return value of this method is a python code object. If the `raw` + parameter is `True` the return value will be a string with python + code equivalent to the bytecode returned otherwise. This method is + mainly used internally. + + `defer_init` is use internally to aid the module code generator. This + causes the generated code to be able to import without the global + environment variable to be set. + + .. versionadded:: 2.4 + `defer_init` parameter added. + """ + source_hint = None + try: + if isinstance(source, str): + source_hint = source + source = self._parse(source, name, filename) + source = self._generate(source, name, filename, defer_init=defer_init) + if raw: + return source + if filename is None: + filename = "