From a2f543c2d5094a8a9fbe5f50479d49527a16d6f7 Mon Sep 17 00:00:00 2001 From: Celestino Rey Date: Fri, 10 May 2024 14:04:21 +0200 Subject: [PATCH] =?UTF-8?q?Implementaci=C3=B3n=20de=20la=20autorizaci?= =?UTF-8?q?=C3=B3n=20de=20usuarios.=20Crear,=20entrar=20y=20salir.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Padel/padel/__init__.py | 33 +- Padel/padel/auth.py | 70 + Padel/padel/models.py | 9 + Padel/padel/paginas.py | 6 +- Padel/padel/reservas.py | 5 +- Padel/padel/schema.sql | 26 +- Padel/padel/templates/_navegacion.html | 30 +- Padel/padel/templates/autorizacion/index.html | 10 + Padel/padel/templates/autorizacion/login.html | 36 + .../padel/templates/autorizacion/profile.html | 7 + .../padel/templates/autorizacion/signup.html | 39 + Padel/padel/templates/base.html | 21 +- .../padel/templates/reservas/misreservas.html | 5 +- flask_auth_app/authenv/bin/Activate.ps1 | 247 + flask_auth_app/authenv/bin/activate | 69 + flask_auth_app/authenv/bin/activate.csh | 26 + flask_auth_app/authenv/bin/activate.fish | 69 + flask_auth_app/authenv/bin/dotenv | 8 + flask_auth_app/authenv/bin/flask | 8 + flask_auth_app/authenv/bin/pip | 8 + flask_auth_app/authenv/bin/pip3 | 8 + flask_auth_app/authenv/bin/pip3.10 | 8 + flask_auth_app/authenv/bin/python | 1 + flask_auth_app/authenv/bin/python3 | 1 + flask_auth_app/authenv/bin/python3.10 | 1 + .../site/python3.10/greenlet/greenlet.h | 164 + .../Flask_Login-0.6.3.dist-info/INSTALLER | 1 + .../Flask_Login-0.6.3.dist-info/LICENSE | 22 + .../Flask_Login-0.6.3.dist-info/METADATA | 183 + .../Flask_Login-0.6.3.dist-info/RECORD | 23 + .../Flask_Login-0.6.3.dist-info/REQUESTED | 0 .../Flask_Login-0.6.3.dist-info/WHEEL | 5 + .../Flask_Login-0.6.3.dist-info/top_level.txt | 1 + .../MarkupSafe-2.1.5.dist-info/INSTALLER | 1 + .../MarkupSafe-2.1.5.dist-info/LICENSE.rst | 28 + .../MarkupSafe-2.1.5.dist-info/METADATA | 93 + .../MarkupSafe-2.1.5.dist-info/RECORD | 14 + .../MarkupSafe-2.1.5.dist-info/WHEEL | 6 + .../MarkupSafe-2.1.5.dist-info/top_level.txt | 1 + .../SQLAlchemy-2.0.30.dist-info/INSTALLER | 1 + .../SQLAlchemy-2.0.30.dist-info/LICENSE | 19 + .../SQLAlchemy-2.0.30.dist-info/METADATA | 242 + .../SQLAlchemy-2.0.30.dist-info/RECORD | 529 + .../SQLAlchemy-2.0.30.dist-info/WHEEL | 6 + .../SQLAlchemy-2.0.30.dist-info/top_level.txt | 1 + .../site-packages/_distutils_hack/__init__.py | 132 + .../site-packages/_distutils_hack/override.py | 1 + .../blinker-1.8.2.dist-info/INSTALLER | 1 + .../blinker-1.8.2.dist-info/LICENSE.txt | 20 + .../blinker-1.8.2.dist-info/METADATA | 60 + .../blinker-1.8.2.dist-info/RECORD | 12 + .../blinker-1.8.2.dist-info/WHEEL | 4 + .../site-packages/blinker/__init__.py | 60 + .../site-packages/blinker/_utilities.py | 64 + .../python3.10/site-packages/blinker/base.py | 621 ++ .../python3.10/site-packages/blinker/py.typed | 0 .../click-8.1.7.dist-info/INSTALLER | 1 + .../click-8.1.7.dist-info/LICENSE.rst | 28 + .../click-8.1.7.dist-info/METADATA | 103 + .../click-8.1.7.dist-info/RECORD | 39 + .../site-packages/click-8.1.7.dist-info/WHEEL | 5 + .../click-8.1.7.dist-info/top_level.txt | 1 + .../site-packages/click/__init__.py | 73 + .../python3.10/site-packages/click/_compat.py | 623 ++ .../site-packages/click/_termui_impl.py | 739 ++ .../site-packages/click/_textwrap.py | 49 + .../site-packages/click/_winconsole.py | 279 + .../python3.10/site-packages/click/core.py | 3042 ++++++ .../site-packages/click/decorators.py | 561 ++ .../site-packages/click/exceptions.py | 288 + .../site-packages/click/formatting.py | 301 + .../python3.10/site-packages/click/globals.py | 68 + .../python3.10/site-packages/click/parser.py | 529 + .../python3.10/site-packages/click/py.typed | 0 .../site-packages/click/shell_completion.py | 596 ++ .../python3.10/site-packages/click/termui.py | 784 ++ .../python3.10/site-packages/click/testing.py | 479 + .../python3.10/site-packages/click/types.py | 1089 +++ .../python3.10/site-packages/click/utils.py | 624 ++ .../site-packages/distutils-precedence.pth | 1 + .../site-packages/dotenv/__init__.py | 49 + .../site-packages/dotenv/__main__.py | 6 + .../python3.10/site-packages/dotenv/cli.py | 199 + .../site-packages/dotenv/ipython.py | 39 + .../python3.10/site-packages/dotenv/main.py | 392 + .../python3.10/site-packages/dotenv/parser.py | 175 + .../python3.10/site-packages/dotenv/py.typed | 1 + .../site-packages/dotenv/variables.py | 86 + .../site-packages/dotenv/version.py | 1 + .../flask-3.0.3.dist-info/INSTALLER | 1 + .../flask-3.0.3.dist-info/LICENSE.txt | 28 + .../flask-3.0.3.dist-info/METADATA | 101 + .../flask-3.0.3.dist-info/RECORD | 58 + .../flask-3.0.3.dist-info/REQUESTED | 0 .../site-packages/flask-3.0.3.dist-info/WHEEL | 4 + .../flask-3.0.3.dist-info/entry_points.txt | 3 + .../site-packages/flask/__init__.py | 60 + .../site-packages/flask/__main__.py | 3 + .../lib/python3.10/site-packages/flask/app.py | 1498 +++ .../site-packages/flask/blueprints.py | 129 + .../lib/python3.10/site-packages/flask/cli.py | 1109 +++ .../python3.10/site-packages/flask/config.py | 370 + .../lib/python3.10/site-packages/flask/ctx.py | 449 + .../site-packages/flask/debughelpers.py | 178 + .../python3.10/site-packages/flask/globals.py | 51 + .../python3.10/site-packages/flask/helpers.py | 621 ++ .../site-packages/flask/json/__init__.py | 170 + .../site-packages/flask/json/provider.py | 215 + .../site-packages/flask/json/tag.py | 327 + .../python3.10/site-packages/flask/logging.py | 79 + .../python3.10/site-packages/flask/py.typed | 0 .../site-packages/flask/sansio/README.md | 6 + .../site-packages/flask/sansio/app.py | 964 ++ .../site-packages/flask/sansio/blueprints.py | 632 ++ .../site-packages/flask/sansio/scaffold.py | 801 ++ .../site-packages/flask/sessions.py | 379 + .../python3.10/site-packages/flask/signals.py | 17 + .../site-packages/flask/templating.py | 219 + .../python3.10/site-packages/flask/testing.py | 298 + .../python3.10/site-packages/flask/typing.py | 90 + .../python3.10/site-packages/flask/views.py | 191 + .../site-packages/flask/wrappers.py | 174 + .../site-packages/flask_login/__about__.py | 10 + .../site-packages/flask_login/__init__.py | 94 + .../site-packages/flask_login/config.py | 55 + .../flask_login/login_manager.py | 543 ++ .../site-packages/flask_login/mixins.py | 65 + .../site-packages/flask_login/signals.py | 61 + .../site-packages/flask_login/test_client.py | 19 + .../site-packages/flask_login/utils.py | 415 + .../INSTALLER | 1 + .../LICENSE.rst | 28 + .../flask_sqlalchemy-3.1.1.dist-info/METADATA | 109 + .../flask_sqlalchemy-3.1.1.dist-info/RECORD | 27 + .../REQUESTED | 0 .../flask_sqlalchemy-3.1.1.dist-info/WHEEL | 4 + .../flask_sqlalchemy/__init__.py | 26 + .../site-packages/flask_sqlalchemy/cli.py | 16 + .../flask_sqlalchemy/extension.py | 1008 ++ .../site-packages/flask_sqlalchemy/model.py | 330 + .../flask_sqlalchemy/pagination.py | 364 + .../site-packages/flask_sqlalchemy/py.typed | 0 .../site-packages/flask_sqlalchemy/query.py | 105 + .../flask_sqlalchemy/record_queries.py | 117 + .../site-packages/flask_sqlalchemy/session.py | 111 + .../site-packages/flask_sqlalchemy/table.py | 39 + .../flask_sqlalchemy/track_modifications.py | 88 + .../greenlet-3.0.3.dist-info/AUTHORS | 51 + .../greenlet-3.0.3.dist-info/INSTALLER | 1 + .../greenlet-3.0.3.dist-info/LICENSE | 30 + .../greenlet-3.0.3.dist-info/LICENSE.PSF | 47 + .../greenlet-3.0.3.dist-info/METADATA | 102 + .../greenlet-3.0.3.dist-info/RECORD | 116 + .../greenlet-3.0.3.dist-info/WHEEL | 6 + .../greenlet-3.0.3.dist-info/top_level.txt | 1 + .../greenlet/TBrokenGreenlet.cpp | 45 + .../greenlet/TExceptionState.cpp | 62 + .../site-packages/greenlet/TGreenlet.cpp | 714 ++ .../greenlet/TGreenletGlobals.cpp | 94 + .../site-packages/greenlet/TMainGreenlet.cpp | 155 + .../site-packages/greenlet/TPythonState.cpp | 375 + .../site-packages/greenlet/TStackState.cpp | 265 + .../greenlet/TThreadStateDestroy.cpp | 195 + .../site-packages/greenlet/TUserGreenlet.cpp | 667 ++ .../site-packages/greenlet/__init__.py | 71 + .../_greenlet.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 1498632 bytes .../site-packages/greenlet/greenlet.cpp | 1494 +++ .../site-packages/greenlet/greenlet.h | 164 + .../greenlet/greenlet_allocator.hpp | 63 + .../greenlet/greenlet_compiler_compat.hpp | 95 + .../greenlet/greenlet_cpython_add_pending.hpp | 172 + .../greenlet/greenlet_cpython_compat.hpp | 127 + .../greenlet/greenlet_exceptions.hpp | 150 + .../greenlet/greenlet_greenlet.hpp | 805 ++ .../greenlet/greenlet_internal.hpp | 106 + .../site-packages/greenlet/greenlet_refs.hpp | 1100 +++ .../greenlet/greenlet_slp_switch.hpp | 99 + .../greenlet/greenlet_thread_state.hpp | 543 ++ .../greenlet_thread_state_dict_cleanup.hpp | 118 + .../greenlet/greenlet_thread_support.hpp | 31 + .../greenlet/platform/__init__.py | 0 .../platform/setup_switch_x64_masm.cmd | 2 + .../greenlet/platform/switch_aarch64_gcc.h | 124 + .../greenlet/platform/switch_alpha_unix.h | 30 + .../greenlet/platform/switch_amd64_unix.h | 87 + .../greenlet/platform/switch_arm32_gcc.h | 79 + .../greenlet/platform/switch_arm32_ios.h | 67 + .../greenlet/platform/switch_arm64_masm.asm | 53 + .../greenlet/platform/switch_arm64_masm.obj | Bin 0 -> 746 bytes .../greenlet/platform/switch_arm64_msvc.h | 17 + .../greenlet/platform/switch_csky_gcc.h | 48 + .../platform/switch_loongarch64_linux.h | 31 + .../greenlet/platform/switch_m68k_gcc.h | 38 + .../greenlet/platform/switch_mips_unix.h | 64 + .../greenlet/platform/switch_ppc64_aix.h | 103 + .../greenlet/platform/switch_ppc64_linux.h | 105 + .../greenlet/platform/switch_ppc_aix.h | 87 + .../greenlet/platform/switch_ppc_linux.h | 84 + .../greenlet/platform/switch_ppc_macosx.h | 82 + .../greenlet/platform/switch_ppc_unix.h | 82 + .../greenlet/platform/switch_riscv_unix.h | 32 + .../greenlet/platform/switch_s390_unix.h | 87 + .../greenlet/platform/switch_sparc_sun_gcc.h | 92 + .../greenlet/platform/switch_x32_unix.h | 63 + .../greenlet/platform/switch_x64_masm.asm | 111 + .../greenlet/platform/switch_x64_masm.obj | Bin 0 -> 1078 bytes .../greenlet/platform/switch_x64_msvc.h | 60 + .../greenlet/platform/switch_x86_msvc.h | 326 + .../greenlet/platform/switch_x86_unix.h | 105 + .../greenlet/slp_platformselect.h | 71 + .../site-packages/greenlet/tests/__init__.py | 237 + .../greenlet/tests/_test_extension.c | 231 + ..._extension.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 36200 bytes .../greenlet/tests/_test_extension_cpp.cpp | 226 + ...ension_cpp.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 57264 bytes .../tests/fail_clearing_run_switches.py | 47 + .../greenlet/tests/fail_cpp_exception.py | 33 + .../tests/fail_initialstub_already_started.py | 78 + .../greenlet/tests/fail_slp_switch.py | 29 + .../tests/fail_switch_three_greenlets.py | 44 + .../tests/fail_switch_three_greenlets2.py | 55 + .../tests/fail_switch_two_greenlets.py | 41 + .../site-packages/greenlet/tests/leakcheck.py | 319 + .../greenlet/tests/test_contextvars.py | 310 + .../site-packages/greenlet/tests/test_cpp.py | 73 + .../tests/test_extension_interface.py | 115 + .../site-packages/greenlet/tests/test_gc.py | 86 + .../greenlet/tests/test_generator.py | 59 + .../greenlet/tests/test_generator_nested.py | 168 + .../greenlet/tests/test_greenlet.py | 1311 +++ .../greenlet/tests/test_greenlet_trash.py | 178 + .../greenlet/tests/test_leaks.py | 443 + .../greenlet/tests/test_stack_saved.py | 19 + .../greenlet/tests/test_throw.py | 128 + .../greenlet/tests/test_tracing.py | 291 + .../greenlet/tests/test_version.py | 41 + .../greenlet/tests/test_weakref.py | 35 + .../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 + .../site-packages/itsdangerous/_json.py | 18 + .../site-packages/itsdangerous/encoding.py | 54 + .../site-packages/itsdangerous/exc.py | 106 + .../site-packages/itsdangerous/py.typed | 0 .../site-packages/itsdangerous/serializer.py | 406 + .../site-packages/itsdangerous/signer.py | 266 + .../site-packages/itsdangerous/timed.py | 228 + .../site-packages/itsdangerous/url_safe.py | 83 + .../jinja2-3.1.4.dist-info/INSTALLER | 1 + .../jinja2-3.1.4.dist-info/LICENSE.txt | 28 + .../jinja2-3.1.4.dist-info/METADATA | 76 + .../jinja2-3.1.4.dist-info/RECORD | 57 + .../jinja2-3.1.4.dist-info/WHEEL | 4 + .../jinja2-3.1.4.dist-info/entry_points.txt | 3 + .../site-packages/jinja2/__init__.py | 38 + .../site-packages/jinja2/_identifier.py | 6 + .../site-packages/jinja2/async_utils.py | 84 + .../site-packages/jinja2/bccache.py | 408 + .../site-packages/jinja2/compiler.py | 1960 ++++ .../site-packages/jinja2/constants.py | 20 + .../python3.10/site-packages/jinja2/debug.py | 191 + .../site-packages/jinja2/defaults.py | 48 + .../site-packages/jinja2/environment.py | 1675 ++++ .../site-packages/jinja2/exceptions.py | 166 + .../python3.10/site-packages/jinja2/ext.py | 870 ++ .../site-packages/jinja2/filters.py | 1866 ++++ .../site-packages/jinja2/idtracking.py | 318 + .../python3.10/site-packages/jinja2/lexer.py | 868 ++ .../site-packages/jinja2/loaders.py | 667 ++ .../python3.10/site-packages/jinja2/meta.py | 112 + .../site-packages/jinja2/nativetypes.py | 130 + .../python3.10/site-packages/jinja2/nodes.py | 1206 +++ .../site-packages/jinja2/optimizer.py | 48 + .../python3.10/site-packages/jinja2/parser.py | 1041 ++ .../python3.10/site-packages/jinja2/py.typed | 0 .../site-packages/jinja2/runtime.py | 1056 ++ .../site-packages/jinja2/sandbox.py | 429 + .../python3.10/site-packages/jinja2/tests.py | 256 + .../python3.10/site-packages/jinja2/utils.py | 755 ++ .../site-packages/jinja2/visitor.py | 92 + .../site-packages/markupsafe/__init__.py | 332 + .../site-packages/markupsafe/_native.py | 63 + .../site-packages/markupsafe/_speedups.c | 320 + .../_speedups.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 44240 bytes .../site-packages/markupsafe/_speedups.pyi | 9 + .../site-packages/markupsafe/py.typed | 0 .../pip-22.0.2.dist-info/INSTALLER | 1 + .../pip-22.0.2.dist-info/LICENSE.txt | 20 + .../pip-22.0.2.dist-info/METADATA | 92 + .../site-packages/pip-22.0.2.dist-info/RECORD | 1037 ++ .../pip-22.0.2.dist-info/REQUESTED | 0 .../site-packages/pip-22.0.2.dist-info/WHEEL | 5 + .../pip-22.0.2.dist-info/entry_points.txt | 5 + .../pip-22.0.2.dist-info/top_level.txt | 1 + .../python3.10/site-packages/pip/__init__.py | 13 + .../python3.10/site-packages/pip/__main__.py | 31 + .../site-packages/pip/_internal/__init__.py | 19 + .../site-packages/pip/_internal/build_env.py | 296 + .../site-packages/pip/_internal/cache.py | 264 + .../pip/_internal/cli/__init__.py | 4 + .../pip/_internal/cli/autocompletion.py | 171 + .../pip/_internal/cli/base_command.py | 220 + .../pip/_internal/cli/cmdoptions.py | 1018 ++ .../pip/_internal/cli/command_context.py | 27 + .../site-packages/pip/_internal/cli/main.py | 70 + .../pip/_internal/cli/main_parser.py | 87 + .../site-packages/pip/_internal/cli/parser.py | 292 + .../pip/_internal/cli/progress_bars.py | 321 + .../pip/_internal/cli/req_command.py | 506 + .../pip/_internal/cli/spinners.py | 157 + .../pip/_internal/cli/status_codes.py | 6 + .../pip/_internal/commands/__init__.py | 127 + .../pip/_internal/commands/cache.py | 223 + .../pip/_internal/commands/check.py | 53 + .../pip/_internal/commands/completion.py | 96 + .../pip/_internal/commands/configuration.py | 266 + .../pip/_internal/commands/debug.py | 202 + .../pip/_internal/commands/download.py | 140 + .../pip/_internal/commands/freeze.py | 97 + .../pip/_internal/commands/hash.py | 59 + .../pip/_internal/commands/help.py | 41 + .../pip/_internal/commands/index.py | 139 + .../pip/_internal/commands/install.py | 771 ++ .../pip/_internal/commands/list.py | 363 + .../pip/_internal/commands/search.py | 174 + .../pip/_internal/commands/show.py | 178 + .../pip/_internal/commands/uninstall.py | 105 + .../pip/_internal/commands/wheel.py | 178 + .../pip/_internal/configuration.py | 366 + .../pip/_internal/distributions/__init__.py | 21 + .../pip/_internal/distributions/base.py | 36 + .../pip/_internal/distributions/installed.py | 20 + .../pip/_internal/distributions/sdist.py | 127 + .../pip/_internal/distributions/wheel.py | 31 + .../site-packages/pip/_internal/exceptions.py | 658 ++ .../pip/_internal/index/__init__.py | 2 + .../pip/_internal/index/collector.py | 648 ++ .../pip/_internal/index/package_finder.py | 1004 ++ .../pip/_internal/index/sources.py | 224 + .../pip/_internal/locations/__init__.py | 520 + .../pip/_internal/locations/_distutils.py | 169 + .../pip/_internal/locations/_sysconfig.py | 219 + .../pip/_internal/locations/base.py | 52 + .../site-packages/pip/_internal/main.py | 12 + .../pip/_internal/metadata/__init__.py | 62 + .../pip/_internal/metadata/base.py | 546 ++ .../pip/_internal/metadata/pkg_resources.py | 256 + .../pip/_internal/models/__init__.py | 2 + .../pip/_internal/models/candidate.py | 34 + .../pip/_internal/models/direct_url.py | 220 + .../pip/_internal/models/format_control.py | 80 + .../pip/_internal/models/index.py | 28 + .../pip/_internal/models/link.py | 288 + .../pip/_internal/models/scheme.py | 31 + .../pip/_internal/models/search_scope.py | 129 + .../pip/_internal/models/selection_prefs.py | 51 + .../pip/_internal/models/target_python.py | 110 + .../pip/_internal/models/wheel.py | 89 + .../pip/_internal/network/__init__.py | 2 + .../pip/_internal/network/auth.py | 323 + .../pip/_internal/network/cache.py | 69 + .../pip/_internal/network/download.py | 185 + .../pip/_internal/network/lazy_wheel.py | 210 + .../pip/_internal/network/session.py | 454 + .../pip/_internal/network/utils.py | 96 + .../pip/_internal/network/xmlrpc.py | 60 + .../pip/_internal/operations/__init__.py | 0 .../_internal/operations/build/__init__.py | 0 .../_internal/operations/build/metadata.py | 39 + .../operations/build/metadata_editable.py | 41 + .../operations/build/metadata_legacy.py | 74 + .../pip/_internal/operations/build/wheel.py | 37 + .../operations/build/wheel_editable.py | 46 + .../operations/build/wheel_legacy.py | 102 + .../pip/_internal/operations/check.py | 149 + .../pip/_internal/operations/freeze.py | 254 + .../_internal/operations/install/__init__.py | 2 + .../operations/install/editable_legacy.py | 47 + .../_internal/operations/install/legacy.py | 120 + .../pip/_internal/operations/install/wheel.py | 738 ++ .../pip/_internal/operations/prepare.py | 642 ++ .../site-packages/pip/_internal/pyproject.py | 168 + .../pip/_internal/req/__init__.py | 94 + .../pip/_internal/req/constructors.py | 490 + .../pip/_internal/req/req_file.py | 536 ++ .../pip/_internal/req/req_install.py | 858 ++ .../pip/_internal/req/req_set.py | 189 + .../pip/_internal/req/req_tracker.py | 124 + .../pip/_internal/req/req_uninstall.py | 633 ++ .../pip/_internal/resolution/__init__.py | 0 .../pip/_internal/resolution/base.py | 20 + .../_internal/resolution/legacy/__init__.py | 0 .../_internal/resolution/legacy/resolver.py | 467 + .../resolution/resolvelib/__init__.py | 0 .../_internal/resolution/resolvelib/base.py | 141 + .../resolution/resolvelib/candidates.py | 547 ++ .../resolution/resolvelib/factory.py | 739 ++ .../resolution/resolvelib/found_candidates.py | 155 + .../resolution/resolvelib/provider.py | 248 + .../resolution/resolvelib/reporter.py | 68 + .../resolution/resolvelib/requirements.py | 166 + .../resolution/resolvelib/resolver.py | 292 + .../pip/_internal/self_outdated_check.py | 189 + .../pip/_internal/utils/__init__.py | 0 .../site-packages/pip/_internal/utils/_log.py | 38 + .../pip/_internal/utils/appdirs.py | 52 + .../pip/_internal/utils/compat.py | 63 + .../pip/_internal/utils/compatibility_tags.py | 165 + .../pip/_internal/utils/datetime.py | 11 + .../pip/_internal/utils/deprecation.py | 120 + .../pip/_internal/utils/direct_url_helpers.py | 87 + .../pip/_internal/utils/distutils_args.py | 42 + .../pip/_internal/utils/egg_link.py | 75 + .../pip/_internal/utils/encoding.py | 36 + .../pip/_internal/utils/entrypoints.py | 27 + .../pip/_internal/utils/filesystem.py | 182 + .../pip/_internal/utils/filetypes.py | 27 + .../pip/_internal/utils/glibc.py | 88 + .../pip/_internal/utils/hashes.py | 144 + .../_internal/utils/inject_securetransport.py | 35 + .../pip/_internal/utils/logging.py | 343 + .../site-packages/pip/_internal/utils/misc.py | 653 ++ .../pip/_internal/utils/models.py | 39 + .../pip/_internal/utils/packaging.py | 57 + .../pip/_internal/utils/setuptools_build.py | 195 + .../pip/_internal/utils/subprocess.py | 260 + .../pip/_internal/utils/temp_dir.py | 246 + .../pip/_internal/utils/unpacking.py | 258 + .../site-packages/pip/_internal/utils/urls.py | 62 + .../pip/_internal/utils/virtualenv.py | 104 + .../pip/_internal/utils/wheel.py | 136 + .../pip/_internal/vcs/__init__.py | 15 + .../site-packages/pip/_internal/vcs/bazaar.py | 101 + .../site-packages/pip/_internal/vcs/git.py | 526 + .../pip/_internal/vcs/mercurial.py | 163 + .../pip/_internal/vcs/subversion.py | 324 + .../pip/_internal/vcs/versioncontrol.py | 705 ++ .../pip/_internal/wheel_builder.py | 377 + .../site-packages/pip/_vendor/__init__.py | 111 + .../pip/_vendor/cachecontrol/__init__.py | 18 + .../pip/_vendor/cachecontrol/_cmd.py | 61 + .../pip/_vendor/cachecontrol/adapter.py | 137 + .../pip/_vendor/cachecontrol/cache.py | 43 + .../_vendor/cachecontrol/caches/__init__.py | 6 + .../_vendor/cachecontrol/caches/file_cache.py | 150 + .../cachecontrol/caches/redis_cache.py | 37 + .../pip/_vendor/cachecontrol/compat.py | 32 + .../pip/_vendor/cachecontrol/controller.py | 415 + .../pip/_vendor/cachecontrol/filewrapper.py | 111 + .../pip/_vendor/cachecontrol/heuristics.py | 139 + .../pip/_vendor/cachecontrol/serialize.py | 186 + .../pip/_vendor/cachecontrol/wrapper.py | 33 + .../pip/_vendor/certifi/__init__.py | 3 + .../pip/_vendor/certifi/__main__.py | 12 + .../pip/_vendor/certifi/cacert.pem | 4362 +++++++++ .../site-packages/pip/_vendor/certifi/core.py | 76 + .../pip/_vendor/chardet/__init__.py | 83 + .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 233 + .../pip/_vendor/chardet/charsetgroupprober.py | 107 + .../pip/_vendor/chardet/charsetprober.py | 145 + .../pip/_vendor/chardet/cli/__init__.py | 1 + .../pip/_vendor/chardet/cli/chardetect.py | 84 + .../pip/_vendor/chardet/codingstatemachine.py | 88 + .../pip/_vendor/chardet/compat.py | 36 + .../pip/_vendor/chardet/cp949prober.py | 49 + .../pip/_vendor/chardet/enums.py | 76 + .../pip/_vendor/chardet/escprober.py | 101 + .../pip/_vendor/chardet/escsm.py | 246 + .../pip/_vendor/chardet/eucjpprober.py | 92 + .../pip/_vendor/chardet/euckrfreq.py | 195 + .../pip/_vendor/chardet/euckrprober.py | 47 + .../pip/_vendor/chardet/euctwfreq.py | 387 + .../pip/_vendor/chardet/euctwprober.py | 46 + .../pip/_vendor/chardet/gb2312freq.py | 283 + .../pip/_vendor/chardet/gb2312prober.py | 46 + .../pip/_vendor/chardet/hebrewprober.py | 292 + .../pip/_vendor/chardet/jisfreq.py | 325 + .../pip/_vendor/chardet/jpcntx.py | 233 + .../pip/_vendor/chardet/langbulgarianmodel.py | 4650 +++++++++ .../pip/_vendor/chardet/langgreekmodel.py | 4398 +++++++++ .../pip/_vendor/chardet/langhebrewmodel.py | 4383 +++++++++ .../pip/_vendor/chardet/langhungarianmodel.py | 4650 +++++++++ .../pip/_vendor/chardet/langrussianmodel.py | 5718 +++++++++++ .../pip/_vendor/chardet/langthaimodel.py | 4383 +++++++++ .../pip/_vendor/chardet/langturkishmodel.py | 4383 +++++++++ .../pip/_vendor/chardet/latin1prober.py | 145 + .../pip/_vendor/chardet/mbcharsetprober.py | 91 + .../pip/_vendor/chardet/mbcsgroupprober.py | 54 + .../pip/_vendor/chardet/mbcssm.py | 572 ++ .../pip/_vendor/chardet/metadata/__init__.py | 0 .../pip/_vendor/chardet/metadata/languages.py | 310 + .../pip/_vendor/chardet/sbcharsetprober.py | 145 + .../pip/_vendor/chardet/sbcsgroupprober.py | 83 + .../pip/_vendor/chardet/sjisprober.py | 92 + .../pip/_vendor/chardet/universaldetector.py | 286 + .../pip/_vendor/chardet/utf8prober.py | 82 + .../pip/_vendor/chardet/version.py | 9 + .../pip/_vendor/colorama/__init__.py | 6 + .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 258 + .../pip/_vendor/colorama/initialise.py | 80 + .../pip/_vendor/colorama/win32.py | 152 + .../pip/_vendor/colorama/winterm.py | 169 + .../pip/_vendor/distlib/__init__.py | 23 + .../pip/_vendor/distlib/compat.py | 1116 +++ .../pip/_vendor/distlib/database.py | 1345 +++ .../pip/_vendor/distlib/index.py | 509 + .../pip/_vendor/distlib/locators.py | 1300 +++ .../pip/_vendor/distlib/manifest.py | 393 + .../pip/_vendor/distlib/markers.py | 152 + .../pip/_vendor/distlib/metadata.py | 1058 ++ .../pip/_vendor/distlib/resources.py | 358 + .../pip/_vendor/distlib/scripts.py | 429 + .../site-packages/pip/_vendor/distlib/util.py | 1932 ++++ .../pip/_vendor/distlib/version.py | 739 ++ .../pip/_vendor/distlib/wheel.py | 1053 ++ .../site-packages/pip/_vendor/distro.py | 1386 +++ .../pip/_vendor/html5lib/__init__.py | 35 + .../pip/_vendor/html5lib/_ihatexml.py | 289 + .../pip/_vendor/html5lib/_inputstream.py | 918 ++ .../pip/_vendor/html5lib/_tokenizer.py | 1735 ++++ .../pip/_vendor/html5lib/_trie/__init__.py | 5 + .../pip/_vendor/html5lib/_trie/_base.py | 40 + .../pip/_vendor/html5lib/_trie/py.py | 67 + .../pip/_vendor/html5lib/_utils.py | 159 + .../pip/_vendor/html5lib/constants.py | 2946 ++++++ .../pip/_vendor/html5lib/filters/__init__.py | 0 .../filters/alphabeticalattributes.py | 29 + .../pip/_vendor/html5lib/filters/base.py | 12 + .../html5lib/filters/inject_meta_charset.py | 73 + .../pip/_vendor/html5lib/filters/lint.py | 93 + .../_vendor/html5lib/filters/optionaltags.py | 207 + .../pip/_vendor/html5lib/filters/sanitizer.py | 916 ++ .../_vendor/html5lib/filters/whitespace.py | 38 + .../pip/_vendor/html5lib/html5parser.py | 2795 ++++++ .../pip/_vendor/html5lib/serializer.py | 409 + .../_vendor/html5lib/treeadapters/__init__.py | 30 + .../_vendor/html5lib/treeadapters/genshi.py | 54 + .../pip/_vendor/html5lib/treeadapters/sax.py | 50 + .../_vendor/html5lib/treebuilders/__init__.py | 88 + .../pip/_vendor/html5lib/treebuilders/base.py | 417 + .../pip/_vendor/html5lib/treebuilders/dom.py | 239 + .../_vendor/html5lib/treebuilders/etree.py | 343 + .../html5lib/treebuilders/etree_lxml.py | 392 + .../_vendor/html5lib/treewalkers/__init__.py | 154 + .../pip/_vendor/html5lib/treewalkers/base.py | 252 + .../pip/_vendor/html5lib/treewalkers/dom.py | 43 + .../pip/_vendor/html5lib/treewalkers/etree.py | 131 + .../html5lib/treewalkers/etree_lxml.py | 215 + .../_vendor/html5lib/treewalkers/genshi.py | 69 + .../pip/_vendor/idna/__init__.py | 44 + .../site-packages/pip/_vendor/idna/codec.py | 112 + .../site-packages/pip/_vendor/idna/compat.py | 13 + .../site-packages/pip/_vendor/idna/core.py | 397 + .../pip/_vendor/idna/idnadata.py | 2137 +++++ .../pip/_vendor/idna/intranges.py | 54 + .../pip/_vendor/idna/package_data.py | 2 + .../pip/_vendor/idna/uts46data.py | 8512 +++++++++++++++++ .../pip/_vendor/msgpack/__init__.py | 54 + .../pip/_vendor/msgpack/_version.py | 1 + .../pip/_vendor/msgpack/exceptions.py | 48 + .../site-packages/pip/_vendor/msgpack/ext.py | 193 + .../pip/_vendor/msgpack/fallback.py | 1012 ++ .../pip/_vendor/packaging/__about__.py | 26 + .../pip/_vendor/packaging/__init__.py | 25 + .../pip/_vendor/packaging/_manylinux.py | 301 + .../pip/_vendor/packaging/_musllinux.py | 136 + .../pip/_vendor/packaging/_structures.py | 61 + .../pip/_vendor/packaging/markers.py | 304 + .../pip/_vendor/packaging/requirements.py | 146 + .../pip/_vendor/packaging/specifiers.py | 802 ++ .../pip/_vendor/packaging/tags.py | 487 + .../pip/_vendor/packaging/utils.py | 136 + .../pip/_vendor/packaging/version.py | 504 + .../pip/_vendor/pep517/__init__.py | 6 + .../site-packages/pip/_vendor/pep517/build.py | 127 + .../site-packages/pip/_vendor/pep517/check.py | 207 + .../pip/_vendor/pep517/colorlog.py | 115 + .../pip/_vendor/pep517/compat.py | 51 + .../pip/_vendor/pep517/dirtools.py | 44 + .../pip/_vendor/pep517/envbuild.py | 171 + .../pip/_vendor/pep517/in_process/__init__.py | 17 + .../_vendor/pep517/in_process/_in_process.py | 363 + .../site-packages/pip/_vendor/pep517/meta.py | 92 + .../pip/_vendor/pep517/wrappers.py | 375 + .../pip/_vendor/pkg_resources/__init__.py | 3296 +++++++ .../pip/_vendor/pkg_resources/py31compat.py | 23 + .../pip/_vendor/platformdirs/__init__.py | 331 + .../pip/_vendor/platformdirs/__main__.py | 46 + .../pip/_vendor/platformdirs/android.py | 119 + .../pip/_vendor/platformdirs/api.py | 156 + .../pip/_vendor/platformdirs/macos.py | 64 + .../pip/_vendor/platformdirs/unix.py | 181 + .../pip/_vendor/platformdirs/version.py | 4 + .../pip/_vendor/platformdirs/windows.py | 182 + .../pip/_vendor/progress/__init__.py | 189 + .../site-packages/pip/_vendor/progress/bar.py | 93 + .../pip/_vendor/progress/colors.py | 79 + .../pip/_vendor/progress/counter.py | 47 + .../pip/_vendor/progress/spinner.py | 45 + .../pip/_vendor/pygments/__init__.py | 83 + .../pip/_vendor/pygments/__main__.py | 17 + .../pip/_vendor/pygments/cmdline.py | 663 ++ .../pip/_vendor/pygments/console.py | 70 + .../pip/_vendor/pygments/filter.py | 71 + .../pip/_vendor/pygments/filters/__init__.py | 937 ++ .../pip/_vendor/pygments/formatter.py | 94 + .../_vendor/pygments/formatters/__init__.py | 153 + .../_vendor/pygments/formatters/_mapping.py | 84 + .../pip/_vendor/pygments/formatters/bbcode.py | 108 + .../pip/_vendor/pygments/formatters/groff.py | 168 + .../pip/_vendor/pygments/formatters/html.py | 983 ++ .../pip/_vendor/pygments/formatters/img.py | 641 ++ .../pip/_vendor/pygments/formatters/irc.py | 179 + .../pip/_vendor/pygments/formatters/latex.py | 511 + .../pip/_vendor/pygments/formatters/other.py | 161 + .../pygments/formatters/pangomarkup.py | 83 + .../pip/_vendor/pygments/formatters/rtf.py | 146 + .../pip/_vendor/pygments/formatters/svg.py | 188 + .../_vendor/pygments/formatters/terminal.py | 127 + .../pygments/formatters/terminal256.py | 338 + .../pip/_vendor/pygments/lexer.py | 879 ++ .../pip/_vendor/pygments/lexers/__init__.py | 341 + .../pip/_vendor/pygments/lexers/_mapping.py | 580 ++ .../pip/_vendor/pygments/lexers/python.py | 1188 +++ .../pip/_vendor/pygments/modeline.py | 43 + .../pip/_vendor/pygments/plugin.py | 69 + .../pip/_vendor/pygments/regexopt.py | 91 + .../pip/_vendor/pygments/scanner.py | 104 + .../pip/_vendor/pygments/sphinxext.py | 155 + .../pip/_vendor/pygments/style.py | 197 + .../pip/_vendor/pygments/styles/__init__.py | 93 + .../pip/_vendor/pygments/token.py | 212 + .../pip/_vendor/pygments/unistring.py | 153 + .../pip/_vendor/pygments/util.py | 308 + .../pip/_vendor/pyparsing/__init__.py | 328 + .../pip/_vendor/pyparsing/actions.py | 207 + .../pip/_vendor/pyparsing/common.py | 424 + .../pip/_vendor/pyparsing/core.py | 5789 +++++++++++ .../pip/_vendor/pyparsing/diagram/__init__.py | 593 ++ .../pip/_vendor/pyparsing/exceptions.py | 267 + .../pip/_vendor/pyparsing/helpers.py | 1069 +++ .../pip/_vendor/pyparsing/results.py | 760 ++ .../pip/_vendor/pyparsing/testing.py | 331 + .../pip/_vendor/pyparsing/unicode.py | 332 + .../pip/_vendor/pyparsing/util.py | 235 + .../pip/_vendor/requests/__init__.py | 154 + .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 42 + .../pip/_vendor/requests/adapters.py | 538 ++ .../site-packages/pip/_vendor/requests/api.py | 159 + .../pip/_vendor/requests/auth.py | 305 + .../pip/_vendor/requests/certs.py | 18 + .../pip/_vendor/requests/compat.py | 77 + .../pip/_vendor/requests/cookies.py | 549 ++ .../pip/_vendor/requests/exceptions.py | 133 + .../pip/_vendor/requests/help.py | 132 + .../pip/_vendor/requests/hooks.py | 34 + .../pip/_vendor/requests/models.py | 973 ++ .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 771 ++ .../pip/_vendor/requests/status_codes.py | 123 + .../pip/_vendor/requests/structures.py | 105 + .../pip/_vendor/requests/utils.py | 1060 ++ .../pip/_vendor/resolvelib/__init__.py | 26 + .../pip/_vendor/resolvelib/compat/__init__.py | 0 .../resolvelib/compat/collections_abc.py | 6 + .../pip/_vendor/resolvelib/providers.py | 133 + .../pip/_vendor/resolvelib/reporters.py | 43 + .../pip/_vendor/resolvelib/resolvers.py | 482 + .../pip/_vendor/resolvelib/structs.py | 165 + .../pip/_vendor/rich/__init__.py | 172 + .../pip/_vendor/rich/__main__.py | 280 + .../pip/_vendor/rich/_cell_widths.py | 451 + .../pip/_vendor/rich/_emoji_codes.py | 3610 +++++++ .../pip/_vendor/rich/_emoji_replace.py | 32 + .../pip/_vendor/rich/_extension.py | 10 + .../pip/_vendor/rich/_inspect.py | 210 + .../pip/_vendor/rich/_log_render.py | 94 + .../site-packages/pip/_vendor/rich/_loop.py | 43 + .../pip/_vendor/rich/_lru_cache.py | 34 + .../pip/_vendor/rich/_palettes.py | 309 + .../site-packages/pip/_vendor/rich/_pick.py | 17 + .../site-packages/pip/_vendor/rich/_ratio.py | 160 + .../pip/_vendor/rich/_spinners.py | 848 ++ .../site-packages/pip/_vendor/rich/_stack.py | 16 + .../site-packages/pip/_vendor/rich/_timer.py | 19 + .../pip/_vendor/rich/_windows.py | 72 + .../site-packages/pip/_vendor/rich/_wrap.py | 55 + .../site-packages/pip/_vendor/rich/abc.py | 33 + .../site-packages/pip/_vendor/rich/align.py | 312 + .../site-packages/pip/_vendor/rich/ansi.py | 228 + .../site-packages/pip/_vendor/rich/bar.py | 94 + .../site-packages/pip/_vendor/rich/box.py | 483 + .../site-packages/pip/_vendor/rich/cells.py | 147 + .../site-packages/pip/_vendor/rich/color.py | 581 ++ .../pip/_vendor/rich/color_triplet.py | 38 + .../site-packages/pip/_vendor/rich/columns.py | 187 + .../site-packages/pip/_vendor/rich/console.py | 2211 +++++ .../pip/_vendor/rich/constrain.py | 37 + .../pip/_vendor/rich/containers.py | 167 + .../site-packages/pip/_vendor/rich/control.py | 175 + .../pip/_vendor/rich/default_styles.py | 183 + .../pip/_vendor/rich/diagnose.py | 6 + .../site-packages/pip/_vendor/rich/emoji.py | 96 + .../site-packages/pip/_vendor/rich/errors.py | 34 + .../pip/_vendor/rich/file_proxy.py | 54 + .../pip/_vendor/rich/filesize.py | 89 + .../pip/_vendor/rich/highlighter.py | 147 + .../site-packages/pip/_vendor/rich/json.py | 140 + .../site-packages/pip/_vendor/rich/jupyter.py | 92 + .../site-packages/pip/_vendor/rich/layout.py | 444 + .../site-packages/pip/_vendor/rich/live.py | 365 + .../pip/_vendor/rich/live_render.py | 113 + .../site-packages/pip/_vendor/rich/logging.py | 268 + .../site-packages/pip/_vendor/rich/markup.py | 244 + .../site-packages/pip/_vendor/rich/measure.py | 149 + .../site-packages/pip/_vendor/rich/padding.py | 141 + .../site-packages/pip/_vendor/rich/pager.py | 34 + .../site-packages/pip/_vendor/rich/palette.py | 100 + .../site-packages/pip/_vendor/rich/panel.py | 250 + .../site-packages/pip/_vendor/rich/pretty.py | 903 ++ .../pip/_vendor/rich/progress.py | 1036 ++ .../pip/_vendor/rich/progress_bar.py | 216 + .../site-packages/pip/_vendor/rich/prompt.py | 376 + .../pip/_vendor/rich/protocol.py | 42 + .../site-packages/pip/_vendor/rich/region.py | 10 + .../site-packages/pip/_vendor/rich/repr.py | 151 + .../site-packages/pip/_vendor/rich/rule.py | 115 + .../site-packages/pip/_vendor/rich/scope.py | 86 + .../site-packages/pip/_vendor/rich/screen.py | 54 + .../site-packages/pip/_vendor/rich/segment.py | 720 ++ .../site-packages/pip/_vendor/rich/spinner.py | 134 + .../site-packages/pip/_vendor/rich/status.py | 132 + .../site-packages/pip/_vendor/rich/style.py | 785 ++ .../site-packages/pip/_vendor/rich/styled.py | 42 + .../site-packages/pip/_vendor/rich/syntax.py | 735 ++ .../site-packages/pip/_vendor/rich/table.py | 968 ++ .../pip/_vendor/rich/tabulate.py | 51 + .../pip/_vendor/rich/terminal_theme.py | 55 + .../site-packages/pip/_vendor/rich/text.py | 1282 +++ .../site-packages/pip/_vendor/rich/theme.py | 112 + .../site-packages/pip/_vendor/rich/themes.py | 5 + .../pip/_vendor/rich/traceback.py | 678 ++ .../site-packages/pip/_vendor/rich/tree.py | 249 + .../site-packages/pip/_vendor/six.py | 998 ++ .../pip/_vendor/tenacity/__init__.py | 517 + .../pip/_vendor/tenacity/_asyncio.py | 92 + .../pip/_vendor/tenacity/_utils.py | 68 + .../pip/_vendor/tenacity/after.py | 46 + .../pip/_vendor/tenacity/before.py | 41 + .../pip/_vendor/tenacity/before_sleep.py | 58 + .../site-packages/pip/_vendor/tenacity/nap.py | 43 + .../pip/_vendor/tenacity/retry.py | 213 + .../pip/_vendor/tenacity/stop.py | 96 + .../pip/_vendor/tenacity/tornadoweb.py | 59 + .../pip/_vendor/tenacity/wait.py | 191 + .../pip/_vendor/tomli/__init__.py | 6 + .../pip/_vendor/tomli/_parser.py | 703 ++ .../site-packages/pip/_vendor/tomli/_re.py | 83 + .../pip/_vendor/typing_extensions.py | 2296 +++++ .../pip/_vendor/urllib3/__init__.py | 85 + .../pip/_vendor/urllib3/_collections.py | 355 + .../pip/_vendor/urllib3/_version.py | 2 + .../pip/_vendor/urllib3/connection.py | 569 ++ .../pip/_vendor/urllib3/connectionpool.py | 1113 +++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../urllib3/contrib/_appengine_environ.py | 36 + .../contrib/_securetransport/__init__.py | 0 .../contrib/_securetransport/bindings.py | 519 + .../contrib/_securetransport/low_level.py | 397 + .../pip/_vendor/urllib3/contrib/appengine.py | 314 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 130 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 511 + .../urllib3/contrib/securetransport.py | 922 ++ .../pip/_vendor/urllib3/contrib/socks.py | 216 + .../pip/_vendor/urllib3/exceptions.py | 323 + .../pip/_vendor/urllib3/fields.py | 274 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 0 .../urllib3/packages/backports/__init__.py | 0 .../urllib3/packages/backports/makefile.py | 51 + .../pip/_vendor/urllib3/packages/six.py | 1077 +++ .../pip/_vendor/urllib3/poolmanager.py | 539 ++ .../pip/_vendor/urllib3/request.py | 170 + .../pip/_vendor/urllib3/response.py | 821 ++ .../pip/_vendor/urllib3/util/__init__.py | 49 + .../pip/_vendor/urllib3/util/connection.py | 149 + .../pip/_vendor/urllib3/util/proxy.py | 57 + .../pip/_vendor/urllib3/util/queue.py | 22 + .../pip/_vendor/urllib3/util/request.py | 143 + .../pip/_vendor/urllib3/util/response.py | 107 + .../pip/_vendor/urllib3/util/retry.py | 620 ++ .../pip/_vendor/urllib3/util/ssl_.py | 495 + .../urllib3/util/ssl_match_hostname.py | 161 + .../pip/_vendor/urllib3/util/ssltransport.py | 221 + .../pip/_vendor/urllib3/util/timeout.py | 268 + .../pip/_vendor/urllib3/util/url.py | 432 + .../pip/_vendor/urllib3/util/wait.py | 153 + .../site-packages/pip/_vendor/vendor.txt | 25 + .../pip/_vendor/webencodings/__init__.py | 342 + .../pip/_vendor/webencodings/labels.py | 231 + .../pip/_vendor/webencodings/mklabels.py | 59 + .../pip/_vendor/webencodings/tests.py | 153 + .../_vendor/webencodings/x_user_defined.py | 325 + .../lib/python3.10/site-packages/pip/py.typed | 4 + .../site-packages/pkg_resources/__init__.py | 3303 +++++++ .../pkg_resources/_vendor/__init__.py | 0 .../pkg_resources/_vendor/appdirs.py | 608 ++ .../_vendor/packaging/__about__.py | 26 + .../_vendor/packaging/__init__.py | 25 + .../_vendor/packaging/_manylinux.py | 301 + .../_vendor/packaging/_musllinux.py | 136 + .../_vendor/packaging/_structures.py | 67 + .../_vendor/packaging/markers.py | 304 + .../_vendor/packaging/requirements.py | 146 + .../_vendor/packaging/specifiers.py | 828 ++ .../pkg_resources/_vendor/packaging/tags.py | 484 + .../pkg_resources/_vendor/packaging/utils.py | 136 + .../_vendor/packaging/version.py | 504 + .../pkg_resources/_vendor/pyparsing.py | 5742 +++++++++++ .../pkg_resources/extern/__init__.py | 73 + .../data/my-test-package-source/setup.py | 6 + .../python_dotenv-1.0.1.dist-info/INSTALLER | 1 + .../python_dotenv-1.0.1.dist-info/LICENSE | 27 + .../python_dotenv-1.0.1.dist-info/METADATA | 692 ++ .../python_dotenv-1.0.1.dist-info/RECORD | 26 + .../python_dotenv-1.0.1.dist-info/REQUESTED | 0 .../python_dotenv-1.0.1.dist-info/WHEEL | 5 + .../entry_points.txt | 2 + .../top_level.txt | 1 + .../setuptools-59.6.0.dist-info/INSTALLER | 1 + .../setuptools-59.6.0.dist-info/LICENSE | 19 + .../setuptools-59.6.0.dist-info/METADATA | 124 + .../setuptools-59.6.0.dist-info/RECORD | 298 + .../setuptools-59.6.0.dist-info/REQUESTED | 0 .../setuptools-59.6.0.dist-info/WHEEL | 5 + .../entry_points.txt | 56 + .../setuptools-59.6.0.dist-info/top_level.txt | 4 + .../site-packages/setuptools/__init__.py | 242 + .../setuptools/_deprecation_warning.py | 7 + .../setuptools/_distutils/__init__.py | 24 + .../setuptools/_distutils/_msvccompiler.py | 561 ++ .../setuptools/_distutils/archive_util.py | 256 + .../setuptools/_distutils/bcppcompiler.py | 393 + .../setuptools/_distutils/ccompiler.py | 1123 +++ .../setuptools/_distutils/cmd.py | 403 + .../setuptools/_distutils/command/__init__.py | 31 + .../setuptools/_distutils/command/bdist.py | 143 + .../_distutils/command/bdist_dumb.py | 123 + .../_distutils/command/bdist_msi.py | 749 ++ .../_distutils/command/bdist_rpm.py | 579 ++ .../_distutils/command/bdist_wininst.py | 377 + .../setuptools/_distutils/command/build.py | 157 + .../_distutils/command/build_clib.py | 209 + .../_distutils/command/build_ext.py | 755 ++ .../setuptools/_distutils/command/build_py.py | 392 + .../_distutils/command/build_scripts.py | 152 + .../setuptools/_distutils/command/check.py | 148 + .../setuptools/_distutils/command/clean.py | 76 + .../setuptools/_distutils/command/config.py | 344 + .../setuptools/_distutils/command/install.py | 721 ++ .../_distutils/command/install_data.py | 79 + .../_distutils/command/install_egg_info.py | 84 + .../_distutils/command/install_headers.py | 47 + .../_distutils/command/install_lib.py | 217 + .../_distutils/command/install_scripts.py | 60 + .../_distutils/command/py37compat.py | 30 + .../setuptools/_distutils/command/register.py | 304 + .../setuptools/_distutils/command/sdist.py | 494 + .../setuptools/_distutils/command/upload.py | 214 + .../setuptools/_distutils/config.py | 130 + .../setuptools/_distutils/core.py | 249 + .../setuptools/_distutils/cygwinccompiler.py | 425 + .../setuptools/_distutils/debug.py | 5 + .../setuptools/_distutils/dep_util.py | 92 + .../setuptools/_distutils/dir_util.py | 210 + .../setuptools/_distutils/dist.py | 1257 +++ .../setuptools/_distutils/errors.py | 97 + .../setuptools/_distutils/extension.py | 240 + .../setuptools/_distutils/fancy_getopt.py | 457 + .../setuptools/_distutils/file_util.py | 238 + .../setuptools/_distutils/filelist.py | 355 + .../setuptools/_distutils/log.py | 77 + .../setuptools/_distutils/msvc9compiler.py | 788 ++ .../setuptools/_distutils/msvccompiler.py | 643 ++ .../setuptools/_distutils/py35compat.py | 19 + .../setuptools/_distutils/py38compat.py | 7 + .../setuptools/_distutils/spawn.py | 106 + .../setuptools/_distutils/sysconfig.py | 601 ++ .../setuptools/_distutils/text_file.py | 286 + .../setuptools/_distutils/unixccompiler.py | 325 + .../setuptools/_distutils/util.py | 548 ++ .../setuptools/_distutils/version.py | 363 + .../setuptools/_distutils/versionpredicate.py | 169 + .../site-packages/setuptools/_imp.py | 82 + .../setuptools/_vendor/__init__.py | 0 .../_vendor/more_itertools/__init__.py | 4 + .../setuptools/_vendor/more_itertools/more.py | 3825 ++++++++ .../_vendor/more_itertools/recipes.py | 620 ++ .../setuptools/_vendor/ordered_set.py | 488 + .../setuptools/_vendor/packaging/__about__.py | 26 + .../setuptools/_vendor/packaging/__init__.py | 25 + .../_vendor/packaging/_manylinux.py | 301 + .../_vendor/packaging/_musllinux.py | 136 + .../_vendor/packaging/_structures.py | 67 + .../setuptools/_vendor/packaging/markers.py | 304 + .../_vendor/packaging/requirements.py | 146 + .../_vendor/packaging/specifiers.py | 828 ++ .../setuptools/_vendor/packaging/tags.py | 484 + .../setuptools/_vendor/packaging/utils.py | 136 + .../setuptools/_vendor/packaging/version.py | 504 + .../setuptools/_vendor/pyparsing.py | 5742 +++++++++++ .../site-packages/setuptools/archive_util.py | 205 + .../site-packages/setuptools/build_meta.py | 290 + .../site-packages/setuptools/cli-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/cli-64.exe | Bin 0 -> 74752 bytes .../site-packages/setuptools/cli-arm64.exe | Bin 0 -> 137216 bytes .../site-packages/setuptools/cli.exe | Bin 0 -> 65536 bytes .../setuptools/command/__init__.py | 8 + .../site-packages/setuptools/command/alias.py | 78 + .../setuptools/command/bdist_egg.py | 456 + .../setuptools/command/bdist_rpm.py | 40 + .../setuptools/command/build_clib.py | 101 + .../setuptools/command/build_ext.py | 328 + .../setuptools/command/build_py.py | 242 + .../setuptools/command/develop.py | 193 + .../setuptools/command/dist_info.py | 36 + .../setuptools/command/easy_install.py | 2354 +++++ .../setuptools/command/egg_info.py | 755 ++ .../setuptools/command/install.py | 132 + .../setuptools/command/install_egg_info.py | 82 + .../setuptools/command/install_lib.py | 148 + .../setuptools/command/install_scripts.py | 69 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/py36compat.py | 134 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 64 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 196 + .../setuptools/command/setopt.py | 149 + .../site-packages/setuptools/command/test.py | 252 + .../setuptools/command/upload.py | 17 + .../setuptools/command/upload_docs.py | 202 + .../site-packages/setuptools/config.py | 751 ++ .../site-packages/setuptools/dep_util.py | 25 + .../site-packages/setuptools/depends.py | 176 + .../site-packages/setuptools/dist.py | 1156 +++ .../site-packages/setuptools/errors.py | 40 + .../site-packages/setuptools/extension.py | 55 + .../setuptools/extern/__init__.py | 73 + .../site-packages/setuptools/glob.py | 167 + .../site-packages/setuptools/gui-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/gui-64.exe | Bin 0 -> 75264 bytes .../site-packages/setuptools/gui-arm64.exe | Bin 0 -> 137728 bytes .../site-packages/setuptools/gui.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/installer.py | 104 + .../site-packages/setuptools/launch.py | 36 + .../site-packages/setuptools/monkey.py | 177 + .../site-packages/setuptools/msvc.py | 1805 ++++ .../site-packages/setuptools/namespaces.py | 107 + .../site-packages/setuptools/package_index.py | 1127 +++ .../site-packages/setuptools/py34compat.py | 13 + .../site-packages/setuptools/sandbox.py | 530 + .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/unicode_utils.py | 42 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/wheel.py | 213 + .../setuptools/windows_support.py | 29 + .../site-packages/sqlalchemy/__init__.py | 294 + .../sqlalchemy/connectors/__init__.py | 18 + .../sqlalchemy/connectors/aioodbc.py | 174 + .../sqlalchemy/connectors/asyncio.py | 204 + .../sqlalchemy/connectors/pyodbc.py | 249 + .../sqlalchemy/cyextension/__init__.py | 6 + ...ollections.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 1868216 bytes .../sqlalchemy/cyextension/collections.pyx | 409 + ...utabledict.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 587592 bytes .../sqlalchemy/cyextension/immutabledict.pxd | 8 + .../sqlalchemy/cyextension/immutabledict.pyx | 133 + ...processors.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 431544 bytes .../sqlalchemy/cyextension/processors.pyx | 68 + ...esultproxy.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 510168 bytes .../sqlalchemy/cyextension/resultproxy.pyx | 102 + .../util.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 725904 bytes .../sqlalchemy/cyextension/util.pyx | 91 + .../sqlalchemy/dialects/__init__.py | 61 + .../sqlalchemy/dialects/_typing.py | 25 + .../sqlalchemy/dialects/mssql/__init__.py | 88 + .../sqlalchemy/dialects/mssql/aioodbc.py | 64 + .../sqlalchemy/dialects/mssql/base.py | 4007 ++++++++ .../dialects/mssql/information_schema.py | 254 + .../sqlalchemy/dialects/mssql/json.py | 133 + .../sqlalchemy/dialects/mssql/provision.py | 155 + .../sqlalchemy/dialects/mssql/pymssql.py | 125 + .../sqlalchemy/dialects/mssql/pyodbc.py | 745 ++ .../sqlalchemy/dialects/mysql/__init__.py | 101 + .../sqlalchemy/dialects/mysql/aiomysql.py | 332 + .../sqlalchemy/dialects/mysql/asyncmy.py | 337 + .../sqlalchemy/dialects/mysql/base.py | 3447 +++++++ .../sqlalchemy/dialects/mysql/cymysql.py | 84 + .../sqlalchemy/dialects/mysql/dml.py | 219 + .../sqlalchemy/dialects/mysql/enumerated.py | 244 + .../sqlalchemy/dialects/mysql/expression.py | 141 + .../sqlalchemy/dialects/mysql/json.py | 81 + .../sqlalchemy/dialects/mysql/mariadb.py | 32 + .../dialects/mysql/mariadbconnector.py | 275 + .../dialects/mysql/mysqlconnector.py | 179 + .../sqlalchemy/dialects/mysql/mysqldb.py | 303 + .../sqlalchemy/dialects/mysql/provision.py | 107 + .../sqlalchemy/dialects/mysql/pymysql.py | 137 + .../sqlalchemy/dialects/mysql/pyodbc.py | 138 + .../sqlalchemy/dialects/mysql/reflection.py | 677 ++ .../dialects/mysql/reserved_words.py | 571 ++ .../sqlalchemy/dialects/mysql/types.py | 774 ++ .../sqlalchemy/dialects/oracle/__init__.py | 67 + .../sqlalchemy/dialects/oracle/base.py | 3240 +++++++ .../sqlalchemy/dialects/oracle/cx_oracle.py | 1492 +++ .../sqlalchemy/dialects/oracle/dictionary.py | 507 + .../sqlalchemy/dialects/oracle/oracledb.py | 311 + .../sqlalchemy/dialects/oracle/provision.py | 220 + .../sqlalchemy/dialects/oracle/types.py | 287 + .../dialects/postgresql/__init__.py | 167 + .../dialects/postgresql/_psycopg_common.py | 187 + .../sqlalchemy/dialects/postgresql/array.py | 425 + .../sqlalchemy/dialects/postgresql/asyncpg.py | 1262 +++ .../sqlalchemy/dialects/postgresql/base.py | 5007 ++++++++++ .../sqlalchemy/dialects/postgresql/dml.py | 310 + .../sqlalchemy/dialects/postgresql/ext.py | 496 + .../sqlalchemy/dialects/postgresql/hstore.py | 397 + .../sqlalchemy/dialects/postgresql/json.py | 325 + .../dialects/postgresql/named_types.py | 509 + .../dialects/postgresql/operators.py | 129 + .../sqlalchemy/dialects/postgresql/pg8000.py | 662 ++ .../dialects/postgresql/pg_catalog.py | 300 + .../dialects/postgresql/provision.py | 175 + .../sqlalchemy/dialects/postgresql/psycopg.py | 781 ++ .../dialects/postgresql/psycopg2.py | 876 ++ .../dialects/postgresql/psycopg2cffi.py | 61 + .../sqlalchemy/dialects/postgresql/ranges.py | 1029 ++ .../sqlalchemy/dialects/postgresql/types.py | 303 + .../sqlalchemy/dialects/sqlite/__init__.py | 57 + .../sqlalchemy/dialects/sqlite/aiosqlite.py | 396 + .../sqlalchemy/dialects/sqlite/base.py | 2782 ++++++ .../sqlalchemy/dialects/sqlite/dml.py | 240 + .../sqlalchemy/dialects/sqlite/json.py | 92 + .../sqlalchemy/dialects/sqlite/provision.py | 198 + .../sqlalchemy/dialects/sqlite/pysqlcipher.py | 155 + .../sqlalchemy/dialects/sqlite/pysqlite.py | 756 ++ .../dialects/type_migration_guidelines.txt | 145 + .../sqlalchemy/engine/__init__.py | 62 + .../sqlalchemy/engine/_py_processors.py | 136 + .../sqlalchemy/engine/_py_row.py | 128 + .../sqlalchemy/engine/_py_util.py | 74 + .../site-packages/sqlalchemy/engine/base.py | 3373 +++++++ .../sqlalchemy/engine/characteristics.py | 155 + .../site-packages/sqlalchemy/engine/create.py | 875 ++ .../site-packages/sqlalchemy/engine/cursor.py | 2181 +++++ .../sqlalchemy/engine/default.py | 2348 +++++ .../site-packages/sqlalchemy/engine/events.py | 951 ++ .../sqlalchemy/engine/interfaces.py | 3395 +++++++ .../site-packages/sqlalchemy/engine/mock.py | 131 + .../sqlalchemy/engine/processors.py | 61 + .../sqlalchemy/engine/reflection.py | 2089 ++++ .../site-packages/sqlalchemy/engine/result.py | 2382 +++++ .../site-packages/sqlalchemy/engine/row.py | 401 + .../sqlalchemy/engine/strategies.py | 19 + .../site-packages/sqlalchemy/engine/url.py | 910 ++ .../site-packages/sqlalchemy/engine/util.py | 167 + .../sqlalchemy/event/__init__.py | 25 + .../site-packages/sqlalchemy/event/api.py | 225 + .../site-packages/sqlalchemy/event/attr.py | 655 ++ .../site-packages/sqlalchemy/event/base.py | 462 + .../site-packages/sqlalchemy/event/legacy.py | 246 + .../sqlalchemy/event/registry.py | 386 + .../site-packages/sqlalchemy/events.py | 17 + .../site-packages/sqlalchemy/exc.py | 830 ++ .../site-packages/sqlalchemy/ext/__init__.py | 11 + .../sqlalchemy/ext/associationproxy.py | 2005 ++++ .../sqlalchemy/ext/asyncio/__init__.py | 25 + .../sqlalchemy/ext/asyncio/base.py | 279 + .../sqlalchemy/ext/asyncio/engine.py | 1466 +++ .../sqlalchemy/ext/asyncio/exc.py | 21 + .../sqlalchemy/ext/asyncio/result.py | 961 ++ .../sqlalchemy/ext/asyncio/scoping.py | 1614 ++++ .../sqlalchemy/ext/asyncio/session.py | 1936 ++++ .../site-packages/sqlalchemy/ext/automap.py | 1691 ++++ .../site-packages/sqlalchemy/ext/baked.py | 574 ++ .../site-packages/sqlalchemy/ext/compiler.py | 555 ++ .../sqlalchemy/ext/declarative/__init__.py | 65 + .../sqlalchemy/ext/declarative/extensions.py | 548 ++ .../sqlalchemy/ext/horizontal_shard.py | 481 + .../site-packages/sqlalchemy/ext/hybrid.py | 1514 +++ .../site-packages/sqlalchemy/ext/indexable.py | 341 + .../sqlalchemy/ext/instrumentation.py | 450 + .../site-packages/sqlalchemy/ext/mutable.py | 1073 +++ .../sqlalchemy/ext/mypy/__init__.py | 6 + .../sqlalchemy/ext/mypy/apply.py | 320 + .../sqlalchemy/ext/mypy/decl_class.py | 515 + .../sqlalchemy/ext/mypy/infer.py | 590 ++ .../sqlalchemy/ext/mypy/names.py | 335 + .../sqlalchemy/ext/mypy/plugin.py | 303 + .../site-packages/sqlalchemy/ext/mypy/util.py | 338 + .../sqlalchemy/ext/orderinglist.py | 416 + .../sqlalchemy/ext/serializer.py | 185 + .../sqlalchemy/future/__init__.py | 16 + .../site-packages/sqlalchemy/future/engine.py | 15 + .../site-packages/sqlalchemy/inspection.py | 174 + .../site-packages/sqlalchemy/log.py | 288 + .../site-packages/sqlalchemy/orm/__init__.py | 170 + .../sqlalchemy/orm/_orm_constructors.py | 2471 +++++ .../site-packages/sqlalchemy/orm/_typing.py | 179 + .../sqlalchemy/orm/attributes.py | 2835 ++++++ .../site-packages/sqlalchemy/orm/base.py | 971 ++ .../sqlalchemy/orm/bulk_persistence.py | 2055 ++++ .../sqlalchemy/orm/clsregistry.py | 570 ++ .../sqlalchemy/orm/collections.py | 1618 ++++ .../site-packages/sqlalchemy/orm/context.py | 3255 +++++++ .../site-packages/sqlalchemy/orm/decl_api.py | 1883 ++++ .../site-packages/sqlalchemy/orm/decl_base.py | 2152 +++++ .../sqlalchemy/orm/dependency.py | 1304 +++ .../sqlalchemy/orm/descriptor_props.py | 1074 +++ .../site-packages/sqlalchemy/orm/dynamic.py | 298 + .../site-packages/sqlalchemy/orm/evaluator.py | 368 + .../site-packages/sqlalchemy/orm/events.py | 3259 +++++++ .../site-packages/sqlalchemy/orm/exc.py | 228 + .../site-packages/sqlalchemy/orm/identity.py | 302 + .../sqlalchemy/orm/instrumentation.py | 754 ++ .../sqlalchemy/orm/interfaces.py | 1469 +++ .../site-packages/sqlalchemy/orm/loading.py | 1682 ++++ .../sqlalchemy/orm/mapped_collection.py | 560 ++ .../site-packages/sqlalchemy/orm/mapper.py | 4421 +++++++++ .../sqlalchemy/orm/path_registry.py | 811 ++ .../sqlalchemy/orm/persistence.py | 1782 ++++ .../sqlalchemy/orm/properties.py | 886 ++ .../site-packages/sqlalchemy/orm/query.py | 3394 +++++++ .../sqlalchemy/orm/relationships.py | 3500 +++++++ .../site-packages/sqlalchemy/orm/scoping.py | 2165 +++++ .../site-packages/sqlalchemy/orm/session.py | 5290 ++++++++++ .../site-packages/sqlalchemy/orm/state.py | 1136 +++ .../sqlalchemy/orm/state_changes.py | 198 + .../sqlalchemy/orm/strategies.py | 3344 +++++++ .../sqlalchemy/orm/strategy_options.py | 2569 +++++ .../site-packages/sqlalchemy/orm/sync.py | 164 + .../sqlalchemy/orm/unitofwork.py | 796 ++ .../site-packages/sqlalchemy/orm/util.py | 2416 +++++ .../site-packages/sqlalchemy/orm/writeonly.py | 678 ++ .../site-packages/sqlalchemy/pool/__init__.py | 44 + .../site-packages/sqlalchemy/pool/base.py | 1515 +++ .../site-packages/sqlalchemy/pool/events.py | 370 + .../site-packages/sqlalchemy/pool/impl.py | 581 ++ .../site-packages/sqlalchemy/py.typed | 0 .../site-packages/sqlalchemy/schema.py | 70 + .../site-packages/sqlalchemy/sql/__init__.py | 145 + .../sqlalchemy/sql/_dml_constructors.py | 140 + .../sqlalchemy/sql/_elements_constructors.py | 1847 ++++ .../sqlalchemy/sql/_orm_types.py | 20 + .../site-packages/sqlalchemy/sql/_py_util.py | 75 + .../sql/_selectable_constructors.py | 635 ++ .../site-packages/sqlalchemy/sql/_typing.py | 460 + .../sqlalchemy/sql/annotation.py | 585 ++ .../site-packages/sqlalchemy/sql/base.py | 2185 +++++ .../site-packages/sqlalchemy/sql/cache_key.py | 1057 ++ .../site-packages/sqlalchemy/sql/coercions.py | 1389 +++ .../site-packages/sqlalchemy/sql/compiler.py | 7816 +++++++++++++++ .../site-packages/sqlalchemy/sql/crud.py | 1669 ++++ .../site-packages/sqlalchemy/sql/ddl.py | 1378 +++ .../sqlalchemy/sql/default_comparator.py | 552 ++ .../site-packages/sqlalchemy/sql/dml.py | 1817 ++++ .../site-packages/sqlalchemy/sql/elements.py | 5433 +++++++++++ .../site-packages/sqlalchemy/sql/events.py | 455 + .../sqlalchemy/sql/expression.py | 162 + .../site-packages/sqlalchemy/sql/functions.py | 2055 ++++ .../site-packages/sqlalchemy/sql/lambdas.py | 1449 +++ .../site-packages/sqlalchemy/sql/naming.py | 212 + .../site-packages/sqlalchemy/sql/operators.py | 2573 +++++ .../site-packages/sqlalchemy/sql/roles.py | 323 + .../site-packages/sqlalchemy/sql/schema.py | 6147 ++++++++++++ .../sqlalchemy/sql/selectable.py | 6932 ++++++++++++++ .../site-packages/sqlalchemy/sql/sqltypes.py | 3786 ++++++++ .../sqlalchemy/sql/traversals.py | 1022 ++ .../site-packages/sqlalchemy/sql/type_api.py | 2303 +++++ .../site-packages/sqlalchemy/sql/util.py | 1486 +++ .../site-packages/sqlalchemy/sql/visitors.py | 1165 +++ .../sqlalchemy/testing/__init__.py | 95 + .../sqlalchemy/testing/assertions.py | 989 ++ .../sqlalchemy/testing/assertsql.py | 516 + .../sqlalchemy/testing/asyncio.py | 135 + .../sqlalchemy/testing/config.py | 427 + .../sqlalchemy/testing/engines.py | 472 + .../sqlalchemy/testing/entities.py | 117 + .../sqlalchemy/testing/exclusions.py | 435 + .../sqlalchemy/testing/fixtures/__init__.py | 28 + .../sqlalchemy/testing/fixtures/base.py | 366 + .../sqlalchemy/testing/fixtures/mypy.py | 312 + .../sqlalchemy/testing/fixtures/orm.py | 227 + .../sqlalchemy/testing/fixtures/sql.py | 493 + .../sqlalchemy/testing/pickleable.py | 155 + .../sqlalchemy/testing/plugin/__init__.py | 6 + .../sqlalchemy/testing/plugin/bootstrap.py | 51 + .../sqlalchemy/testing/plugin/plugin_base.py | 779 ++ .../sqlalchemy/testing/plugin/pytestplugin.py | 868 ++ .../sqlalchemy/testing/profiling.py | 324 + .../sqlalchemy/testing/provision.py | 496 + .../sqlalchemy/testing/requirements.py | 1783 ++++ .../sqlalchemy/testing/schema.py | 224 + .../sqlalchemy/testing/suite/__init__.py | 19 + .../sqlalchemy/testing/suite/test_cte.py | 211 + .../sqlalchemy/testing/suite/test_ddl.py | 389 + .../testing/suite/test_deprecations.py | 153 + .../sqlalchemy/testing/suite/test_dialect.py | 740 ++ .../sqlalchemy/testing/suite/test_insert.py | 630 ++ .../testing/suite/test_reflection.py | 3128 ++++++ .../sqlalchemy/testing/suite/test_results.py | 468 + .../sqlalchemy/testing/suite/test_rowcount.py | 258 + .../sqlalchemy/testing/suite/test_select.py | 1888 ++++ .../sqlalchemy/testing/suite/test_sequence.py | 317 + .../sqlalchemy/testing/suite/test_types.py | 2071 ++++ .../testing/suite/test_unicode_ddl.py | 189 + .../testing/suite/test_update_delete.py | 139 + .../site-packages/sqlalchemy/testing/util.py | 519 + .../sqlalchemy/testing/warnings.py | 52 + .../site-packages/sqlalchemy/types.py | 76 + .../site-packages/sqlalchemy/util/__init__.py | 159 + .../sqlalchemy/util/_collections.py | 715 ++ .../sqlalchemy/util/_concurrency_py3k.py | 290 + .../site-packages/sqlalchemy/util/_has_cy.py | 40 + .../sqlalchemy/util/_py_collections.py | 541 ++ .../site-packages/sqlalchemy/util/compat.py | 300 + .../sqlalchemy/util/concurrency.py | 108 + .../sqlalchemy/util/deprecations.py | 401 + .../sqlalchemy/util/langhelpers.py | 2208 +++++ .../sqlalchemy/util/preloaded.py | 150 + .../site-packages/sqlalchemy/util/queue.py | 322 + .../sqlalchemy/util/tool_support.py | 201 + .../sqlalchemy/util/topological.py | 120 + .../site-packages/sqlalchemy/util/typing.py | 580 ++ .../INSTALLER | 1 + .../LICENSE | 279 + .../METADATA | 66 + .../typing_extensions-4.11.0.dist-info/RECORD | 7 + .../typing_extensions-4.11.0.dist-info/WHEEL | 4 + .../site-packages/typing_extensions.py | 3332 +++++++ .../werkzeug-3.0.3.dist-info/INSTALLER | 1 + .../werkzeug-3.0.3.dist-info/LICENSE.txt | 28 + .../werkzeug-3.0.3.dist-info/METADATA | 99 + .../werkzeug-3.0.3.dist-info/RECORD | 125 + .../werkzeug-3.0.3.dist-info/WHEEL | 4 + .../site-packages/werkzeug/__init__.py | 25 + .../site-packages/werkzeug/_internal.py | 211 + .../site-packages/werkzeug/_reloader.py | 460 + .../werkzeug/datastructures/__init__.py | 34 + .../werkzeug/datastructures/accept.py | 326 + .../werkzeug/datastructures/accept.pyi | 54 + .../werkzeug/datastructures/auth.py | 316 + .../werkzeug/datastructures/cache_control.py | 175 + .../werkzeug/datastructures/cache_control.pyi | 115 + .../werkzeug/datastructures/csp.py | 94 + .../werkzeug/datastructures/csp.pyi | 169 + .../werkzeug/datastructures/etag.py | 95 + .../werkzeug/datastructures/etag.pyi | 30 + .../werkzeug/datastructures/file_storage.py | 196 + .../werkzeug/datastructures/file_storage.pyi | 49 + .../werkzeug/datastructures/headers.py | 515 + .../werkzeug/datastructures/headers.pyi | 109 + .../werkzeug/datastructures/mixins.py | 242 + .../werkzeug/datastructures/mixins.pyi | 97 + .../werkzeug/datastructures/range.py | 180 + .../werkzeug/datastructures/range.pyi | 57 + .../werkzeug/datastructures/structures.py | 1010 ++ .../werkzeug/datastructures/structures.pyi | 206 + .../site-packages/werkzeug/debug/__init__.py | 560 ++ .../site-packages/werkzeug/debug/console.py | 219 + .../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 | 360 + .../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 | 439 + .../site-packages/werkzeug/exceptions.py | 881 ++ .../site-packages/werkzeug/formparser.py | 423 + .../python3.10/site-packages/werkzeug/http.py | 1370 +++ .../site-packages/werkzeug/local.py | 653 ++ .../werkzeug/middleware/__init__.py | 0 .../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 | 282 + .../site-packages/werkzeug/py.typed | 0 .../werkzeug/routing/__init__.py | 134 + .../werkzeug/routing/converters.py | 261 + .../werkzeug/routing/exceptions.py | 152 + .../site-packages/werkzeug/routing/map.py | 951 ++ .../site-packages/werkzeug/routing/matcher.py | 202 + .../site-packages/werkzeug/routing/rules.py | 917 ++ .../site-packages/werkzeug/sansio/__init__.py | 0 .../site-packages/werkzeug/sansio/http.py | 171 + .../werkzeug/sansio/multipart.py | 321 + .../site-packages/werkzeug/sansio/request.py | 536 ++ .../site-packages/werkzeug/sansio/response.py | 754 ++ .../site-packages/werkzeug/sansio/utils.py | 159 + .../site-packages/werkzeug/security.py | 161 + .../site-packages/werkzeug/serving.py | 1116 +++ .../python3.10/site-packages/werkzeug/test.py | 1464 +++ .../site-packages/werkzeug/testapp.py | 194 + .../python3.10/site-packages/werkzeug/urls.py | 203 + .../site-packages/werkzeug/user_agent.py | 47 + .../site-packages/werkzeug/utils.py | 691 ++ .../werkzeug/wrappers/__init__.py | 3 + .../werkzeug/wrappers/request.py | 647 ++ .../werkzeug/wrappers/response.py | 831 ++ .../python3.10/site-packages/werkzeug/wsgi.py | 595 ++ flask_auth_app/authenv/lib64 | 1 + flask_auth_app/authenv/pyvenv.cfg | 3 + flask_auth_app/basededatos.py | 32 + flask_auth_app/initdb.sh | 1 + flask_auth_app/project/__init__.py | 28 + flask_auth_app/project/auth.py | 16 + flask_auth_app/project/main.py | 12 + flask_auth_app/project/models.py | 7 + flask_auth_app/project/templates/base.html | 51 + flask_auth_app/project/templates/index.html | 10 + flask_auth_app/project/templates/login.html | 29 + flask_auth_app/project/templates/profile.html | 7 + flask_auth_app/project/templates/signup.html | 30 + flask_auth_app/schema.sql | 8 + 1336 files changed, 562841 insertions(+), 44 deletions(-) create mode 100644 Padel/padel/auth.py create mode 100644 Padel/padel/models.py create mode 100644 Padel/padel/templates/autorizacion/index.html create mode 100644 Padel/padel/templates/autorizacion/login.html create mode 100644 Padel/padel/templates/autorizacion/profile.html create mode 100644 Padel/padel/templates/autorizacion/signup.html create mode 100644 flask_auth_app/authenv/bin/Activate.ps1 create mode 100644 flask_auth_app/authenv/bin/activate create mode 100644 flask_auth_app/authenv/bin/activate.csh create mode 100644 flask_auth_app/authenv/bin/activate.fish create mode 100755 flask_auth_app/authenv/bin/dotenv create mode 100755 flask_auth_app/authenv/bin/flask create mode 100755 flask_auth_app/authenv/bin/pip create mode 100755 flask_auth_app/authenv/bin/pip3 create mode 100755 flask_auth_app/authenv/bin/pip3.10 create mode 120000 flask_auth_app/authenv/bin/python create mode 120000 flask_auth_app/authenv/bin/python3 create mode 120000 flask_auth_app/authenv/bin/python3.10 create mode 100644 flask_auth_app/authenv/include/site/python3.10/greenlet/greenlet.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/LICENSE create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/REQUESTED create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/top_level.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/LICENSE.rst create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/top_level.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/LICENSE create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/top_level.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/_distutils_hack/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/_distutils_hack/override.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/LICENSE.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/blinker/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/blinker/_utilities.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/blinker/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/blinker/py.typed create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/LICENSE.rst create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/top_level.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/_compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/_termui_impl.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/_textwrap.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/_winconsole.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/core.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/decorators.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/exceptions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/formatting.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/globals.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/parser.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/py.typed create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/shell_completion.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/termui.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/testing.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/types.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/click/utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/distutils-precedence.pth create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/__main__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/cli.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/ipython.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/main.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/parser.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/py.typed create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/variables.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/version.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/LICENSE.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/REQUESTED create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/entry_points.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/__main__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/app.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/blueprints.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/cli.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/config.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/ctx.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/debughelpers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/globals.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/helpers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/json/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/json/provider.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/json/tag.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/logging.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/py.typed create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/README.md create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/app.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/blueprints.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/scaffold.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/sessions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/signals.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/templating.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/testing.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/typing.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/views.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask/wrappers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/__about__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/config.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/login_manager.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/mixins.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/signals.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/test_client.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/LICENSE.rst create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/REQUESTED create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/cli.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/extension.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/model.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/pagination.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/py.typed create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/query.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/record_queries.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/session.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/table.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/track_modifications.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/AUTHORS create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/LICENSE create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/LICENSE.PSF create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/top_level.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TBrokenGreenlet.cpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TExceptionState.cpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TGreenlet.cpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TGreenletGlobals.cpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TMainGreenlet.cpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TPythonState.cpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TStackState.cpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TThreadStateDestroy.cpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TUserGreenlet.cpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/__init__.py create mode 100755 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/_greenlet.cpython-310-x86_64-linux-gnu.so create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet.cpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_allocator.hpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_compiler_compat.hpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_cpython_add_pending.hpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_cpython_compat.hpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_exceptions.hpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_greenlet.hpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_internal.hpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_refs.hpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_slp_switch.hpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_thread_state.hpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_thread_state_dict_cleanup.hpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_thread_support.hpp create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/setup_switch_x64_masm.cmd create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_aarch64_gcc.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_alpha_unix.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_amd64_unix.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_gcc.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_ios.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.asm create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.obj create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_msvc.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_csky_gcc.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_loongarch64_linux.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_m68k_gcc.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_mips_unix.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc64_aix.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc64_linux.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_aix.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_linux.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_macosx.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_unix.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_riscv_unix.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_s390_unix.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_sparc_sun_gcc.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x32_unix.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x64_masm.asm create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x64_masm.obj create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x64_msvc.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x86_msvc.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x86_unix.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/slp_platformselect.h create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension.c create mode 100755 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension.cpython-310-x86_64-linux-gnu.so create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension_cpp.cpp create mode 100755 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension_cpp.cpython-310-x86_64-linux-gnu.so create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_clearing_run_switches.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_cpp_exception.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_initialstub_already_started.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_slp_switch.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_switch_three_greenlets.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_switch_three_greenlets2.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_switch_two_greenlets.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/leakcheck.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_contextvars.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_cpp.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_extension_interface.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_gc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_generator.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_generator_nested.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_greenlet.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_greenlet_trash.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_leaks.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_stack_saved.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_throw.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_tracing.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_version.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_weakref.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/LICENSE.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/_json.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/encoding.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/exc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/py.typed create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/serializer.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/signer.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/timed.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/url_safe.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/LICENSE.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/entry_points.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/_identifier.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/async_utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/bccache.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/compiler.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/constants.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/debug.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/defaults.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/environment.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/exceptions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/ext.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/filters.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/idtracking.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/lexer.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/loaders.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/meta.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/nativetypes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/nodes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/optimizer.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/parser.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/py.typed create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/runtime.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/sandbox.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/tests.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/visitor.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/markupsafe/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/markupsafe/_native.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/markupsafe/_speedups.c create mode 100755 flask_auth_app/authenv/lib/python3.10/site-packages/markupsafe/_speedups.cpython-310-x86_64-linux-gnu.so create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/markupsafe/_speedups.pyi create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/markupsafe/py.typed create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip-22.0.2.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip-22.0.2.dist-info/LICENSE.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip-22.0.2.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip-22.0.2.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip-22.0.2.dist-info/REQUESTED create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip-22.0.2.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip-22.0.2.dist-info/entry_points.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip-22.0.2.dist-info/top_level.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/__main__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/build_env.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cache.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cli/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cli/base_command.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cli/command_context.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cli/main.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cli/main_parser.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cli/parser.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cli/req_command.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cli/spinners.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/cli/status_codes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/cache.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/check.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/completion.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/configuration.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/debug.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/download.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/freeze.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/hash.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/help.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/index.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/install.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/list.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/search.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/show.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/uninstall.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/commands/wheel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/configuration.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/distributions/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/distributions/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/distributions/installed.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/distributions/sdist.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/distributions/wheel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/exceptions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/index/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/index/collector.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/index/package_finder.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/index/sources.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/locations/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/locations/_distutils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/locations/_sysconfig.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/locations/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/main.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/metadata/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/metadata/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/metadata/pkg_resources.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/models/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/models/candidate.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/models/direct_url.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/models/format_control.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/models/index.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/models/link.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/models/scheme.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/models/search_scope.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/models/target_python.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/models/wheel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/network/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/network/auth.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/network/cache.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/network/download.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/network/lazy_wheel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/network/session.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/network/utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/build/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata_editable.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/build/metadata_legacy.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel_editable.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/build/wheel_legacy.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/check.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/freeze.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/install/legacy.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/operations/prepare.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/pyproject.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/req/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/req/constructors.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/req/req_file.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/req/req_install.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/req/req_set.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/req/req_tracker.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/reporter.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/self_outdated_check.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/_log.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/appdirs.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/datetime.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/deprecation.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/distutils_args.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/egg_link.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/encoding.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/filesystem.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/filetypes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/glibc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/hashes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/inject_securetransport.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/logging.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/misc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/models.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/packaging.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/subprocess.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/unpacking.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/urls.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/utils/wheel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/vcs/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/vcs/git.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/vcs/subversion.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_internal/wheel_builder.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/certifi/core.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/enums.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/langrussianmodel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/metadata/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/metadata/languages.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/chardet/version.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/colorama/win32.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/database.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/index.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/locators.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/markers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/resources.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/version.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/distro.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_ihatexml.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_inputstream.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_tokenizer.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/_base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_trie/py.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/_utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/constants.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/lint.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/optionaltags.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/sanitizer.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/filters/whitespace.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/html5parser.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/serializer.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treeadapters/sax.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/dom.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/etree.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/dom.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/etree.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/idna/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/idna/codec.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/idna/compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/idna/core.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/idna/intranges.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/idna/package_data.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/msgpack/_version.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/msgpack/ext.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/packaging/_manylinux.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/packaging/_musllinux.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/packaging/markers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/packaging/tags.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/packaging/utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/packaging/version.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pep517/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pep517/build.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pep517/check.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pep517/colorlog.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pep517/compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pep517/dirtools.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pep517/envbuild.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pep517/in_process/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pep517/in_process/_in_process.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pep517/meta.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pep517/wrappers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pkg_resources/py31compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/platformdirs/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/platformdirs/__main__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/platformdirs/android.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/platformdirs/api.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/platformdirs/macos.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/platformdirs/unix.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/platformdirs/version.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/platformdirs/windows.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/progress/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/progress/bar.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/progress/colors.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/progress/counter.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/progress/spinner.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/__main__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/cmdline.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/console.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/filter.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/filters/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatter.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/_mapping.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/bbcode.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/groff.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/html.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/img.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/irc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/latex.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/other.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/pangomarkup.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/rtf.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/svg.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/terminal.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/formatters/terminal256.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/lexer.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/lexers/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/lexers/_mapping.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/lexers/python.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/modeline.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/plugin.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/regexopt.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/scanner.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/sphinxext.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/style.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/styles/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/token.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/unistring.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pygments/util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pyparsing/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pyparsing/actions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pyparsing/common.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pyparsing/core.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pyparsing/diagram/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pyparsing/exceptions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pyparsing/helpers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pyparsing/results.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pyparsing/testing.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pyparsing/unicode.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/pyparsing/util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/__version__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/adapters.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/api.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/auth.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/certs.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/cookies.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/help.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/hooks.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/models.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/packages.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/sessions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/structures.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/requests/utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/compat/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/providers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/reporters.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/resolvers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/resolvelib/structs.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/__main__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_cell_widths.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_emoji_codes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_emoji_replace.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_extension.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_inspect.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_log_render.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_loop.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_lru_cache.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_palettes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_pick.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_ratio.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_spinners.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_stack.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_timer.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_windows.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/_wrap.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/abc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/align.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/ansi.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/bar.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/box.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/cells.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/color.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/color_triplet.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/columns.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/console.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/constrain.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/containers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/control.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/default_styles.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/diagnose.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/emoji.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/errors.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/file_proxy.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/filesize.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/highlighter.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/json.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/jupyter.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/layout.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/live.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/live_render.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/logging.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/markup.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/measure.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/padding.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/pager.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/palette.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/panel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/pretty.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/progress.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/progress_bar.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/prompt.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/protocol.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/region.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/repr.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/rule.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/scope.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/screen.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/segment.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/spinner.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/status.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/style.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/styled.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/syntax.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/table.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/tabulate.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/terminal_theme.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/text.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/theme.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/themes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/traceback.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/rich/tree.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/six.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tenacity/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tenacity/_asyncio.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tenacity/_utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tenacity/after.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tenacity/before.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tenacity/before_sleep.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tenacity/nap.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tenacity/retry.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tenacity/stop.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tenacity/tornadoweb.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tenacity/wait.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tomli/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tomli/_parser.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/tomli/_re.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/typing_extensions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/_version.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/request.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/response.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/proxy.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/ssltransport.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/vendor.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pip/py.typed create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/_manylinux.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/_musllinux.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/tags.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/_vendor/pyparsing.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/extern/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/pkg_resources/tests/data/my-test-package-source/setup.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/python_dotenv-1.0.1.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/python_dotenv-1.0.1.dist-info/LICENSE create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/python_dotenv-1.0.1.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/python_dotenv-1.0.1.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/python_dotenv-1.0.1.dist-info/REQUESTED create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/python_dotenv-1.0.1.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/python_dotenv-1.0.1.dist-info/entry_points.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/python_dotenv-1.0.1.dist-info/top_level.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools-59.6.0.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools-59.6.0.dist-info/LICENSE create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools-59.6.0.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools-59.6.0.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools-59.6.0.dist-info/REQUESTED create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools-59.6.0.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools-59.6.0.dist-info/entry_points.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools-59.6.0.dist-info/top_level.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_deprecation_warning.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/_msvccompiler.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/archive_util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/bcppcompiler.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/ccompiler.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/cmd.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/bdist.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/bdist_dumb.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/bdist_msi.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/bdist_rpm.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/bdist_wininst.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/build.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/build_clib.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/build_ext.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/build_py.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/build_scripts.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/check.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/clean.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/config.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/install.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/install_data.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/install_egg_info.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/install_headers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/install_lib.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/install_scripts.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/py37compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/register.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/sdist.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/command/upload.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/config.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/core.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/cygwinccompiler.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/debug.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/dep_util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/dir_util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/dist.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/errors.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/extension.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/fancy_getopt.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/file_util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/filelist.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/log.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/msvc9compiler.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/msvccompiler.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/py35compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/py38compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/spawn.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/sysconfig.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/text_file.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/unixccompiler.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/version.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_distutils/versionpredicate.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_imp.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/more_itertools/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/more_itertools/more.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/more_itertools/recipes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/ordered_set.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/_manylinux.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/_musllinux.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/tags.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/_vendor/pyparsing.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/archive_util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/build_meta.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/cli-32.exe create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/cli-64.exe create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/cli-arm64.exe create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/cli.exe create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/alias.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/bdist_egg.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/bdist_rpm.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/build_clib.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/build_ext.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/build_py.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/develop.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/dist_info.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/easy_install.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/egg_info.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/install.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/install_egg_info.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/install_lib.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/install_scripts.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/launcher manifest.xml create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/py36compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/register.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/rotate.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/saveopts.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/sdist.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/setopt.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/test.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/upload.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/command/upload_docs.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/config.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/dep_util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/depends.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/dist.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/errors.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/extension.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/extern/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/glob.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/gui-32.exe create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/gui-64.exe create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/gui-arm64.exe create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/gui.exe create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/installer.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/launch.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/monkey.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/msvc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/namespaces.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/package_index.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/py34compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/sandbox.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/script (dev).tmpl create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/script.tmpl create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/unicode_utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/version.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/wheel.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/setuptools/windows_support.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/connectors/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/connectors/aioodbc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/connectors/asyncio.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/connectors/pyodbc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/cyextension/__init__.py create mode 100755 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/cyextension/collections.cpython-310-x86_64-linux-gnu.so create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/cyextension/collections.pyx create mode 100755 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/cyextension/immutabledict.cpython-310-x86_64-linux-gnu.so create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/cyextension/immutabledict.pxd create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/cyextension/immutabledict.pyx create mode 100755 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/cyextension/processors.cpython-310-x86_64-linux-gnu.so create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/cyextension/processors.pyx create mode 100755 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/cyextension/resultproxy.cpython-310-x86_64-linux-gnu.so create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/cyextension/resultproxy.pyx create mode 100755 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/cyextension/util.cpython-310-x86_64-linux-gnu.so create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/cyextension/util.pyx create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/_typing.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mssql/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mssql/aioodbc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mssql/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mssql/information_schema.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mssql/json.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mssql/provision.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mssql/pymssql.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mssql/pyodbc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/aiomysql.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/asyncmy.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/cymysql.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/dml.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/enumerated.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/expression.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/json.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/mariadb.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/mariadbconnector.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/mysqlconnector.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/mysqldb.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/provision.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/pymysql.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/pyodbc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/reflection.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/reserved_words.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/mysql/types.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/oracle/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/oracle/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/oracle/dictionary.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/oracle/oracledb.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/oracle/provision.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/oracle/types.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/_psycopg_common.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/array.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/dml.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/ext.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/hstore.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/json.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/named_types.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/operators.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/pg8000.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/pg_catalog.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/provision.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/psycopg.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/psycopg2cffi.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/ranges.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/postgresql/types.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/sqlite/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/sqlite/aiosqlite.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/sqlite/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/sqlite/dml.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/sqlite/json.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/sqlite/provision.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/sqlite/pysqlcipher.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/dialects/type_migration_guidelines.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/_py_processors.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/_py_row.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/_py_util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/characteristics.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/create.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/cursor.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/default.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/events.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/interfaces.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/mock.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/processors.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/reflection.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/result.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/row.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/strategies.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/url.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/engine/util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/event/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/event/api.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/event/attr.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/event/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/event/legacy.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/event/registry.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/events.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/exc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/associationproxy.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/asyncio/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/asyncio/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/asyncio/engine.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/asyncio/exc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/asyncio/result.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/asyncio/scoping.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/asyncio/session.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/automap.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/baked.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/compiler.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/declarative/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/declarative/extensions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/horizontal_shard.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/hybrid.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/indexable.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/instrumentation.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/mutable.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/mypy/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/mypy/apply.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/mypy/decl_class.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/mypy/infer.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/mypy/names.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/mypy/plugin.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/mypy/util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/orderinglist.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/ext/serializer.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/future/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/future/engine.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/inspection.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/log.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/_orm_constructors.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/_typing.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/attributes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/bulk_persistence.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/clsregistry.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/collections.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/context.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/decl_api.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/decl_base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/dependency.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/descriptor_props.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/dynamic.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/evaluator.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/events.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/exc.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/identity.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/instrumentation.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/interfaces.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/loading.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/mapped_collection.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/mapper.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/path_registry.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/persistence.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/properties.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/query.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/relationships.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/scoping.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/session.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/state.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/state_changes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/strategies.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/strategy_options.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/sync.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/unitofwork.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/orm/writeonly.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/pool/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/pool/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/pool/events.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/pool/impl.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/py.typed create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/schema.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/_dml_constructors.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/_elements_constructors.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/_orm_types.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/_py_util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/_selectable_constructors.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/_typing.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/annotation.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/cache_key.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/coercions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/compiler.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/crud.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/ddl.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/default_comparator.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/dml.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/elements.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/events.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/expression.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/functions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/lambdas.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/naming.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/operators.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/roles.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/schema.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/selectable.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/sqltypes.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/traversals.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/type_api.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/sql/visitors.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/assertions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/assertsql.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/asyncio.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/config.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/engines.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/entities.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/exclusions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/fixtures/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/fixtures/base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/fixtures/mypy.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/fixtures/orm.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/fixtures/sql.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/pickleable.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/plugin/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/plugin/bootstrap.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/plugin/plugin_base.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/plugin/pytestplugin.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/profiling.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/provision.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/requirements.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/schema.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_cte.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_ddl.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_deprecations.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_dialect.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_insert.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_reflection.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_results.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_rowcount.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_select.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_sequence.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_types.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_unicode_ddl.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/suite/test_update_delete.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/util.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/testing/warnings.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/types.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/_collections.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/_concurrency_py3k.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/_has_cy.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/_py_collections.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/compat.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/concurrency.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/deprecations.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/langhelpers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/preloaded.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/queue.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/tool_support.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/topological.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/sqlalchemy/util/typing.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/LICENSE create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/typing_extensions-4.11.0.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/typing_extensions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug-3.0.3.dist-info/INSTALLER create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug-3.0.3.dist-info/LICENSE.txt create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug-3.0.3.dist-info/METADATA create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug-3.0.3.dist-info/RECORD create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug-3.0.3.dist-info/WHEEL create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/_internal.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/_reloader.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/accept.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/accept.pyi create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/auth.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/cache_control.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/cache_control.pyi create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/csp.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/csp.pyi create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/etag.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/etag.pyi create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/file_storage.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/file_storage.pyi create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/headers.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/headers.pyi create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/mixins.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/mixins.pyi create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/range.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/range.pyi create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/structures.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/datastructures/structures.pyi create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/debug/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/debug/console.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/debug/repr.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/debug/shared/ICON_LICENSE.md create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/debug/shared/console.png create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/debug/shared/debugger.js create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/debug/shared/less.png create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/debug/shared/more.png create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/debug/shared/style.css create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/debug/tbtools.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/exceptions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/formparser.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/http.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/local.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/middleware/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/middleware/dispatcher.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/middleware/http_proxy.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/middleware/lint.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/middleware/profiler.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/middleware/proxy_fix.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/middleware/shared_data.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/py.typed create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/routing/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/routing/converters.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/routing/exceptions.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/routing/map.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/routing/matcher.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/routing/rules.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/sansio/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/sansio/http.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/sansio/multipart.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/sansio/request.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/sansio/response.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/sansio/utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/security.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/serving.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/test.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/testapp.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/urls.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/user_agent.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/utils.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/wrappers/__init__.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/wrappers/request.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/wrappers/response.py create mode 100644 flask_auth_app/authenv/lib/python3.10/site-packages/werkzeug/wsgi.py create mode 120000 flask_auth_app/authenv/lib64 create mode 100644 flask_auth_app/authenv/pyvenv.cfg create mode 100644 flask_auth_app/basededatos.py create mode 100644 flask_auth_app/initdb.sh create mode 100644 flask_auth_app/project/__init__.py create mode 100644 flask_auth_app/project/auth.py create mode 100644 flask_auth_app/project/main.py create mode 100644 flask_auth_app/project/models.py create mode 100644 flask_auth_app/project/templates/base.html create mode 100644 flask_auth_app/project/templates/index.html create mode 100644 flask_auth_app/project/templates/login.html create mode 100644 flask_auth_app/project/templates/profile.html create mode 100644 flask_auth_app/project/templates/signup.html create mode 100644 flask_auth_app/schema.sql diff --git a/Padel/padel/__init__.py b/Padel/padel/__init__.py index f04c1c8..7a06c32 100644 --- a/Padel/padel/__init__.py +++ b/Padel/padel/__init__.py @@ -1,8 +1,15 @@ import os from dotenv import load_dotenv from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_login import LoginManager -from padel import paginas, reservas, basededatos + + +# init SQLAlchemy so we can use it later in our models +db = SQLAlchemy() + +from padel import paginas, reservas, basededatos, auth load_dotenv() @@ -10,10 +17,34 @@ def create_app(): app = Flask(__name__) app.config.from_prefixed_env() + + app.config['SECRET_KEY'] = 'secret-key-goes-here' + app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite' + + db.init_app(app) + + login_manager = LoginManager() + login_manager.login_view = 'auth.login' + login_manager.init_app(app) + + from .models import User + + @login_manager.user_loader + def load_user(user_id): + # since the user_id is just the primary key of our user table, use it in the query for the user + return User.query.get(int(user_id)) + + + from . import models + + with app.app_context(): + db.create_all() + basededatos.init_app(app) app.register_blueprint(paginas.bp) app.register_blueprint(reservas.bp) + app.register_blueprint(auth.bp) print(f"Current Environment: {os.getenv('ENVIRONMENT')}") print(f"Using Database: {app.config.get('DATABASE')}") diff --git a/Padel/padel/auth.py b/Padel/padel/auth.py new file mode 100644 index 0000000..9311298 --- /dev/null +++ b/Padel/padel/auth.py @@ -0,0 +1,70 @@ +from flask import Blueprint, render_template, redirect, url_for, request, flash +from werkzeug.security import generate_password_hash, check_password_hash +from flask_login import login_user, logout_user, login_required +from .models import User +from . import db + +bp = Blueprint('auth', __name__) + +@bp.route('/login') +def login(): + return render_template('autorizacion/login.html') + + +@bp.route('/login', methods=['POST']) +def login_post(): + email = request.form.get('email') + password = request.form.get('password') + remember = True if request.form.get('remember') else False + + user = User.query.filter_by(email=email).first() + + # check if the user actually exists + # take the user-supplied password, hash it, and compare it to the hashed password in the database + if not user or not check_password_hash(user.password, password): + flash('Please check your login details and try again.') + return redirect(url_for('auth.login')) # if the user doesn't exist or password is wrong, reload the page + + # if the above check passes, then we know the user has the right credentials + login_user(user, remember=remember) + + return redirect(url_for('reservas.misreservas')) + +@bp.route('/signup') +def signup(): + return render_template('autorizacion/signup.html') + +@bp.route('/signup', methods=['POST']) +def signup_post(): + email = request.form.get('email') + name = request.form.get('name') + password = request.form.get('password') + + user = User.query.filter_by(email=email).first() # if this returns a user, then the email already exists in database + + if user: # if a user is found, we want to redirect back to signup page so user can try again + flash('La dirección de correo ya existe') + return redirect(url_for('auth.signup')) + + # create a new user with the form data. Hash the password so the plaintext version isn't saved. + new_user = User(email=email, name=name, password=generate_password_hash(password, method='pbkdf2:sha256')) + + # add the new user to the database + db.session.add(new_user) + db.session.commit() + + return redirect(url_for('auth.login')) + +@bp.route('/logout') +@login_required +def logout(): + logout_user() + return redirect(url_for('auth.index')) + +@bp.route('/index') +def index(): + return render_template('autorizacion/index.html') + +@bp.route('/profile') +def profile(): + return render_template('autorizacion/profile.html') \ No newline at end of file diff --git a/Padel/padel/models.py b/Padel/padel/models.py new file mode 100644 index 0000000..669d3d0 --- /dev/null +++ b/Padel/padel/models.py @@ -0,0 +1,9 @@ +from flask_login import UserMixin + +from . import db + +class User(UserMixin, db.Model): + id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy + email = db.Column(db.String(100), unique=True) + password = db.Column(db.String(100)) + name = db.Column(db.String(1000)) \ No newline at end of file diff --git a/Padel/padel/paginas.py b/Padel/padel/paginas.py index 08a13a8..94a61cb 100644 --- a/Padel/padel/paginas.py +++ b/Padel/padel/paginas.py @@ -1,14 +1,10 @@ from flask import ( Blueprint, - render_template, - request, redirect, - url_for, + render_template ) bp = Blueprint("paginas", __name__) -reservas = [] - @bp.route("/") def inicio(): return render_template("paginas/inicio.html") diff --git a/Padel/padel/reservas.py b/Padel/padel/reservas.py index f2cf305..c7bc38d 100644 --- a/Padel/padel/reservas.py +++ b/Padel/padel/reservas.py @@ -3,10 +3,13 @@ from flask import Blueprint, render_template, request, redirect, url_for from datetime import date, timedelta from padel.basededatos import get_db +from flask_login import login_required, current_user + bp = Blueprint("reservas", __name__) @bp.route("/misreservas") +@login_required def misreservas(): db = get_db() @@ -19,7 +22,7 @@ def misreservas(): "SELECT id, nombre, fecha, hora FROM reservas ORDER BY fecha DESC" ).fetchall() - return render_template("reservas/misreservas.html", reservas=reservas) + return render_template("reservas/misreservas.html", reservas=reservas, name=current_user.name) @bp.route("/inserta/", methods=['GET', 'POST']) def inserta(diaelegido): diff --git a/Padel/padel/schema.sql b/Padel/padel/schema.sql index c809c77..1bf8e10 100644 --- a/Padel/padel/schema.sql +++ b/Padel/padel/schema.sql @@ -7,23 +7,11 @@ CREATE TABLE reservas ( hora TIME NOT NULL ); +DROP TABLE IF EXISTS usuarios; -DROP TABLE IF EXISTS horas; - -CREATE TABLE horas ( - fecha TEXT NOT NULL, - hora0900 TEXT NOT NULL, - hora1000 TEXT NOT NULL, - hora1100 TEXT NOT NULL, - hora1200 TEXT NOT NULL, - hora1300 TEXT NOT NULL, - hora1400 TEXT NOT NULL, - hora1500 TEXT NOT NULL, - hora1600 TEXT NOT NULL, - hora1700 TEXT NOT NULL, - hora1800 TEXT NOT NULL, - hora1900 TEXT NOT NULL, - hora2000 TEXT NOT NULL, - hora2100 TEXT NOT NULL, - hora2200 TEXT NOT NULL -); \ No newline at end of file +CREATE TABLE usuarios ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT NOT NULL, + password TEXT NOT NULL, + nombre TEXT NOT NULL +); diff --git a/Padel/padel/templates/_navegacion.html b/Padel/padel/templates/_navegacion.html index 93673b5..2073f11 100644 --- a/Padel/padel/templates/_navegacion.html +++ b/Padel/padel/templates/_navegacion.html @@ -1,8 +1,22 @@ - \ No newline at end of file + diff --git a/Padel/padel/templates/autorizacion/index.html b/Padel/padel/templates/autorizacion/index.html new file mode 100644 index 0000000..ce45b56 --- /dev/null +++ b/Padel/padel/templates/autorizacion/index.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} + +{% block content %} +

+ Flask Login Example +

+

+ Easy authentication and authorization in Flask. +

+{% endblock %} \ No newline at end of file diff --git a/Padel/padel/templates/autorizacion/login.html b/Padel/padel/templates/autorizacion/login.html new file mode 100644 index 0000000..6ec1a6f --- /dev/null +++ b/Padel/padel/templates/autorizacion/login.html @@ -0,0 +1,36 @@ +{% extends "base.html" %} + +{% block content %} +
+

Login

+
+ {% with messages = get_flashed_messages() %} + {% if messages %} +
+ {{ messages[0] }} +
+ {% endif %} + {% endwith %} +
+
+
+ +
+
+ +
+
+ +
+
+
+ +
+ +
+
+
+{% endblock %} \ No newline at end of file diff --git a/Padel/padel/templates/autorizacion/profile.html b/Padel/padel/templates/autorizacion/profile.html new file mode 100644 index 0000000..b59fcc7 --- /dev/null +++ b/Padel/padel/templates/autorizacion/profile.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} + +{% block content %} +

+ Welcome, Anthony! +

+{% endblock %} \ No newline at end of file diff --git a/Padel/padel/templates/autorizacion/signup.html b/Padel/padel/templates/autorizacion/signup.html new file mode 100644 index 0000000..7c77864 --- /dev/null +++ b/Padel/padel/templates/autorizacion/signup.html @@ -0,0 +1,39 @@ +{% extends "base.html" %} + +{% block content %} +
+

Sign Up

+
+ + {% with messages = get_flashed_messages() %} + {% if messages %} +
+ {{ messages[0] }}. Go to login page. +
+ {% endif %} + {% endwith %} + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + +
+
+
+{% endblock %} diff --git a/Padel/padel/templates/base.html b/Padel/padel/templates/base.html index 250283f..cfb01b4 100644 --- a/Padel/padel/templates/base.html +++ b/Padel/padel/templates/base.html @@ -8,24 +8,27 @@ Reservas de padel - {% block title %}{% endblock title %} - + + + - -

Reservas de padel

- {% include("_navegacion.html") %} -
+
+ {% include("_navegacion.html") %} +
{% block header %}{% endblock header %}
- {% block content %}{% endblock content %} -
+
+
+ {% block content %} + {% endblock %} +
+
diff --git a/Padel/padel/templates/reservas/misreservas.html b/Padel/padel/templates/reservas/misreservas.html index b2c741e..271e521 100644 --- a/Padel/padel/templates/reservas/misreservas.html +++ b/Padel/padel/templates/reservas/misreservas.html @@ -1,7 +1,10 @@ {% extends 'base.html' %} {% block header %} -

{% block title %}Reservas de padel{% endblock title %}

+

+ Welcome, {{ name }}! +

+

{% block title %}Reservas de padel{% endblock title %}

{% endblock header %} {% block content %} diff --git a/flask_auth_app/authenv/bin/Activate.ps1 b/flask_auth_app/authenv/bin/Activate.ps1 new file mode 100644 index 0000000..b49d77b --- /dev/null +++ b/flask_auth_app/authenv/bin/Activate.ps1 @@ -0,0 +1,247 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/flask_auth_app/authenv/bin/activate b/flask_auth_app/authenv/bin/activate new file mode 100644 index 0000000..998066b --- /dev/null +++ b/flask_auth_app/authenv/bin/activate @@ -0,0 +1,69 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/home/creylopez/AppsPy/flask_auth_app/authenv" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="(authenv) ${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT="(authenv) " + export VIRTUAL_ENV_PROMPT +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r 2> /dev/null +fi diff --git a/flask_auth_app/authenv/bin/activate.csh b/flask_auth_app/authenv/bin/activate.csh new file mode 100644 index 0000000..93ff71b --- /dev/null +++ b/flask_auth_app/authenv/bin/activate.csh @@ -0,0 +1,26 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/home/creylopez/AppsPy/flask_auth_app/authenv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = "(authenv) $prompt" + setenv VIRTUAL_ENV_PROMPT "(authenv) " +endif + +alias pydoc python -m pydoc + +rehash diff --git a/flask_auth_app/authenv/bin/activate.fish b/flask_auth_app/authenv/bin/activate.fish new file mode 100644 index 0000000..9f50304 --- /dev/null +++ b/flask_auth_app/authenv/bin/activate.fish @@ -0,0 +1,69 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/); you cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + set -e _OLD_FISH_PROMPT_OVERRIDE + # prevents error when using nested fish instances (Issue #93858) + if functions -q _old_fish_prompt + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV "/home/creylopez/AppsPy/flask_auth_app/authenv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) "(authenv) " (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT "(authenv) " +end diff --git a/flask_auth_app/authenv/bin/dotenv b/flask_auth_app/authenv/bin/dotenv new file mode 100755 index 0000000..74538e3 --- /dev/null +++ b/flask_auth_app/authenv/bin/dotenv @@ -0,0 +1,8 @@ +#!/home/creylopez/AppsPy/flask_auth_app/authenv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from dotenv.__main__ import cli +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(cli()) diff --git a/flask_auth_app/authenv/bin/flask b/flask_auth_app/authenv/bin/flask new file mode 100755 index 0000000..6f2dc35 --- /dev/null +++ b/flask_auth_app/authenv/bin/flask @@ -0,0 +1,8 @@ +#!/home/creylopez/AppsPy/flask_auth_app/authenv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from flask.cli import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/flask_auth_app/authenv/bin/pip b/flask_auth_app/authenv/bin/pip new file mode 100755 index 0000000..99ea402 --- /dev/null +++ b/flask_auth_app/authenv/bin/pip @@ -0,0 +1,8 @@ +#!/home/creylopez/AppsPy/flask_auth_app/authenv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/flask_auth_app/authenv/bin/pip3 b/flask_auth_app/authenv/bin/pip3 new file mode 100755 index 0000000..99ea402 --- /dev/null +++ b/flask_auth_app/authenv/bin/pip3 @@ -0,0 +1,8 @@ +#!/home/creylopez/AppsPy/flask_auth_app/authenv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/flask_auth_app/authenv/bin/pip3.10 b/flask_auth_app/authenv/bin/pip3.10 new file mode 100755 index 0000000..99ea402 --- /dev/null +++ b/flask_auth_app/authenv/bin/pip3.10 @@ -0,0 +1,8 @@ +#!/home/creylopez/AppsPy/flask_auth_app/authenv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/flask_auth_app/authenv/bin/python b/flask_auth_app/authenv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/flask_auth_app/authenv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/flask_auth_app/authenv/bin/python3 b/flask_auth_app/authenv/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/flask_auth_app/authenv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/flask_auth_app/authenv/bin/python3.10 b/flask_auth_app/authenv/bin/python3.10 new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/flask_auth_app/authenv/bin/python3.10 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/flask_auth_app/authenv/include/site/python3.10/greenlet/greenlet.h b/flask_auth_app/authenv/include/site/python3.10/greenlet/greenlet.h new file mode 100644 index 0000000..d02a16e --- /dev/null +++ b/flask_auth_app/authenv/include/site/python3.10/greenlet/greenlet.h @@ -0,0 +1,164 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ + +/* Greenlet object interface */ + +#ifndef Py_GREENLETOBJECT_H +#define Py_GREENLETOBJECT_H + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is deprecated and undocumented. It does not change. */ +#define GREENLET_VERSION "1.0.0" + +#ifndef GREENLET_MODULE +#define implementation_ptr_t void* +#endif + +typedef struct _greenlet { + PyObject_HEAD + PyObject* weakreflist; + PyObject* dict; + implementation_ptr_t pimpl; +} PyGreenlet; + +#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type)) + + +/* C API functions */ + +/* Total number of symbols that are exported */ +#define PyGreenlet_API_pointers 12 + +#define PyGreenlet_Type_NUM 0 +#define PyExc_GreenletError_NUM 1 +#define PyExc_GreenletExit_NUM 2 + +#define PyGreenlet_New_NUM 3 +#define PyGreenlet_GetCurrent_NUM 4 +#define PyGreenlet_Throw_NUM 5 +#define PyGreenlet_Switch_NUM 6 +#define PyGreenlet_SetParent_NUM 7 + +#define PyGreenlet_MAIN_NUM 8 +#define PyGreenlet_STARTED_NUM 9 +#define PyGreenlet_ACTIVE_NUM 10 +#define PyGreenlet_GET_PARENT_NUM 11 + +#ifndef GREENLET_MODULE +/* This section is used by modules that uses the greenlet C API */ +static void** _PyGreenlet_API = NULL; + +# define PyGreenlet_Type \ + (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM]) + +# define PyExc_GreenletError \ + ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM]) + +# define PyExc_GreenletExit \ + ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM]) + +/* + * PyGreenlet_New(PyObject *args) + * + * greenlet.greenlet(run, parent=None) + */ +# define PyGreenlet_New \ + (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \ + _PyGreenlet_API[PyGreenlet_New_NUM]) + +/* + * PyGreenlet_GetCurrent(void) + * + * greenlet.getcurrent() + */ +# define PyGreenlet_GetCurrent \ + (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) + +/* + * PyGreenlet_Throw( + * PyGreenlet *greenlet, + * PyObject *typ, + * PyObject *val, + * PyObject *tb) + * + * g.throw(...) + */ +# define PyGreenlet_Throw \ + (*(PyObject * (*)(PyGreenlet * self, \ + PyObject * typ, \ + PyObject * val, \ + PyObject * tb)) \ + _PyGreenlet_API[PyGreenlet_Throw_NUM]) + +/* + * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) + * + * g.switch(*args, **kwargs) + */ +# define PyGreenlet_Switch \ + (*(PyObject * \ + (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \ + _PyGreenlet_API[PyGreenlet_Switch_NUM]) + +/* + * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) + * + * g.parent = new_parent + */ +# define PyGreenlet_SetParent \ + (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \ + _PyGreenlet_API[PyGreenlet_SetParent_NUM]) + +/* + * PyGreenlet_GetParent(PyObject* greenlet) + * + * return greenlet.parent; + * + * This could return NULL even if there is no exception active. + * If it does not return NULL, you are responsible for decrementing the + * reference count. + */ +# define PyGreenlet_GetParent \ + (*(PyGreenlet* (*)(PyGreenlet*)) \ + _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM]) + +/* + * deprecated, undocumented alias. + */ +# define PyGreenlet_GET_PARENT PyGreenlet_GetParent + +# define PyGreenlet_MAIN \ + (*(int (*)(PyGreenlet*)) \ + _PyGreenlet_API[PyGreenlet_MAIN_NUM]) + +# define PyGreenlet_STARTED \ + (*(int (*)(PyGreenlet*)) \ + _PyGreenlet_API[PyGreenlet_STARTED_NUM]) + +# define PyGreenlet_ACTIVE \ + (*(int (*)(PyGreenlet*)) \ + _PyGreenlet_API[PyGreenlet_ACTIVE_NUM]) + + + + +/* Macro that imports greenlet and initializes C API */ +/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we + keep the older definition to be sure older code that might have a copy of + the header still works. */ +# define PyGreenlet_Import() \ + { \ + _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ + } + +#endif /* GREENLET_MODULE */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GREENLETOBJECT_H */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/INSTALLER b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/LICENSE b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/LICENSE new file mode 100644 index 0000000..0446381 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2011 Matthew Frazier + +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/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/METADATA b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/METADATA new file mode 100644 index 0000000..445a0b2 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/METADATA @@ -0,0 +1,183 @@ +Metadata-Version: 2.1 +Name: Flask-Login +Version: 0.6.3 +Summary: User authentication and session management for Flask. +Home-page: https://github.com/maxcountryman/flask-login +Author: Matthew Frazier +Author-email: leafstormrush@gmail.com +Maintainer: Max Countryman +License: MIT +Project-URL: Documentation, https://flask-login.readthedocs.io/ +Project-URL: Changes, https://github.com/maxcountryman/flask-login/blob/main/CHANGES.md +Project-URL: Source Code, https://github.com/maxcountryman/flask-login +Project-URL: Issue Tracker, https://github.com/maxcountryman/flask-login/issues +Classifier: Development Status :: 4 - Beta +Classifier: Environment :: Web Environment +Classifier: Framework :: Flask +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: Flask >=1.0.4 +Requires-Dist: Werkzeug >=1.0.1 + +# Flask-Login + +![Tests](https://github.com/maxcountryman/flask-login/workflows/Tests/badge.svg) +[![coverage](https://coveralls.io/repos/maxcountryman/flask-login/badge.svg?branch=main&service=github)](https://coveralls.io/github/maxcountryman/flask-login?branch=main) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE) + +Flask-Login provides user session management for Flask. It handles the common +tasks of logging in, logging out, and remembering your users' sessions over +extended periods of time. + +Flask-Login is not bound to any particular database system or permissions +model. The only requirement is that your user objects implement a few methods, +and that you provide a callback to the extension capable of loading users from +their ID. + +## Installation + +Install the extension with pip: + +```sh +$ pip install flask-login +``` + +## Usage + +Once installed, the Flask-Login is easy to use. Let's walk through setting up +a basic application. Also please note that this is a very basic guide: we will +be taking shortcuts here that you should never take in a real application. + +To begin we'll set up a Flask app: + +```python +import flask + +app = flask.Flask(__name__) +app.secret_key = 'super secret string' # Change this! +``` + +Flask-Login works via a login manager. To kick things off, we'll set up the +login manager by instantiating it and telling it about our Flask app: + +```python +import flask_login + +login_manager = flask_login.LoginManager() + +login_manager.init_app(app) +``` + +To keep things simple we're going to use a dictionary to represent a database +of users. In a real application, this would be an actual persistence layer. +However it's important to point out this is a feature of Flask-Login: it +doesn't care how your data is stored so long as you tell it how to retrieve it! + +```python +# Our mock database. +users = {'foo@bar.tld': {'password': 'secret'}} +``` + +We also need to tell Flask-Login how to load a user from a Flask request and +from its session. To do this we need to define our user object, a +`user_loader` callback, and a `request_loader` callback. + +```python +class User(flask_login.UserMixin): + pass + + +@login_manager.user_loader +def user_loader(email): + if email not in users: + return + + user = User() + user.id = email + return user + + +@login_manager.request_loader +def request_loader(request): + email = request.form.get('email') + if email not in users: + return + + user = User() + user.id = email + return user +``` + +Now we're ready to define our views. We can start with a login view, which will +populate the session with authentication bits. After that we can define a view +that requires authentication. + +```python +@app.route('/login', methods=['GET', 'POST']) +def login(): + if flask.request.method == 'GET': + return ''' +
+ + + +
+ ''' + + email = flask.request.form['email'] + if email in users and flask.request.form['password'] == users[email]['password']: + user = User() + user.id = email + flask_login.login_user(user) + return flask.redirect(flask.url_for('protected')) + + return 'Bad login' + + +@app.route('/protected') +@flask_login.login_required +def protected(): + return 'Logged in as: ' + flask_login.current_user.id +``` + +Finally we can define a view to clear the session and log users out: + +```python +@app.route('/logout') +def logout(): + flask_login.logout_user() + return 'Logged out' +``` + +We now have a basic working application that makes use of session-based +authentication. To round things off, we should provide a callback for login +failures: + +```python +@login_manager.unauthorized_handler +def unauthorized_handler(): + return 'Unauthorized', 401 +``` + +Documentation for Flask-Login is available on [ReadTheDocs](https://flask-login.readthedocs.io/en/latest/). +For complete understanding of available configuration, please refer to the [source code](https://github.com/maxcountryman/flask-login). + + +## Contributing + +We welcome contributions! If you would like to hack on Flask-Login, please +follow these steps: + +1. Fork this repository +2. Make your changes +3. Install the dev requirements with `pip install -r requirements/dev.txt` +4. Submit a pull request after running `tox` (ensure it does not error!) + +Please give us adequate time to review your submission. Thanks! diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/RECORD b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/RECORD new file mode 100644 index 0000000..2c638e4 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/RECORD @@ -0,0 +1,23 @@ +Flask_Login-0.6.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask_Login-0.6.3.dist-info/LICENSE,sha256=ep37nF2iBO0TcPO2LBPimSoS2h2nB_R-FWiX7rQ0Tls,1059 +Flask_Login-0.6.3.dist-info/METADATA,sha256=AUSHR5Po6-Cwmz1KBrAZbTzR-iVVFvtb2NQKYl7UuAU,5799 +Flask_Login-0.6.3.dist-info/RECORD,, +Flask_Login-0.6.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Flask_Login-0.6.3.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92 +Flask_Login-0.6.3.dist-info/top_level.txt,sha256=OuXmIpiFnXLvW-iBbW2km7ZIy5EZvwSBnYaOC3Kt7j8,12 +flask_login/__about__.py,sha256=Kkp5e9mV9G7vK_FqZof-g9RFmyyBzq1gge5aKXgvilE,389 +flask_login/__init__.py,sha256=wYQiQCikT_Ndp3PhOD-1gRTGCrUPIE-FrjQUrT9aVAg,2681 +flask_login/__pycache__/__about__.cpython-310.pyc,, +flask_login/__pycache__/__init__.cpython-310.pyc,, +flask_login/__pycache__/config.cpython-310.pyc,, +flask_login/__pycache__/login_manager.cpython-310.pyc,, +flask_login/__pycache__/mixins.cpython-310.pyc,, +flask_login/__pycache__/signals.cpython-310.pyc,, +flask_login/__pycache__/test_client.cpython-310.pyc,, +flask_login/__pycache__/utils.cpython-310.pyc,, +flask_login/config.py,sha256=YAocv18La7YGQyNY5aT7rU1GQIZnX6pvchwqx3kA9p8,1813 +flask_login/login_manager.py,sha256=h20F_iv3mqc6rIJ4-V6_XookzOUl8Rcpasua-dCByQY,20073 +flask_login/mixins.py,sha256=gPd7otMRljxw0eUhUMbHsnEBc_jK2cYdxg5KFLuJcoI,1528 +flask_login/signals.py,sha256=xCMoFHKU1RTVt1NY-Gfl0OiVKpiyNt6YJw_PsgkjY3w,2464 +flask_login/test_client.py,sha256=6mrjiBRLGJpgvvFlLypXPTBLiMp0BAN-Ft-uogqC81g,517 +flask_login/utils.py,sha256=Y1wxjCVxpYohBaQJ0ADLypQ-VvBNycwG-gVXFF7k99I,14021 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/REQUESTED b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/WHEEL b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/WHEEL new file mode 100644 index 0000000..ba48cbc --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.41.3) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/top_level.txt b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/top_level.txt new file mode 100644 index 0000000..31514bd --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/Flask_Login-0.6.3.dist-info/top_level.txt @@ -0,0 +1 @@ +flask_login diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/INSTALLER b/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/LICENSE.rst b/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/METADATA b/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/METADATA new file mode 100644 index 0000000..dfe37d5 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/METADATA @@ -0,0 +1,93 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 2.1.5 +Summary: Safely add untrusted strings to HTML/XML markup. +Home-page: https://palletsprojects.com/p/markupsafe/ +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/markupsafe/ +Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/ +Project-URL: Chat, https://discord.gg/pallets +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst + +MarkupSafe +========== + +MarkupSafe implements a text object that escapes characters so it is +safe to use in HTML and XML. Characters that have special meanings are +replaced so that they display as the actual characters. This mitigates +injection attacks, meaning untrusted user input can safely be displayed +on a page. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U MarkupSafe + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +Examples +-------- + +.. code-block:: pycon + + >>> from markupsafe import Markup, escape + + >>> # escape replaces special characters and wraps in Markup + >>> escape("") + Markup('<script>alert(document.cookie);</script>') + + >>> # wrap in Markup to mark text "safe" and prevent escaping + >>> Markup("Hello") + Markup('hello') + + >>> escape(Markup("Hello")) + Markup('hello') + + >>> # Markup is a str subclass + >>> # methods and operators escape their arguments + >>> template = Markup("Hello {name}") + >>> template.format(name='"World"') + Markup('Hello "World"') + + +Donate +------ + +The Pallets organization develops and supports MarkupSafe and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://markupsafe.palletsprojects.com/ +- Changes: https://markupsafe.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/MarkupSafe/ +- Source Code: https://github.com/pallets/markupsafe/ +- Issue Tracker: https://github.com/pallets/markupsafe/issues/ +- Chat: https://discord.gg/pallets diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/RECORD b/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/RECORD new file mode 100644 index 0000000..1a961ed --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/RECORD @@ -0,0 +1,14 @@ +MarkupSafe-2.1.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-2.1.5.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +MarkupSafe-2.1.5.dist-info/METADATA,sha256=2dRDPam6OZLfpX0wg1JN5P3u9arqACxVSfdGmsJU7o8,3003 +MarkupSafe-2.1.5.dist-info/RECORD,, +MarkupSafe-2.1.5.dist-info/WHEEL,sha256=1FEjxEYgybphwh9S0FO9IcZ0B-NIeM2ko8OzhFZeOeQ,152 +MarkupSafe-2.1.5.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=r7VOTjUq7EMQ4v3p4R1LoVOGJg6ysfYRncLr34laRBs,10958 +markupsafe/__pycache__/__init__.cpython-310.pyc,, +markupsafe/__pycache__/_native.cpython-310.pyc,, +markupsafe/_native.py,sha256=GR86Qvo_GcgKmKreA1WmYN9ud17OFwkww8E-fiW-57s,1713 +markupsafe/_speedups.c,sha256=X2XvQVtIdcK4Usz70BvkzoOfjTCmQlDkkjYSn-swE0g,7083 +markupsafe/_speedups.cpython-310-x86_64-linux-gnu.so,sha256=kPt-fhZ_RG7PUbDvwmyC26ZvRJ9DvUlF3hszBIB6_xs,44240 +markupsafe/_speedups.pyi,sha256=vfMCsOgbAXRNLUXkyuyonG8uEWKYU4PDqNuMaDELAYw,229 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/WHEEL b/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/WHEEL new file mode 100644 index 0000000..1d81251 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: false +Tag: cp310-cp310-manylinux_2_17_x86_64 +Tag: cp310-cp310-manylinux2014_x86_64 + diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/top_level.txt b/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/top_level.txt new file mode 100644 index 0000000..75bf729 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/MarkupSafe-2.1.5.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/INSTALLER b/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/LICENSE b/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/LICENSE new file mode 100644 index 0000000..967cdc5 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright 2005-2024 SQLAlchemy authors and contributors . + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/METADATA b/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/METADATA new file mode 100644 index 0000000..c21e152 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/METADATA @@ -0,0 +1,242 @@ +Metadata-Version: 2.1 +Name: SQLAlchemy +Version: 2.0.30 +Summary: Database Abstraction Library +Home-page: https://www.sqlalchemy.org +Author: Mike Bayer +Author-email: mike_mp@zzzcomputing.com +License: MIT +Project-URL: Documentation, https://docs.sqlalchemy.org +Project-URL: Issue Tracker, https://github.com/sqlalchemy/sqlalchemy/ +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 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 :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Database :: Front-Ends +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: typing-extensions >=4.6.0 +Requires-Dist: greenlet !=0.4.17 ; platform_machine == "aarch64" or (platform_machine == "ppc64le" or (platform_machine == "x86_64" or (platform_machine == "amd64" or (platform_machine == "AMD64" or (platform_machine == "win32" or platform_machine == "WIN32"))))) +Requires-Dist: importlib-metadata ; python_version < "3.8" +Provides-Extra: aiomysql +Requires-Dist: greenlet !=0.4.17 ; extra == 'aiomysql' +Requires-Dist: aiomysql >=0.2.0 ; extra == 'aiomysql' +Provides-Extra: aioodbc +Requires-Dist: greenlet !=0.4.17 ; extra == 'aioodbc' +Requires-Dist: aioodbc ; extra == 'aioodbc' +Provides-Extra: aiosqlite +Requires-Dist: greenlet !=0.4.17 ; extra == 'aiosqlite' +Requires-Dist: aiosqlite ; extra == 'aiosqlite' +Requires-Dist: typing-extensions !=3.10.0.1 ; extra == 'aiosqlite' +Provides-Extra: asyncio +Requires-Dist: greenlet !=0.4.17 ; extra == 'asyncio' +Provides-Extra: asyncmy +Requires-Dist: greenlet !=0.4.17 ; extra == 'asyncmy' +Requires-Dist: asyncmy !=0.2.4,!=0.2.6,>=0.2.3 ; extra == 'asyncmy' +Provides-Extra: mariadb_connector +Requires-Dist: mariadb !=1.1.2,!=1.1.5,>=1.0.1 ; extra == 'mariadb_connector' +Provides-Extra: mssql +Requires-Dist: pyodbc ; extra == 'mssql' +Provides-Extra: mssql_pymssql +Requires-Dist: pymssql ; extra == 'mssql_pymssql' +Provides-Extra: mssql_pyodbc +Requires-Dist: pyodbc ; extra == 'mssql_pyodbc' +Provides-Extra: mypy +Requires-Dist: mypy >=0.910 ; extra == 'mypy' +Provides-Extra: mysql +Requires-Dist: mysqlclient >=1.4.0 ; extra == 'mysql' +Provides-Extra: mysql_connector +Requires-Dist: mysql-connector-python ; extra == 'mysql_connector' +Provides-Extra: oracle +Requires-Dist: cx-oracle >=8 ; extra == 'oracle' +Provides-Extra: oracle_oracledb +Requires-Dist: oracledb >=1.0.1 ; extra == 'oracle_oracledb' +Provides-Extra: postgresql +Requires-Dist: psycopg2 >=2.7 ; extra == 'postgresql' +Provides-Extra: postgresql_asyncpg +Requires-Dist: greenlet !=0.4.17 ; extra == 'postgresql_asyncpg' +Requires-Dist: asyncpg ; extra == 'postgresql_asyncpg' +Provides-Extra: postgresql_pg8000 +Requires-Dist: pg8000 >=1.29.1 ; extra == 'postgresql_pg8000' +Provides-Extra: postgresql_psycopg +Requires-Dist: psycopg >=3.0.7 ; extra == 'postgresql_psycopg' +Provides-Extra: postgresql_psycopg2binary +Requires-Dist: psycopg2-binary ; extra == 'postgresql_psycopg2binary' +Provides-Extra: postgresql_psycopg2cffi +Requires-Dist: psycopg2cffi ; extra == 'postgresql_psycopg2cffi' +Provides-Extra: postgresql_psycopgbinary +Requires-Dist: psycopg[binary] >=3.0.7 ; extra == 'postgresql_psycopgbinary' +Provides-Extra: pymysql +Requires-Dist: pymysql ; extra == 'pymysql' +Provides-Extra: sqlcipher +Requires-Dist: sqlcipher3-binary ; extra == 'sqlcipher' + +SQLAlchemy +========== + +|PyPI| |Python| |Downloads| + +.. |PyPI| image:: https://img.shields.io/pypi/v/sqlalchemy + :target: https://pypi.org/project/sqlalchemy + :alt: PyPI + +.. |Python| image:: https://img.shields.io/pypi/pyversions/sqlalchemy + :target: https://pypi.org/project/sqlalchemy + :alt: PyPI - Python Version + +.. |Downloads| image:: https://static.pepy.tech/badge/sqlalchemy/month + :target: https://pepy.tech/project/sqlalchemy + :alt: PyPI - Downloads + + +The Python SQL Toolkit and Object Relational Mapper + +Introduction +------------- + +SQLAlchemy is the Python SQL toolkit and Object Relational Mapper +that gives application developers the full power and +flexibility of SQL. SQLAlchemy provides a full suite +of well known enterprise-level persistence patterns, +designed for efficient and high-performing database +access, adapted into a simple and Pythonic domain +language. + +Major SQLAlchemy features include: + +* An industrial strength ORM, built + from the core on the identity map, unit of work, + and data mapper patterns. These patterns + allow transparent persistence of objects + using a declarative configuration system. + Domain models + can be constructed and manipulated naturally, + and changes are synchronized with the + current transaction automatically. +* A relationally-oriented query system, exposing + the full range of SQL's capabilities + explicitly, including joins, subqueries, + correlation, and most everything else, + in terms of the object model. + Writing queries with the ORM uses the same + techniques of relational composition you use + when writing SQL. While you can drop into + literal SQL at any time, it's virtually never + needed. +* A comprehensive and flexible system + of eager loading for related collections and objects. + Collections are cached within a session, + and can be loaded on individual access, all + at once using joins, or by query per collection + across the full result set. +* A Core SQL construction system and DBAPI + interaction layer. The SQLAlchemy Core is + separate from the ORM and is a full database + abstraction layer in its own right, and includes + an extensible Python-based SQL expression + language, schema metadata, connection pooling, + type coercion, and custom types. +* All primary and foreign key constraints are + assumed to be composite and natural. Surrogate + integer primary keys are of course still the + norm, but SQLAlchemy never assumes or hardcodes + to this model. +* Database introspection and generation. Database + schemas can be "reflected" in one step into + Python structures representing database metadata; + those same structures can then generate + CREATE statements right back out - all within + the Core, independent of the ORM. + +SQLAlchemy's philosophy: + +* SQL databases behave less and less like object + collections the more size and performance start to + matter; object collections behave less and less like + tables and rows the more abstraction starts to matter. + SQLAlchemy aims to accommodate both of these + principles. +* An ORM doesn't need to hide the "R". A relational + database provides rich, set-based functionality + that should be fully exposed. SQLAlchemy's + ORM provides an open-ended set of patterns + that allow a developer to construct a custom + mediation layer between a domain model and + a relational schema, turning the so-called + "object relational impedance" issue into + a distant memory. +* The developer, in all cases, makes all decisions + regarding the design, structure, and naming conventions + of both the object model as well as the relational + schema. SQLAlchemy only provides the means + to automate the execution of these decisions. +* With SQLAlchemy, there's no such thing as + "the ORM generated a bad query" - you + retain full control over the structure of + queries, including how joins are organized, + how subqueries and correlation is used, what + columns are requested. Everything SQLAlchemy + does is ultimately the result of a developer-initiated + decision. +* Don't use an ORM if the problem doesn't need one. + SQLAlchemy consists of a Core and separate ORM + component. The Core offers a full SQL expression + language that allows Pythonic construction + of SQL constructs that render directly to SQL + strings for a target database, returning + result sets that are essentially enhanced DBAPI + cursors. +* Transactions should be the norm. With SQLAlchemy's + ORM, nothing goes to permanent storage until + commit() is called. SQLAlchemy encourages applications + to create a consistent means of delineating + the start and end of a series of operations. +* Never render a literal value in a SQL statement. + Bound parameters are used to the greatest degree + possible, allowing query optimizers to cache + query plans effectively and making SQL injection + attacks a non-issue. + +Documentation +------------- + +Latest documentation is at: + +https://www.sqlalchemy.org/docs/ + +Installation / Requirements +--------------------------- + +Full documentation for installation is at +`Installation `_. + +Getting Help / Development / Bug reporting +------------------------------------------ + +Please refer to the `SQLAlchemy Community Guide `_. + +Code of Conduct +--------------- + +Above all, SQLAlchemy places great emphasis on polite, thoughtful, and +constructive communication between users and developers. +Please see our current Code of Conduct at +`Code of Conduct `_. + +License +------- + +SQLAlchemy is distributed under the `MIT license +`_. + diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/RECORD b/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/RECORD new file mode 100644 index 0000000..b1447f9 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/RECORD @@ -0,0 +1,529 @@ +SQLAlchemy-2.0.30.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +SQLAlchemy-2.0.30.dist-info/LICENSE,sha256=PA9Zq4h9BB3mpOUv_j6e212VIt6Qn66abNettue-MpM,1100 +SQLAlchemy-2.0.30.dist-info/METADATA,sha256=je_u9qkpPX36jMg6zWrd8VjcEuly2P0zmsxfGP9GAHE,9602 +SQLAlchemy-2.0.30.dist-info/RECORD,, +SQLAlchemy-2.0.30.dist-info/WHEEL,sha256=CzQQWV-lNyM92gr3iaBk8dvO35YDHRxgzkZ-dxumUIM,152 +SQLAlchemy-2.0.30.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11 +sqlalchemy/__init__.py,sha256=yI12J_Tdp5kmBfaR8P8wQjcZg4U0qua6pbbB7JTvCsw,13033 +sqlalchemy/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/__pycache__/events.cpython-310.pyc,, +sqlalchemy/__pycache__/exc.cpython-310.pyc,, +sqlalchemy/__pycache__/inspection.cpython-310.pyc,, +sqlalchemy/__pycache__/log.cpython-310.pyc,, +sqlalchemy/__pycache__/schema.cpython-310.pyc,, +sqlalchemy/__pycache__/types.cpython-310.pyc,, +sqlalchemy/connectors/__init__.py,sha256=PzXPqZqi3BzEnrs1eW0DcsR4lyknAzhhN9rWcQ97hb4,476 +sqlalchemy/connectors/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/connectors/__pycache__/aioodbc.cpython-310.pyc,, +sqlalchemy/connectors/__pycache__/asyncio.cpython-310.pyc,, +sqlalchemy/connectors/__pycache__/pyodbc.cpython-310.pyc,, +sqlalchemy/connectors/aioodbc.py,sha256=GSTiNMO9h0qjPxgqaxDwWZ8HvhWMFNVR6MJQnN1oc40,5288 +sqlalchemy/connectors/asyncio.py,sha256=K3U5857TD0GmJXIhxgRljKnJhJNzYFhQ8UjyFX0Yyoo,5878 +sqlalchemy/connectors/pyodbc.py,sha256=t7AjyxIOnaWg3CrlUEpBs4Y5l0HFdNt3P_cSSKhbi0Y,8501 +sqlalchemy/cyextension/__init__.py,sha256=GzhhN8cjMnDTE0qerlUlpbrNmFPHQWCZ4Gk74OAxl04,244 +sqlalchemy/cyextension/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/cyextension/collections.cpython-310-x86_64-linux-gnu.so,sha256=8od2cbgOsahxxeSGA0tRaCAFP2tRJAKA-XROqBE0Tug,1868216 +sqlalchemy/cyextension/collections.pyx,sha256=L7DZ3DGKpgw2MT2ZZRRxCnrcyE5pU1NAFowWgAzQPEc,12571 +sqlalchemy/cyextension/immutabledict.cpython-310-x86_64-linux-gnu.so,sha256=9ikr_c1y8B09IKjt2bzjfVLUH98rv1tjY2e8TsbClvk,587592 +sqlalchemy/cyextension/immutabledict.pxd,sha256=3x3-rXG5eRQ7bBnktZ-OJ9-6ft8zToPmTDOd92iXpB0,291 +sqlalchemy/cyextension/immutabledict.pyx,sha256=KfDTYbTfebstE8xuqAtuXsHNAK0_b5q_ymUiinUe_xs,3535 +sqlalchemy/cyextension/processors.cpython-310-x86_64-linux-gnu.so,sha256=gjZUjwboTD2ARYslv_kINc248FkgLDqhrilpGE9s3o0,431544 +sqlalchemy/cyextension/processors.pyx,sha256=R1rHsGLEaGeBq5VeCydjClzYlivERIJ9B-XLOJlf2MQ,1792 +sqlalchemy/cyextension/resultproxy.cpython-310-x86_64-linux-gnu.so,sha256=pgZceU3BnCgrTQM8KZVVI3HxAUQdeZSfyJjcyx7zZwM,510168 +sqlalchemy/cyextension/resultproxy.pyx,sha256=eWLdyBXiBy_CLQrF5ScfWJm7X0NeelscSXedtj1zv9Q,2725 +sqlalchemy/cyextension/util.cpython-310-x86_64-linux-gnu.so,sha256=Ytj9EHZjmPg5Zkw1ZXO5Q5kTvOgrXp-rGb8pFzweBsg,725904 +sqlalchemy/cyextension/util.pyx,sha256=B85orxa9LddLuQEaDoVSq1XmAXIbLKxrxpvuB8ogV_o,2530 +sqlalchemy/dialects/__init__.py,sha256=Kos9Gf5JZg1Vg6GWaCqEbD6e0r1jCwCmcnJIfcxDdcY,1770 +sqlalchemy/dialects/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/dialects/__pycache__/_typing.cpython-310.pyc,, +sqlalchemy/dialects/_typing.py,sha256=hyv0nKucX2gI8ispB1IsvaUgrEPn9zEcq9hS7kfstEw,888 +sqlalchemy/dialects/mssql/__init__.py,sha256=r5t8wFRNtBQoiUWh0WfIEWzXZW6f3D0uDt6NZTW_7Cc,1880 +sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/dialects/mssql/__pycache__/aioodbc.cpython-310.pyc,, +sqlalchemy/dialects/mssql/__pycache__/base.cpython-310.pyc,, +sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-310.pyc,, +sqlalchemy/dialects/mssql/__pycache__/json.cpython-310.pyc,, +sqlalchemy/dialects/mssql/__pycache__/provision.cpython-310.pyc,, +sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-310.pyc,, +sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-310.pyc,, +sqlalchemy/dialects/mssql/aioodbc.py,sha256=UQd9ecSMIML713TDnLAviuBVJle7P7i1FtqGZZePk2Y,2022 +sqlalchemy/dialects/mssql/base.py,sha256=87Z8bQbA8q5_T2Oyd4tUuttWNjYTQD9Dhq-eplDJcY4,132301 +sqlalchemy/dialects/mssql/information_schema.py,sha256=HswjDc6y0mPXCf_x6VyylHlBdBa4PSY6Evxmmlch700,8084 +sqlalchemy/dialects/mssql/json.py,sha256=evUACW2O62TAPq8B7QIPagz7jfc664ql9ms68JqiYzg,4816 +sqlalchemy/dialects/mssql/provision.py,sha256=RTVbgYLFAHzEnpVQDJroU8ji_10MqBTiZfyP9_-QNT4,5362 +sqlalchemy/dialects/mssql/pymssql.py,sha256=eZRLz7HGt3SdoZUjFBmA9BS43N7AhIASw7VPBPEJuG0,4038 +sqlalchemy/dialects/mssql/pyodbc.py,sha256=vwM-vBlmRwrqxOc73P0sFOrBSwn24wzc5IkEOpalbXQ,27056 +sqlalchemy/dialects/mysql/__init__.py,sha256=bxbi4hkysUK2OOVvr1F49akUj1cky27kKb07tgFzI9U,2153 +sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/asyncmy.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/base.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/dml.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/expression.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/json.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/provision.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/reserved_words.cpython-310.pyc,, +sqlalchemy/dialects/mysql/__pycache__/types.cpython-310.pyc,, +sqlalchemy/dialects/mysql/aiomysql.py,sha256=67JrSUD1BmN88k_ASk6GvrttZFQiFjDY0wBiwdllxMk,9964 +sqlalchemy/dialects/mysql/asyncmy.py,sha256=CGILIRKf_2Ut9Ng2yBlmdg62laL-ockEm6GMuN7xlKE,10033 +sqlalchemy/dialects/mysql/base.py,sha256=KA7tvRxKUw0KwHwMth2rz-NWV0xMkVbYvPoBM9wrAFw,120850 +sqlalchemy/dialects/mysql/cymysql.py,sha256=eXT1ry0w_qRxjiO24M980c-8PZ9qSsbhqBHntjEiKB0,2300 +sqlalchemy/dialects/mysql/dml.py,sha256=HXJMAvimJsqvhj3UZO4vW_6LkF5RqaKbHvklAjor7yU,7645 +sqlalchemy/dialects/mysql/enumerated.py,sha256=ipEPPQqoXfFwcywNdcLlZCEzHBtnitHRah1Gn6nItcg,8448 +sqlalchemy/dialects/mysql/expression.py,sha256=lsmQCHKwfPezUnt27d2kR6ohk4IRFCA64KBS16kx5dc,4097 +sqlalchemy/dialects/mysql/json.py,sha256=l6MEZ0qp8FgiRrIQvOMhyEJq0q6OqiEnvDTx5Cbt9uQ,2269 +sqlalchemy/dialects/mysql/mariadb.py,sha256=kTfBLioLKk4JFFst4TY_iWqPtnvvQXFHknLfm89H2N8,853 +sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=sRlzRFU5E1512aFyEhmmL1ffeU1u5meaLqtRYTcrH7Y,8568 +sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=qiQdfLPze3QHuASAZ9iqRzD0hDW8FbKoQnfAEQCF7tM,5675 +sqlalchemy/dialects/mysql/mysqldb.py,sha256=zPHKMQbHu8bFuD7YvOJewn2axQbfwA93NZoeX3bbbiQ,9502 +sqlalchemy/dialects/mysql/provision.py,sha256=4oGkClQ8jC3YLPF54sB4kCjFc8HRTwf5zl5zftAAXGo,3474 +sqlalchemy/dialects/mysql/pymysql.py,sha256=GUnSHd2M2uKjmN46Hheymtm26g7phEgwYOXrX0zLY8M,4083 +sqlalchemy/dialects/mysql/pyodbc.py,sha256=072crI4qVyPhajYvHnsfFeSrNjLFVPIjBQKo5uyz5yk,4297 +sqlalchemy/dialects/mysql/reflection.py,sha256=XXM8AGpaRTqDvuObg89Bzn_4h2ETG03viYBpWZJM3vc,22822 +sqlalchemy/dialects/mysql/reserved_words.py,sha256=ucKX2p2c3UnMq2ayZuOHuf73eXhu7SKsOsTlIN1Q83I,9258 +sqlalchemy/dialects/mysql/types.py,sha256=L5cTCsMT1pTedszNEM3jSxFNZEMcHQLprYCZ0vmfsnA,24343 +sqlalchemy/dialects/oracle/__init__.py,sha256=p4-2gw7TT0bX_MoJXTGD4i8WHctYsK9kCRbkpzykBrc,1493 +sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/dialects/oracle/__pycache__/base.cpython-310.pyc,, +sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-310.pyc,, +sqlalchemy/dialects/oracle/__pycache__/dictionary.cpython-310.pyc,, +sqlalchemy/dialects/oracle/__pycache__/oracledb.cpython-310.pyc,, +sqlalchemy/dialects/oracle/__pycache__/provision.cpython-310.pyc,, +sqlalchemy/dialects/oracle/__pycache__/types.cpython-310.pyc,, +sqlalchemy/dialects/oracle/base.py,sha256=-7b5iubFPxJyDRoLXlxj8rk8eBRN2_IdZlB2zzzrrbw,118246 +sqlalchemy/dialects/oracle/cx_oracle.py,sha256=t5yH4svVz7xoDSITF958blgZ01hbCUEWUKrAXwiCiAE,55566 +sqlalchemy/dialects/oracle/dictionary.py,sha256=7WMrbPkqo8ZdGjaEZyQr-5f2pajSOF1OTGb8P97z8-g,19519 +sqlalchemy/dialects/oracle/oracledb.py,sha256=UFcZwrrk0pWfAp_SKJZ1B5rIQHtNhOvuu73_JaSnTbI,9487 +sqlalchemy/dialects/oracle/provision.py,sha256=O9ZpF4OG6Cx4mMzLRfZwhs8dZjrJETWR402n9c7726A,8304 +sqlalchemy/dialects/oracle/types.py,sha256=QK3hJvWzKnnCe3oD3rItwEEIwcoBze8qGg7VFOvVlIk,8231 +sqlalchemy/dialects/postgresql/__init__.py,sha256=wwnNAq4wDQzrlPRzDNB06ayuq3L2HNO99nzeEvq-YcU,3892 +sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/_psycopg_common.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/array.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/base.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/json.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/named_types.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/operators.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pg_catalog.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/psycopg.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/types.cpython-310.pyc,, +sqlalchemy/dialects/postgresql/_psycopg_common.py,sha256=7TudtgsPiSB8O5kX8W8KxcNYR8t5h_UHb86b_ChL0P8,5696 +sqlalchemy/dialects/postgresql/array.py,sha256=bWcame7ntmI_Kx6gmBX0-chwADFdLHeCvaDQ4iX8id8,13734 +sqlalchemy/dialects/postgresql/asyncpg.py,sha256=e4Zv7hTb8OdwfvrM_B82GXgORBJeNQ2vChVfogvN7KI,40240 +sqlalchemy/dialects/postgresql/base.py,sha256=ed49Ode09deJF3jXr8V5sSHTwVoFVq0dlvNdP3mJaBQ,178989 +sqlalchemy/dialects/postgresql/dml.py,sha256=Pc69Le6qzmUHHb1FT5zeUSD31dWm6SBgdCAGW89cs3s,11212 +sqlalchemy/dialects/postgresql/ext.py,sha256=1bZ--iNh2O9ym7l2gXZX48yP3yMO4dqb9RpYro2Mj2Q,16262 +sqlalchemy/dialects/postgresql/hstore.py,sha256=otAx-RTDfpi_tcXkMuQV0JOIXtYgevgnsikLKKOkI6U,11541 +sqlalchemy/dialects/postgresql/json.py,sha256=9-ZvkLVMfQNSz0aHGml_B8sNg3gVq3-gxLnb5BO7Pog,11217 +sqlalchemy/dialects/postgresql/named_types.py,sha256=3IV1ufo7zJjKmX4VtGDEnoXE6xEqLJAtGG82IiqHXwY,17594 +sqlalchemy/dialects/postgresql/operators.py,sha256=NsAaWun_tL3d_be0fs9YL6T4LPKK6crnmFxxIJHgyeY,2808 +sqlalchemy/dialects/postgresql/pg8000.py,sha256=3yoekiWSF-xnaWMqG76XrYPMqerg-42TdmfsW_ivK9E,18640 +sqlalchemy/dialects/postgresql/pg_catalog.py,sha256=hY3NXEUHxTWD4umhd2aowNu3laC-61Q_qQ_pReyXTUM,9254 +sqlalchemy/dialects/postgresql/provision.py,sha256=yqyx-aDFO9l2YcL9f4T5HBP_Lnt5dHsMjpuXUG8mi7A,5762 +sqlalchemy/dialects/postgresql/psycopg.py,sha256=3_xRoZ71mgTR-tYKDb6N0ebbrRD3dFwVRI5-ZuFFOg8,23378 +sqlalchemy/dialects/postgresql/psycopg2.py,sha256=DbCB1-90O8YQDaIR67RtKOjzW3nvrdHoiwnVWYv_mX4,31605 +sqlalchemy/dialects/postgresql/psycopg2cffi.py,sha256=M7wAYSL6Pvt-4nbfacAHGyyw4XMKJ_bQZ1tc1pBtIdg,1756 +sqlalchemy/dialects/postgresql/ranges.py,sha256=6CgV7qkxEMJ9AQsiibo_XBLJYzGh-2ZxpG83sRaesVY,32949 +sqlalchemy/dialects/postgresql/types.py,sha256=Jfxqw9JaKNOq29JRWBublywgb3lLMyzx8YZI7CXpS2s,7300 +sqlalchemy/dialects/sqlite/__init__.py,sha256=lp9DIggNn349M-7IYhUA8et8--e8FRExWD2V_r1LJk4,1182 +sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-310.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/base.cpython-310.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-310.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/json.cpython-310.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-310.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-310.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-310.pyc,, +sqlalchemy/dialects/sqlite/aiosqlite.py,sha256=OMvxP2eWyqk5beF-sHhzxRmjzO4VCQp55q7NH2XPVTE,12305 +sqlalchemy/dialects/sqlite/base.py,sha256=lUtigjn7NdPBq831zQsLcBwdwRJqdgKM_tUaDrMElOE,96794 +sqlalchemy/dialects/sqlite/dml.py,sha256=9GE55WvwoktKy2fHeT-Wbc9xPHgsbh5oBfd_fckMH5Q,8443 +sqlalchemy/dialects/sqlite/json.py,sha256=Eoplbb_4dYlfrtmQaI8Xddd2suAIHA-IdbDQYM-LIhs,2777 +sqlalchemy/dialects/sqlite/provision.py,sha256=UCpmwxf4IWlrpb2eLHGbPTpCFVbdI_KAh2mKtjiLYao,5632 +sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=OL2S_05DK9kllZj6DOz7QtEl7jI7syxjW6woS725ii4,5356 +sqlalchemy/dialects/sqlite/pysqlite.py,sha256=USPhTjA19ks7VfzjlMXN_BkUpZiPH8J5ZyR4B9DBhVY,28045 +sqlalchemy/dialects/type_migration_guidelines.txt,sha256=-uHNdmYFGB7bzUNT6i8M5nb4j6j9YUKAtW4lcBZqsMg,8239 +sqlalchemy/engine/__init__.py,sha256=Stb2oV6l8w65JvqEo6J4qtKoApcmOpXy3AAxQud4C1o,2818 +sqlalchemy/engine/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/_py_processors.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/_py_row.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/_py_util.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/base.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/characteristics.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/create.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/cursor.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/default.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/events.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/interfaces.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/mock.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/processors.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/reflection.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/result.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/row.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/strategies.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/url.cpython-310.pyc,, +sqlalchemy/engine/__pycache__/util.cpython-310.pyc,, +sqlalchemy/engine/_py_processors.py,sha256=j9i_lcYYQOYJMcsDerPxI0sVFBIlX5sqoYMdMJlgWPI,3744 +sqlalchemy/engine/_py_row.py,sha256=wSqoUFzLOJ1f89kgDb6sJm9LUrF5LMFpXPcK1vUsKcs,3787 +sqlalchemy/engine/_py_util.py,sha256=f2DI3AN1kv6EplelowesCVpwS8hSXNufRkZoQmJtSH8,2484 +sqlalchemy/engine/base.py,sha256=0jDuJYb1tipD-N9chnMCvd25JySJQCm8CDCt_lobmv0,122898 +sqlalchemy/engine/characteristics.py,sha256=N3kbvw_ApMh86wb5yAGnxtPYD4YRhYMWion1H_aVZBI,4765 +sqlalchemy/engine/create.py,sha256=mYJtOG2ZKM8sgyfjpGpamW15RDU7JXi5s6iibbJHMIs,33206 +sqlalchemy/engine/cursor.py,sha256=cFq61yrw76k-QR_xNUBWuL-Zeyb14ltG-6jo2Q2iuuw,76392 +sqlalchemy/engine/default.py,sha256=B3i9Ce3Jk64CtNaS1gnes4uCMhiQn2cMwnZh5RxR-aE,84137 +sqlalchemy/engine/events.py,sha256=c0unNFFiHzTAvkUtXoJaxzMFMDwurBkHiiUhuN8qluc,37381 +sqlalchemy/engine/interfaces.py,sha256=n6G7VW5kaGyBlKdCkgaDs694tfzLPOxZeboDmVaJThg,112832 +sqlalchemy/engine/mock.py,sha256=yvpxgFmRw5G4QsHeF-ZwQGHKES-HqQOucTxFtN1uzdk,4179 +sqlalchemy/engine/processors.py,sha256=XyfINKbo-2fjN-mW55YybvFyQMOil50_kVqsunahkNs,2379 +sqlalchemy/engine/reflection.py,sha256=FlT5kPpKm7Lah50GNt5XcnlJWojTL3LD_x0SoCF9kfY,75127 +sqlalchemy/engine/result.py,sha256=j6BI4Wj2bziQNQG5OlG_Cm4KcNWY9AoYvTXVlJUU-D8,77603 +sqlalchemy/engine/row.py,sha256=9AAQo9zYDL88GcZ3bjcQTwMT-YIcuGTSMAyTfmBJ_yM,12032 +sqlalchemy/engine/strategies.py,sha256=DqFSWaXJPL-29Omot9O0aOcuGL8KmCGyOvnPGDkAJoE,442 +sqlalchemy/engine/url.py,sha256=8eWkUaIUyDExOcJ2D4xJXRcn4OY1GQJ3Q2duSX6UGAg,30784 +sqlalchemy/engine/util.py,sha256=bNirO8k1S8yOW61uNH-a9QrWtAJ9VGFgbiR0lk1lUQU,5682 +sqlalchemy/event/__init__.py,sha256=KBrp622xojnC3FFquxa2JsMamwAbfkvzfv6Op0NKiYc,997 +sqlalchemy/event/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/event/__pycache__/api.cpython-310.pyc,, +sqlalchemy/event/__pycache__/attr.cpython-310.pyc,, +sqlalchemy/event/__pycache__/base.cpython-310.pyc,, +sqlalchemy/event/__pycache__/legacy.cpython-310.pyc,, +sqlalchemy/event/__pycache__/registry.cpython-310.pyc,, +sqlalchemy/event/api.py,sha256=DtDVgjKSorOfp9MGJ7fgMWrj4seC_hkwF4D8CW1RFZU,8226 +sqlalchemy/event/attr.py,sha256=X8QeHGK4ioSYht1vkhc11f606_mq_t91jMNIT314ubs,20751 +sqlalchemy/event/base.py,sha256=3n9FmUkcXYHHyGzfpjKDsrIUVCNST_hq4zOtrNm0_a4,14954 +sqlalchemy/event/legacy.py,sha256=teMPs00fO-4g8a_z2omcVKkYce5wj_1uvJO2n2MIeuo,8227 +sqlalchemy/event/registry.py,sha256=nfTSSyhjZZXc5wseWB4sXn-YibSc0LKX8mg17XlWmAo,10835 +sqlalchemy/events.py,sha256=k-ZD38aSPD29LYhED7CBqttp5MDVVx_YSaWC2-cu9ec,525 +sqlalchemy/exc.py,sha256=M_8-O1hd8i6gbyx-TapV400p_Lxq2QqTGMXUAO-YgCc,23976 +sqlalchemy/ext/__init__.py,sha256=S1fGKAbycnQDV01gs-JWGaFQ9GCD4QHwKcU2wnugg_o,322 +sqlalchemy/ext/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/ext/__pycache__/associationproxy.cpython-310.pyc,, +sqlalchemy/ext/__pycache__/automap.cpython-310.pyc,, +sqlalchemy/ext/__pycache__/baked.cpython-310.pyc,, +sqlalchemy/ext/__pycache__/compiler.cpython-310.pyc,, +sqlalchemy/ext/__pycache__/horizontal_shard.cpython-310.pyc,, +sqlalchemy/ext/__pycache__/hybrid.cpython-310.pyc,, +sqlalchemy/ext/__pycache__/indexable.cpython-310.pyc,, +sqlalchemy/ext/__pycache__/instrumentation.cpython-310.pyc,, +sqlalchemy/ext/__pycache__/mutable.cpython-310.pyc,, +sqlalchemy/ext/__pycache__/orderinglist.cpython-310.pyc,, +sqlalchemy/ext/__pycache__/serializer.cpython-310.pyc,, +sqlalchemy/ext/associationproxy.py,sha256=5O5ANHARO8jytvqBQmOu-QjNVE4Hh3tfYquqKAj5ajs,65771 +sqlalchemy/ext/asyncio/__init__.py,sha256=1OqSxEyIUn7RWLGyO12F-jAUIvk1I6DXlVy80-Gvkds,1317 +sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/ext/asyncio/__pycache__/base.cpython-310.pyc,, +sqlalchemy/ext/asyncio/__pycache__/engine.cpython-310.pyc,, +sqlalchemy/ext/asyncio/__pycache__/exc.cpython-310.pyc,, +sqlalchemy/ext/asyncio/__pycache__/result.cpython-310.pyc,, +sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-310.pyc,, +sqlalchemy/ext/asyncio/__pycache__/session.cpython-310.pyc,, +sqlalchemy/ext/asyncio/base.py,sha256=fl7wxZD9KjgFiCtG3WXrYjHEvanamcsodCqq9pH9lOk,8905 +sqlalchemy/ext/asyncio/engine.py,sha256=S_IRWX4QAjj2veLSu4Y3gKBIXkKQt7_2StJAK2_KUDY,48190 +sqlalchemy/ext/asyncio/exc.py,sha256=8sII7VMXzs2TrhizhFQMzSfcroRtiesq8o3UwLfXSgQ,639 +sqlalchemy/ext/asyncio/result.py,sha256=ID2eh-NHW-lnNFTxbKhje8fr-tnsucUsiw_jcpGcSPc,30409 +sqlalchemy/ext/asyncio/scoping.py,sha256=BmE1UbFV_C4BXB4WngJc523DeMH-nTchNb8ORiSPYfE,52597 +sqlalchemy/ext/asyncio/session.py,sha256=ZZklu124Rm2p9B5pbcDbR0zVyEuq-rn69_ltByuKNXo,63092 +sqlalchemy/ext/automap.py,sha256=cMs3mMXZBbfQXahxHbT3-6h5uMM--GZIcdtwhRrZoio,61589 +sqlalchemy/ext/baked.py,sha256=H6T1il7GY84BhzPFj49UECSpZh_eBuiHomA-QIsYOYQ,17807 +sqlalchemy/ext/compiler.py,sha256=ONPoxoKD2yUS9R2-oOhmPsA7efm-Bs0BXo7HE1dGlsU,20391 +sqlalchemy/ext/declarative/__init__.py,sha256=20psLdFQbbOWfpdXHZ0CTY6I1k4UqXvKemNVu1LvPOI,1818 +sqlalchemy/ext/declarative/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/ext/declarative/__pycache__/extensions.cpython-310.pyc,, +sqlalchemy/ext/declarative/extensions.py,sha256=uCjN1GisQt54AjqYnKYzJdUjnGd2pZBW47WWdPlS7FE,19547 +sqlalchemy/ext/horizontal_shard.py,sha256=wuwAPnHymln0unSBnyx-cpX0AfESKSsypaSQTYCvzDk,16750 +sqlalchemy/ext/hybrid.py,sha256=IYkCaPZ29gm2cPKPg0cWMkLCEqMykD8-JJTvgacGbmc,52458 +sqlalchemy/ext/indexable.py,sha256=UkTelbydKCdKelzbv3HWFFavoET9WocKaGRPGEOVfN8,11032 +sqlalchemy/ext/instrumentation.py,sha256=sg8ghDjdHSODFXh_jAmpgemnNX1rxCeeXEG3-PMdrNk,15707 +sqlalchemy/ext/mutable.py,sha256=L5ZkHBGYhMaqO75Xtyrk2DBR44RDk0g6Rz2HzHH0F8Q,37355 +sqlalchemy/ext/mypy/__init__.py,sha256=0WebDIZmqBD0OTq5JLtd_PmfF9JGxe4d4Qv3Ml3PKUg,241 +sqlalchemy/ext/mypy/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/ext/mypy/__pycache__/apply.cpython-310.pyc,, +sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-310.pyc,, +sqlalchemy/ext/mypy/__pycache__/infer.cpython-310.pyc,, +sqlalchemy/ext/mypy/__pycache__/names.cpython-310.pyc,, +sqlalchemy/ext/mypy/__pycache__/plugin.cpython-310.pyc,, +sqlalchemy/ext/mypy/__pycache__/util.cpython-310.pyc,, +sqlalchemy/ext/mypy/apply.py,sha256=Aek_-XA1eXihT4attxhfE43yBKtCgsxBSb--qgZKUqc,10550 +sqlalchemy/ext/mypy/decl_class.py,sha256=1vVJRII2apnLTUbc5HkJS6Z2GueaUv_eKvhbqh7Wik4,17384 +sqlalchemy/ext/mypy/infer.py,sha256=KVnmLFEVS33Al8pUKI7MJbJQu3KeveBUMl78EluBORw,19369 +sqlalchemy/ext/mypy/names.py,sha256=Q3ef8XQBgVm9WUwlItqlYCXDNi_kbV5DdLEgbtEMEI8,10479 +sqlalchemy/ext/mypy/plugin.py,sha256=74ML8LI9xar0V86oCxnPFv5FQGEEfUzK64vOay4BKFs,9750 +sqlalchemy/ext/mypy/util.py,sha256=1zuDQG8ezmF-XhJmAQU_lcBHiD--sL-lq20clg8t4lE,9448 +sqlalchemy/ext/orderinglist.py,sha256=TGYbsGH72wEZcFNQDYDsZg9OSPuzf__P8YX8_2HtYUo,14384 +sqlalchemy/ext/serializer.py,sha256=YemanWdeMVUDweHCnQc-iMO6mVVXNo2qQ5NK0Eb2_Es,6178 +sqlalchemy/future/__init__.py,sha256=q2mw-gxk_xoxJLEvRoyMha3vO1xSRHrslcExOHZwmPA,512 +sqlalchemy/future/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/future/__pycache__/engine.cpython-310.pyc,, +sqlalchemy/future/engine.py,sha256=AgIw6vMsef8W6tynOTkxsjd6o_OQDwGjLdbpoMD8ue8,495 +sqlalchemy/inspection.py,sha256=MF-LE358wZDUEl1IH8-Uwt2HI65EsQpQW5o5udHkZwA,5063 +sqlalchemy/log.py,sha256=8x9UR3nj0uFm6or6bQF-JWb4fYv2zOeQjG_w-0wOJFA,8607 +sqlalchemy/orm/__init__.py,sha256=ZYys5nL3RFUDCMOLFDBrRI52F6er3S1U1OY9TeORuKs,8463 +sqlalchemy/orm/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/_orm_constructors.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/_typing.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/attributes.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/base.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/bulk_persistence.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/clsregistry.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/collections.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/context.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/decl_api.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/decl_base.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/dependency.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/descriptor_props.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/dynamic.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/evaluator.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/events.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/exc.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/identity.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/instrumentation.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/interfaces.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/loading.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/mapped_collection.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/mapper.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/path_registry.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/persistence.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/properties.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/query.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/relationships.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/scoping.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/session.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/state.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/state_changes.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/strategies.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/strategy_options.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/sync.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/unitofwork.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/util.cpython-310.pyc,, +sqlalchemy/orm/__pycache__/writeonly.cpython-310.pyc,, +sqlalchemy/orm/_orm_constructors.py,sha256=tlTZoY87CFSBpnw7iIYKTCeofjLg-r2ibfCfMfbyEMU,99461 +sqlalchemy/orm/_typing.py,sha256=DVBfpHmDVK4x1zxaGJPY2GoTrAsyR6uexv20Lzf1afc,4973 +sqlalchemy/orm/attributes.py,sha256=wGpyY9aIxWRkL3y6qlbUgIW78rGA4X38ZhOaWI9S818,92535 +sqlalchemy/orm/base.py,sha256=re6A8ooMxLyfCAeQfhBwwxeJQkiH7EtzzOZIVIiTPlw,27466 +sqlalchemy/orm/bulk_persistence.py,sha256=wU204OQK-V6rJfpc3HPeMAA1X9vZg9nSNpI1zKuZtcY,70086 +sqlalchemy/orm/clsregistry.py,sha256=29LyYiuj0qbebOpgW6DbBPNB2ikTweFQar1byCst7I0,17958 +sqlalchemy/orm/collections.py,sha256=jpMsJGVixmrW9kfT8wevm9kpatKRqyDLcqWd7CjKPxE,52179 +sqlalchemy/orm/context.py,sha256=pFFc-1ZjaV3aQMAO27UIh0W6NyLc30n2IbuWNprMtS8,112445 +sqlalchemy/orm/decl_api.py,sha256=_WPKQ_vSE5k2TLtNmkaxxYmvbhZvkRMrrvCeDxdqDQE,63998 +sqlalchemy/orm/decl_base.py,sha256=Tq6I3Jm3bkM01mmoiHfdFXLE94YDk1ik2u2dXL1RxLc,81601 +sqlalchemy/orm/dependency.py,sha256=hgjksUWhgbmgHK5GdJdiDCBgDAIGQXIrY-Tj79tbL2k,47631 +sqlalchemy/orm/descriptor_props.py,sha256=pKtpP7H1LB_YuHRVrEYpfFZybEnUUdPwQXxduYFe2hA,37180 +sqlalchemy/orm/dynamic.py,sha256=jksBDCOsm6EBMVParcNGuMeaAv12hX4IzouKspC-HPA,9786 +sqlalchemy/orm/evaluator.py,sha256=q292K5vdpP69G7Z9y1RqI5GFAk2diUPwnsXE8De_Wgw,11925 +sqlalchemy/orm/events.py,sha256=0lxP-EluVWSUr07ny2nsuQ8QhrDu9Qc4ON6QRUhuJnA,127703 +sqlalchemy/orm/exc.py,sha256=IP40P-wOeXhkYk0YizuTC3wqm6W9cPTaQU08f5MMaQ0,7413 +sqlalchemy/orm/identity.py,sha256=jHdCxCpCyda_8mFOfGmN_Pr0XZdKiU-2hFZshlNxbHs,9249 +sqlalchemy/orm/instrumentation.py,sha256=M-kZmkUvHUxtf-0mCA8RIM5QmMH1hWlYR_pKMwaidjA,24321 +sqlalchemy/orm/interfaces.py,sha256=Hmf1BjbfOarZRgMlruqghR7cgH2xyugA9v5t0x-a-wU,48502 +sqlalchemy/orm/loading.py,sha256=9RacpzFOWbuKgPRWHFmyIvD4fYCLAnkpwBFASyQ2CoI,58277 +sqlalchemy/orm/mapped_collection.py,sha256=3cneB1dfPTLrsTvKoo9_oCY2xtq4UAHfe5WSXPyqIS4,19690 +sqlalchemy/orm/mapper.py,sha256=bfoRzNKKnjF-CDvr2Df7HZC9TepvtuQ49LRz_fW7DGQ,171088 +sqlalchemy/orm/path_registry.py,sha256=sJZMv_WPqUpHfQtKWaX3WYFeKBcNJ8C3wOM2mkBGkTE,25920 +sqlalchemy/orm/persistence.py,sha256=dzyB2JOXNwQgaCbN8kh0sEz00WFePr48qf8NWVCUZH8,61701 +sqlalchemy/orm/properties.py,sha256=eDPFzxYUgdM3uWjHywnb1XW-i0tVKKyx7A2MCD31GQU,29306 +sqlalchemy/orm/query.py,sha256=qKuFTUlbGVjs84AQ7APBY0PJhrzc2JJ1upeI658MV_o,117596 +sqlalchemy/orm/relationships.py,sha256=dS5SY0v1MiD7iCNnAQlHaI6prUQhL5EkXT7ijc8FR8E,128644 +sqlalchemy/orm/scoping.py,sha256=gFYywLeMmd5qjFdVPzeuCX727mTaChrCv8aqn4wPke0,78677 +sqlalchemy/orm/session.py,sha256=Da-wd01Rw5fCqRr05RBY3dath-ssdloB4BvVrTVy8LA,195250 +sqlalchemy/orm/state.py,sha256=mW2f1hMSNeTJ89foutOE1EqLLD6DZkrSeO-pgagZweg,37520 +sqlalchemy/orm/state_changes.py,sha256=qKYg7NxwrDkuUY3EPygAztym6oAVUFcP2wXn7QD3Mz4,6815 +sqlalchemy/orm/strategies.py,sha256=OtmMtWpCDk4ZiaM_ipzGn80sPOi6Opwj3Co4lUHpd_w,114206 +sqlalchemy/orm/strategy_options.py,sha256=tFDJllL8Evxo2UjpyMkrxxRdphaclM-FBUd9O8WArdY,85366 +sqlalchemy/orm/sync.py,sha256=g7iZfSge1HgxMk9SKRgUgtHEbpbZ1kP_CBqOIdTOXqc,5779 +sqlalchemy/orm/unitofwork.py,sha256=fiVaqcymbDDHRa1NjS90N9Z466nd5pkJOEi1dHO6QLY,27033 +sqlalchemy/orm/util.py,sha256=PvU_J4PSNcaOoNnsLY9qZ0lcElG71ykvdZSDZ76WSwg,80660 +sqlalchemy/orm/writeonly.py,sha256=SYu2sAaHZONk2pW4PmtE871LG-O0P_bjidvKzY1H_zI,22305 +sqlalchemy/pool/__init__.py,sha256=qiDdq4r4FFAoDrK6ncugF_i6usi_X1LeJt-CuBHey0s,1804 +sqlalchemy/pool/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/pool/__pycache__/base.cpython-310.pyc,, +sqlalchemy/pool/__pycache__/events.cpython-310.pyc,, +sqlalchemy/pool/__pycache__/impl.cpython-310.pyc,, +sqlalchemy/pool/base.py,sha256=WF4az4ZKuzQGuKeSJeyexaYjmWZUvYdC6KIi8zTGodw,52236 +sqlalchemy/pool/events.py,sha256=xGjkIUZl490ZDtCHqnQF9ZCwe2Jv93eGXmnQxftB11E,13147 +sqlalchemy/pool/impl.py,sha256=JwpALSkH-pCoO_6oENbkHYY00Jx9nlttyoI61LivRNc,18944 +sqlalchemy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sqlalchemy/schema.py,sha256=dKiWmgHYjcKQ4TiiD6vD0UMmIsD8u0Fsor1M9AAeGUs,3194 +sqlalchemy/sql/__init__.py,sha256=UNa9EUiYWoPayf-FzNcwVgQvpsBdInPZfpJesAStN9o,5820 +sqlalchemy/sql/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/_dml_constructors.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/_elements_constructors.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/_orm_types.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/_py_util.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/_selectable_constructors.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/_typing.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/annotation.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/base.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/cache_key.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/coercions.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/compiler.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/crud.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/ddl.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/default_comparator.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/dml.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/elements.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/events.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/expression.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/functions.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/lambdas.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/naming.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/operators.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/roles.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/schema.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/selectable.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/sqltypes.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/traversals.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/type_api.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/util.cpython-310.pyc,, +sqlalchemy/sql/__pycache__/visitors.cpython-310.pyc,, +sqlalchemy/sql/_dml_constructors.py,sha256=YdBJex0MCVACv4q2nl_ii3uhxzwU6aDB8zAsratX5UQ,3867 +sqlalchemy/sql/_elements_constructors.py,sha256=1SX6o1ezeB8C9DAa2m0WxmfhM3ji3FeCprXFQkNerNY,63048 +sqlalchemy/sql/_orm_types.py,sha256=T-vjcry4C1y0GToFKVxQCnmly_-Zsq4IO4SHN6bvUF4,625 +sqlalchemy/sql/_py_util.py,sha256=hiM9ePbRSGs60bAMxPFuJCIC_p9SQ1VzqXGiPchiYwE,2173 +sqlalchemy/sql/_selectable_constructors.py,sha256=wjE6HrLm9cR7bxvZXT8sFLUqT6t_J9G1XyQCnYmBDl0,18780 +sqlalchemy/sql/_typing.py,sha256=oqwrYHVMtK-AuKGH9c4SgfiOEJUt5vjkzSEzzscMHkM,12771 +sqlalchemy/sql/annotation.py,sha256=aqbbVz9kfbCT3_66CZ9GEirVN197Cukoqt8rq48FgkQ,18245 +sqlalchemy/sql/base.py,sha256=Lg7iHywXmB7XuRMar45XSM4KayChGhfj1c8E7nWUJdo,73899 +sqlalchemy/sql/cache_key.py,sha256=0Db8mR8IrpBgdzXs4TGTt98LOpL3c7KABd72MAPKUQQ,33668 +sqlalchemy/sql/coercions.py,sha256=1xzN_9U5BCJGgokdc5iYj5o2cMAfEEZkr1Oa9Q-JYj8,40493 +sqlalchemy/sql/compiler.py,sha256=e5XWUmeZnE3O8r7fndfmXjAze3qlFZY223BHqrso0AY,274647 +sqlalchemy/sql/crud.py,sha256=g9xcol2KRGjZi1qsb2-bVz8zgVy_53gfMtJcnNO2vyQ,56521 +sqlalchemy/sql/ddl.py,sha256=CIqMilCKfuQnF0lrZsQdTxgrbXqcTauKr0Ojzj77PFQ,45602 +sqlalchemy/sql/default_comparator.py,sha256=utXWsZVGEjflhFfCT4ywa6RnhORc1Rryo87Hga71Rps,16707 +sqlalchemy/sql/dml.py,sha256=pn0Lm1ofC5qVZzwGWFW73lPCiNba8OsTeemurJgwRyg,65614 +sqlalchemy/sql/elements.py,sha256=coAHu2qUNrEKOv0ZvnDsc3G8_6o9lciylZn-K6fJDXU,174621 +sqlalchemy/sql/events.py,sha256=iC_Q1Htm1Aobt5tOYxWfHHqNpoytrULORmUKcusH_-E,18290 +sqlalchemy/sql/expression.py,sha256=VMX-dLpsZYnVRJpYNDozDUgaj7iQ0HuewUKVefD57PE,7586 +sqlalchemy/sql/functions.py,sha256=kMMYplvuIHFAPwxBI03SizwaLcYEHzysecWk-R1V-JM,63762 +sqlalchemy/sql/lambdas.py,sha256=DP0Qz7Ypo8QhzMwygGHYgRhwJMx-rNezO1euouH3iYU,49292 +sqlalchemy/sql/naming.py,sha256=ZHs1qSV3ou8TYmZ92uvU3sfdklUQlIz4uhe330n05SU,6858 +sqlalchemy/sql/operators.py,sha256=r4oQp4h5zTMFFOpiFNV56joIK-QIjJCobatsmaZ-724,75935 +sqlalchemy/sql/roles.py,sha256=pOsVn_OZD7mF2gJByHf24Rjopt0_Hu3dUCEOK5t4KS8,7662 +sqlalchemy/sql/schema.py,sha256=GctEXTYd5R9nqS07xgf5uFC-_3UIZJ7q3RaetzSs0Pk,229225 +sqlalchemy/sql/selectable.py,sha256=XHL0xJeLWnQeGG_dK7zQ8lNsCGdP8DeJQuCF4bCW-bo,233505 +sqlalchemy/sql/sqltypes.py,sha256=AAXED0rebCNmrY8K8kL6zox8_GD0L_p0lRzwM3SxCGk,126076 +sqlalchemy/sql/traversals.py,sha256=NFgJrVJzInO3HrnG90CklxrDXhFydZohPs2vRJkh3Bo,33589 +sqlalchemy/sql/type_api.py,sha256=CJAvRix_rRoOsE77sH4BqeGX0lIdtk8dX-StjGx3mUo,83208 +sqlalchemy/sql/util.py,sha256=qGHQF-tPCj-m1FBerzT7weCanGcXU7dK5m-W7NHio-4,48077 +sqlalchemy/sql/visitors.py,sha256=71wdVvhhZL4nJvVwFAs6ssaW-qZgNRSmKjpAcOzF_TA,36317 +sqlalchemy/testing/__init__.py,sha256=VsrEHrORpAF5n7Vfl43YQgABh6EP1xBx_gHxs7pSXeE,3126 +sqlalchemy/testing/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/assertions.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/assertsql.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/asyncio.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/config.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/engines.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/entities.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/exclusions.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/pickleable.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/profiling.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/provision.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/requirements.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/schema.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/util.cpython-310.pyc,, +sqlalchemy/testing/__pycache__/warnings.cpython-310.pyc,, +sqlalchemy/testing/assertions.py,sha256=gL0rA7CCZJbcVgvWOPV91tTZTRwQc1_Ta0-ykBn83Ew,31439 +sqlalchemy/testing/assertsql.py,sha256=IgQG7l94WaiRP8nTbilJh1ZHZl125g7GPq-S5kmQZN0,16817 +sqlalchemy/testing/asyncio.py,sha256=kM8uuOqDBagZF0r9xvGmsiirUVLUQ_KBzjUFU67W-b8,3830 +sqlalchemy/testing/config.py,sha256=AqyH1qub_gDqX0BvlL-JBQe7N-t2wo8655FtwblUNOY,12090 +sqlalchemy/testing/engines.py,sha256=HFJceEBD3Q_TTFQMTtIV5wGWO_a7oUgoKtUF_z636SM,13481 +sqlalchemy/testing/entities.py,sha256=IphFegPKbff3Un47jY6bi7_MQXy6qkx_50jX2tHZJR4,3354 +sqlalchemy/testing/exclusions.py,sha256=T8B01hmm8WVs-EKcUOQRzabahPqblWJfOidi6bHJ6GA,12460 +sqlalchemy/testing/fixtures/__init__.py,sha256=dMClrIoxqlYIFpk2ia4RZpkbfxsS_3EBigr9QsPJ66g,1198 +sqlalchemy/testing/fixtures/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/testing/fixtures/__pycache__/base.cpython-310.pyc,, +sqlalchemy/testing/fixtures/__pycache__/mypy.cpython-310.pyc,, +sqlalchemy/testing/fixtures/__pycache__/orm.cpython-310.pyc,, +sqlalchemy/testing/fixtures/__pycache__/sql.cpython-310.pyc,, +sqlalchemy/testing/fixtures/base.py,sha256=9r_J2ksiTzClpUxW0TczICHrWR7Ny8PV8IsBz6TsGFI,12256 +sqlalchemy/testing/fixtures/mypy.py,sha256=gdxiwNFIzDlNGSOdvM3gbwDceVCC9t8oM5kKbwyhGBk,11973 +sqlalchemy/testing/fixtures/orm.py,sha256=8EFbnaBbXX_Bf4FcCzBUaAHgyVpsLGBHX16SGLqE3Fg,6095 +sqlalchemy/testing/fixtures/sql.py,sha256=TE5q2BSOc_Vq1TPaLnj1F9ZA_YiQHlqyvUtGId9bGr0,15774 +sqlalchemy/testing/pickleable.py,sha256=U9mIqk-zaxq9Xfy7HErP7UrKgTov-A3QFnhZh-NiOjI,2833 +sqlalchemy/testing/plugin/__init__.py,sha256=79F--BIY_NTBzVRIlJGgAY5LNJJ3cD19XvrAo4X0W9A,247 +sqlalchemy/testing/plugin/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-310.pyc,, +sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-310.pyc,, +sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-310.pyc,, +sqlalchemy/testing/plugin/bootstrap.py,sha256=oYScMbEW4pCnWlPEAq1insFruCXFQeEVBwo__i4McpU,1685 +sqlalchemy/testing/plugin/plugin_base.py,sha256=BgNzWNEmgpK4CwhyblQQKnH-7FDKVi_Uul5vw8fFjBU,21578 +sqlalchemy/testing/plugin/pytestplugin.py,sha256=6jkQHH2VQMD75k2As9CuWXmEy9jrscoFRhCNg6-PaTw,27656 +sqlalchemy/testing/profiling.py,sha256=PbuPhRFbauFilUONeY3tV_Y_5lBkD7iCa8VVyH2Sk9Y,10148 +sqlalchemy/testing/provision.py,sha256=zXsw2D2Xpmw_chmYLsE1GXQqKQ-so3V8xU_joTcKan0,14619 +sqlalchemy/testing/requirements.py,sha256=N9pSj7z2wVMkBif-DQfPVa_cl9k6p9g_J5FY1OsWtrY,51817 +sqlalchemy/testing/schema.py,sha256=lr4GkGrGwagaHMuSGzWdzkMaj3HnS7dgfLLWfxt__-U,6513 +sqlalchemy/testing/suite/__init__.py,sha256=Y5DRNG0Yl1u3ypt9zVF0Z9suPZeuO_UQGLl-wRgvTjU,722 +sqlalchemy/testing/suite/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_cte.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_insert.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_results.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_select.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_types.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-310.pyc,, +sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-310.pyc,, +sqlalchemy/testing/suite/test_cte.py,sha256=6zBC3W2OwX1Xs-HedzchcKN2S7EaLNkgkvV_JSZ_Pq0,6451 +sqlalchemy/testing/suite/test_ddl.py,sha256=1Npkf0C_4UNxphthAGjG078n0vPEgnSIHpDu5MfokxQ,12031 +sqlalchemy/testing/suite/test_deprecations.py,sha256=BcJxZTcjYqeOAENVElCg3hVvU6fkGEW3KGBMfnW8bng,5337 +sqlalchemy/testing/suite/test_dialect.py,sha256=EH4ZQWbnGdtjmx5amZtTyhYmrkXJCvW1SQoLahoE7uk,22923 +sqlalchemy/testing/suite/test_insert.py,sha256=9azifj6-OCD7s8h_tAO1uPw100ibQv8YoKc_VA3hn3c,18824 +sqlalchemy/testing/suite/test_reflection.py,sha256=tJSbJFg5fw0sSUv3I_FPmhN7rWWeJtq3YyxmylWJUlM,106466 +sqlalchemy/testing/suite/test_results.py,sha256=NQ23m8FDVd0ub751jN4PswGoAhk5nrqvjHvpYULZXnc,15937 +sqlalchemy/testing/suite/test_rowcount.py,sha256=3KDTlRgjpQ1OVfp__1cv8Hvq4CsDKzmrhJQ_WIJWoJg,7900 +sqlalchemy/testing/suite/test_select.py,sha256=FvMFYQW9IJpDWGYZiJk46is6YrtmdSghBdTjZCG8T0Y,58574 +sqlalchemy/testing/suite/test_sequence.py,sha256=66bCoy4xo99GBSaX6Hxb88foANAykLGRz1YEKbvpfuA,9923 +sqlalchemy/testing/suite/test_types.py,sha256=rFmTOg6XuMch9L2-XthfLJRCTTwpZbMfrNss2g09gmc,65677 +sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=c3_eIxLyORuSOhNDP0jWKxPyUf3SwMFpdalxtquwqlM,6141 +sqlalchemy/testing/suite/test_update_delete.py,sha256=yTiM2unnfOK9rK8ZkqeTTU_MkT-RsKFLmdYliniZfAY,3994 +sqlalchemy/testing/util.py,sha256=BFiSp3CEX95Dr-vv4l_7ZRu5vjZi9hjjnp-JKNfuS5E,14080 +sqlalchemy/testing/warnings.py,sha256=fJ-QJUY2zY2PPxZJKv9medW-BKKbCNbA4Ns_V3YwFXM,1546 +sqlalchemy/types.py,sha256=cQFM-hFRmaf1GErun1qqgEs6QxufvzMuwKqj9tuMPpE,3168 +sqlalchemy/util/__init__.py,sha256=B3bedg-LSQEscwqgmYYU-VENUX8_zAE3q9vb7tkfJNY,8277 +sqlalchemy/util/__pycache__/__init__.cpython-310.pyc,, +sqlalchemy/util/__pycache__/_collections.cpython-310.pyc,, +sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-310.pyc,, +sqlalchemy/util/__pycache__/_has_cy.cpython-310.pyc,, +sqlalchemy/util/__pycache__/_py_collections.cpython-310.pyc,, +sqlalchemy/util/__pycache__/compat.cpython-310.pyc,, +sqlalchemy/util/__pycache__/concurrency.cpython-310.pyc,, +sqlalchemy/util/__pycache__/deprecations.cpython-310.pyc,, +sqlalchemy/util/__pycache__/langhelpers.cpython-310.pyc,, +sqlalchemy/util/__pycache__/preloaded.cpython-310.pyc,, +sqlalchemy/util/__pycache__/queue.cpython-310.pyc,, +sqlalchemy/util/__pycache__/tool_support.cpython-310.pyc,, +sqlalchemy/util/__pycache__/topological.cpython-310.pyc,, +sqlalchemy/util/__pycache__/typing.cpython-310.pyc,, +sqlalchemy/util/_collections.py,sha256=NE9dGJo8UNXIMbY3l3k8AO9BdPW04DlKTYraKCinchI,20063 +sqlalchemy/util/_concurrency_py3k.py,sha256=IBxZDTSyLbEx9I9ViQVXYP1twxM-pTa-3x_-U8snmLU,9191 +sqlalchemy/util/_has_cy.py,sha256=wCQmeSjT3jaH_oxfCEtGk-1g0gbSpt5MCK5UcWdMWqk,1247 +sqlalchemy/util/_py_collections.py,sha256=U6L5AoyLdgSv7cdqB4xxQbw1rpeJjyOZVXffgxgga8I,16714 +sqlalchemy/util/compat.py,sha256=R6bpBydldtbr6h7oJePihQxFb7jKiI-YDsK465MSOzk,8714 +sqlalchemy/util/concurrency.py,sha256=9lT_cMoO1fZNdY8QTUZ22oeSf-L5I-79Ke7chcBNPA0,3304 +sqlalchemy/util/deprecations.py,sha256=YBwvvYhSB8LhasIZRKvg_-WNoVhPUcaYI1ZrnjDn868,11971 +sqlalchemy/util/langhelpers.py,sha256=kT7kwtTOPzHUFs6PhM1IEoW5ioeuFUFg2pjHSbH4d94,64891 +sqlalchemy/util/preloaded.py,sha256=az7NmLJLsqs0mtM9uBkIu10-841RYDq8wOyqJ7xXvqE,5904 +sqlalchemy/util/queue.py,sha256=CaeSEaYZ57YwtmLdNdOIjT5PK_LCuwMFiO0mpp39ybM,10185 +sqlalchemy/util/tool_support.py,sha256=9braZyidaiNrZVsWtGmkSmus50-byhuYrlAqvhjcmnA,6135 +sqlalchemy/util/topological.py,sha256=N3M3Le7KzGHCmqPGg0ZBqixTDGwmFLhOZvBtc4rHL_g,3458 +sqlalchemy/util/typing.py,sha256=UctfgugSTnFJgZraikXJbB-5RoV7oyccZmbf2NeRfoA,16671 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/WHEEL b/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/WHEEL new file mode 100644 index 0000000..9bb86cf --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.43.0) +Root-Is-Purelib: false +Tag: cp310-cp310-manylinux_2_17_x86_64 +Tag: cp310-cp310-manylinux2014_x86_64 + diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/top_level.txt b/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/top_level.txt new file mode 100644 index 0000000..39fb2be --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/SQLAlchemy-2.0.30.dist-info/top_level.txt @@ -0,0 +1 @@ +sqlalchemy diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/_distutils_hack/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/_distutils_hack/__init__.py new file mode 100644 index 0000000..f707416 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/_distutils_hack/__init__.py @@ -0,0 +1,132 @@ +import sys +import os +import re +import importlib +import warnings + + +is_pypy = '__pypy__' in sys.builtin_module_names + + +warnings.filterwarnings('ignore', + r'.+ distutils\b.+ deprecated', + DeprecationWarning) + + +def warn_distutils_present(): + if 'distutils' not in sys.modules: + return + if is_pypy and sys.version_info < (3, 7): + # PyPy for 3.6 unconditionally imports distutils, so bypass the warning + # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 + return + warnings.warn( + "Distutils was imported before Setuptools, but importing Setuptools " + "also replaces the `distutils` module in `sys.modules`. This may lead " + "to undesirable behaviors or errors. To avoid these issues, avoid " + "using distutils directly, ensure that setuptools is installed in the " + "traditional way (e.g. not an editable install), and/or make sure " + "that setuptools is always imported before distutils.") + + +def clear_distutils(): + if 'distutils' not in sys.modules: + return + warnings.warn("Setuptools is replacing distutils.") + mods = [name for name in sys.modules if re.match(r'distutils\b', name)] + for name in mods: + del sys.modules[name] + + +def enabled(): + """ + Allow selection of distutils by environment variable. + """ + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') + return which == 'local' + + +def ensure_local_distutils(): + clear_distutils() + + # With the DistutilsMetaFinder in place, + # perform an import to cause distutils to be + # loaded from setuptools._distutils. Ref #2906. + add_shim() + importlib.import_module('distutils') + remove_shim() + + # check that submodules load as expected + core = importlib.import_module('distutils.core') + assert '_distutils' in core.__file__, core.__file__ + + +def do_override(): + """ + Ensure that the local copy of distutils is preferred over stdlib. + + See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 + for more motivation. + """ + if enabled(): + warn_distutils_present() + ensure_local_distutils() + + +class DistutilsMetaFinder: + def find_spec(self, fullname, path, target=None): + if path is not None: + return + + method_name = 'spec_for_{fullname}'.format(**locals()) + method = getattr(self, method_name, lambda: None) + return method() + + def spec_for_distutils(self): + import importlib.abc + import importlib.util + + class DistutilsLoader(importlib.abc.Loader): + + def create_module(self, spec): + return importlib.import_module('setuptools._distutils') + + def exec_module(self, module): + pass + + return importlib.util.spec_from_loader('distutils', DistutilsLoader()) + + def spec_for_pip(self): + """ + Ensure stdlib distutils when running under pip. + See pypa/pip#8761 for rationale. + """ + if self.pip_imported_during_build(): + return + clear_distutils() + self.spec_for_distutils = lambda: None + + @staticmethod + def pip_imported_during_build(): + """ + Detect if pip is being imported in a build script. Ref #2355. + """ + import traceback + return any( + frame.f_globals['__file__'].endswith('setup.py') + for frame, line in traceback.walk_stack(None) + ) + + +DISTUTILS_FINDER = DistutilsMetaFinder() + + +def add_shim(): + sys.meta_path.insert(0, DISTUTILS_FINDER) + + +def remove_shim(): + try: + sys.meta_path.remove(DISTUTILS_FINDER) + except ValueError: + pass diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/_distutils_hack/override.py b/flask_auth_app/authenv/lib/python3.10/site-packages/_distutils_hack/override.py new file mode 100644 index 0000000..2cc433a --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/_distutils_hack/override.py @@ -0,0 +1 @@ +__import__('_distutils_hack').do_override() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/INSTALLER b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/LICENSE.txt b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/LICENSE.txt new file mode 100644 index 0000000..79c9825 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.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/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/METADATA b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/METADATA new file mode 100644 index 0000000..efa45f5 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/METADATA @@ -0,0 +1,60 @@ +Metadata-Version: 2.1 +Name: blinker +Version: 1.8.2 +Summary: Fast, simple object-to-object and broadcast signaling +Author: Jason Kirtland +Maintainer-email: Pallets Ecosystem +Requires-Python: >=3.8 +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/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/RECORD b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/RECORD new file mode 100644 index 0000000..e6c304b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/RECORD @@ -0,0 +1,12 @@ +blinker-1.8.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +blinker-1.8.2.dist-info/LICENSE.txt,sha256=nrc6HzhZekqhcCXSrhvjg5Ykx5XphdTw6Xac4p-spGc,1054 +blinker-1.8.2.dist-info/METADATA,sha256=3tEx40hm9IEofyFqDPJsDPE9MAIEhtifapoSp7FqzuA,1633 +blinker-1.8.2.dist-info/RECORD,, +blinker-1.8.2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +blinker/__init__.py,sha256=ymyJY_PoTgBzaPgdr4dq-RRsGh7D-sYQIGMNp8Rx4qc,1577 +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=nIZJEtXQ8LLZZJrwVp2wQcdfCzDixvAHR9VpSWiyVcQ,22574 +blinker/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/WHEEL b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.dist-info/WHEEL new file mode 100644 index 0000000..3b5e64b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker-1.8.2.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/flask_auth_app/authenv/lib/python3.10/site-packages/blinker/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker/__init__.py new file mode 100644 index 0000000..c93527e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker/__init__.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +import typing as t + +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", +] + + +def __getattr__(name: str) -> t.Any: + import warnings + + if name == "__version__": + import importlib.metadata + + warnings.warn( + "The '__version__' attribute is deprecated and will be removed in" + " Blinker 1.9.0. Use feature detection or" + " 'importlib.metadata.version(\"blinker\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("blinker") + + if name == "receiver_connected": + from .base import _receiver_connected + + warnings.warn( + "The global 'receiver_connected' signal is deprecated and will be" + " removed in Blinker 1.9. Use 'Signal.receiver_connected' and" + " 'Signal.receiver_disconnected' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _receiver_connected + + if name == "WeakNamespace": + from .base import _WeakNamespace + + warnings.warn( + "'WeakNamespace' is deprecated and will be removed in Blinker 1.9." + " Use 'Namespace' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _WeakNamespace + + raise AttributeError(name) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/blinker/_utilities.py b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker/_utilities.py new file mode 100644 index 0000000..000c902 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker/_utilities.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +import collections.abc as c +import inspect +import typing as t +from weakref import ref +from weakref import WeakMethod + +T = t.TypeVar("T") + + +class Symbol: + """A constant symbol, nicer than ``object()``. Repeated calls return the + same instance. + + >>> 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/flask_auth_app/authenv/lib/python3.10/site-packages/blinker/base.py b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker/base.py new file mode 100644 index 0000000..ec494b1 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker/base.py @@ -0,0 +1,621 @@ +from __future__ import annotations + +import collections.abc as c +import typing as t +import warnings +import weakref +from collections import defaultdict +from contextlib import AbstractContextManager +from contextlib import contextmanager +from functools import cached_property +from inspect import iscoroutinefunction +from weakref import WeakValueDictionary + +from ._utilities import make_id +from ._utilities import make_ref +from ._utilities import Symbol + +if t.TYPE_CHECKING: + 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 + + if _receiver_connected.receivers and self is not _receiver_connected: + try: + _receiver_connected.send( + self, receiver_arg=receiver, sender_arg=sender, weak_arg=weak + ) + except TypeError: + 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 temporarily_connected_to( + self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY + ) -> AbstractContextManager[None]: + """Deprecated alias for :meth:`connected_to`. + + .. deprecated:: 1.1 + Renamed to ``connected_to``. Will be removed in Blinker 1.9. + + .. versionadded:: 0.9 + """ + warnings.warn( + "'temporarily_connected_to' is renamed to 'connected_to'. The old name is" + " deprecated and will be removed in Blinker 1.9.", + DeprecationWarning, + stacklevel=2, + ) + return self.connected_to(receiver, sender) + + 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: + 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() + + +_receiver_connected = Signal( + """\ +Sent by a :class:`Signal` after a receiver connects. + +:argument: the Signal that was connected to +:keyword receiver_arg: the connected receiver +:keyword sender_arg: the sender to connect to +:keyword weak_arg: true if the connection to receiver_arg is a weak reference + +.. deprecated:: 1.2 + Individual signals have their own :attr:`~Signal.receiver_connected` and + :attr:`~Signal.receiver_disconnected` signals with a slightly simplified + call signature. This global signal will be removed in Blinker 1.9. +""" +) + + +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 + + +if t.TYPE_CHECKING: + + class PNamespaceSignal(t.Protocol): + def __call__(self, name: str, doc: str | None = None) -> NamedSignal: ... + + # Python < 3.9 + _NamespaceBase = dict[str, NamedSignal] # type: ignore[misc] +else: + _NamespaceBase = dict + + +class Namespace(_NamespaceBase): + """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 _WeakNamespace(WeakValueDictionary): # type: ignore[type-arg] + """A weak mapping of names to signals. + + Automatically cleans up unused signals when the last reference goes out + of scope. This namespace implementation provides similar behavior to Blinker + <= 1.2. + + .. deprecated:: 1.3 + Will be removed in Blinker 1.9. + + .. versionadded:: 1.3 + """ + + def __init__(self) -> None: + warnings.warn( + "'WeakNamespace' is deprecated and will be removed in Blinker 1.9." + " Use 'Namespace' instead.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__() + + 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] # type: ignore[no-any-return] + + +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. +""" + + +def __getattr__(name: str) -> t.Any: + if name == "receiver_connected": + warnings.warn( + "The global 'receiver_connected' signal is deprecated and will be" + " removed in Blinker 1.9. Use 'Signal.receiver_connected' and" + " 'Signal.receiver_disconnected' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _receiver_connected + + if name == "WeakNamespace": + warnings.warn( + "'WeakNamespace' is deprecated and will be removed in Blinker 1.9." + " Use 'Namespace' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _WeakNamespace + + raise AttributeError(name) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/blinker/py.typed b/flask_auth_app/authenv/lib/python3.10/site-packages/blinker/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/INSTALLER b/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/LICENSE.rst b/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/LICENSE.rst new file mode 100644 index 0000000..d12a849 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2014 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/METADATA b/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/METADATA new file mode 100644 index 0000000..7a6bbb2 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/METADATA @@ -0,0 +1,103 @@ +Metadata-Version: 2.1 +Name: click +Version: 8.1.7 +Summary: Composable command line interface toolkit +Home-page: https://palletsprojects.com/p/click/ +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Changes, https://click.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/click/ +Project-URL: Issue Tracker, https://github.com/pallets/click/issues/ +Project-URL: Chat, https://discord.gg/pallets +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: colorama ; platform_system == "Windows" +Requires-Dist: importlib-metadata ; python_version < "3.8" + +\$ click\_ +========== + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U click + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + import click + + @click.command() + @click.option("--count", default=1, help="Number of greetings.") + @click.option("--name", prompt="Your name", help="The person to greet.") + def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + + if __name__ == '__main__': + hello() + +.. code-block:: text + + $ python hello.py --count=3 + Your name: Click + Hello, Click! + Hello, Click! + Hello, Click! + + +Donate +------ + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://click.palletsprojects.com/ +- Changes: https://click.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/click/ +- Source Code: https://github.com/pallets/click +- Issue Tracker: https://github.com/pallets/click/issues +- Chat: https://discord.gg/pallets diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/RECORD b/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/RECORD new file mode 100644 index 0000000..3ff0797 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/RECORD @@ -0,0 +1,39 @@ +click-8.1.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.1.7.dist-info/LICENSE.rst,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click-8.1.7.dist-info/METADATA,sha256=qIMevCxGA9yEmJOM_4WHuUJCwWpsIEVbCPOhs45YPN4,3014 +click-8.1.7.dist-info/RECORD,, +click-8.1.7.dist-info/WHEEL,sha256=5sUXSg9e4bi7lTLOHcm6QEYwO5TIF1TNbTSVFVjcJcc,92 +click-8.1.7.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 +click/__init__.py,sha256=YDDbjm406dTOA0V8bTtdGnhN7zj5j-_dFRewZF_pLvw,3138 +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__/_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=5318agQpbt4kroKsbqDOYpTSWzL_YCZVUQiTT04yXmc,18744 +click/_termui_impl.py,sha256=3dFYv4445Nw-rFvZOTBMBPYwB1bxnmNk9Du6Dm_oBSU,24069 +click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 +click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 +click/core.py,sha256=j6oEWtGgGna8JarD6WxhXmNnxLnfRjwXglbBc-8jr7U,114086 +click/decorators.py,sha256=-ZlbGYgV-oI8jr_oH4RpuL1PFS-5QmeuEAsLDAYgxtw,18719 +click/exceptions.py,sha256=fyROO-47HWFDjt2qupo7A3J32VlpM-ovJnfowu92K3s,9273 +click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 +click/globals.py,sha256=TP-qM88STzc7f127h35TD_v920FgfOD2EwzqA0oE8XU,1961 +click/parser.py,sha256=LKyYQE9ZLj5KgIDXkrcTHQRXIggfoivX14_UVIn56YA,19067 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=Ty3VM_ts0sQhj6u7eFTiLwHPoTgcXTGEAUg2OpLqYKw,18460 +click/termui.py,sha256=H7Q8FpmPelhJ2ovOhfCRhjMtCpNyjFXryAMLZODqsdc,28324 +click/testing.py,sha256=1Qd4kS5bucn1hsNIRryd0WtTMuCpkA93grkWxT8POsU,16084 +click/types.py,sha256=TZvz3hKvBztf-Hpa2enOmP4eznSPLzijjig5b_0XMxE,36391 +click/utils.py,sha256=1476UduUNY6UePGU4m18uzVHLt1sKM2PP3yWsQhbItM,20298 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/WHEEL b/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/WHEEL new file mode 100644 index 0000000..2c08da0 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.41.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/top_level.txt b/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/top_level.txt new file mode 100644 index 0000000..dca9a90 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click-8.1.7.dist-info/top_level.txt @@ -0,0 +1 @@ +click diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/__init__.py new file mode 100644 index 0000000..9a1dab0 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/__init__.py @@ -0,0 +1,73 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" +from .core import Argument as Argument +from .core import BaseCommand as BaseCommand +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import MultiCommand as MultiCommand +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .parser import OptionParser as OptionParser +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + +__version__ = "8.1.7" diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/_compat.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/_compat.py new file mode 100644 index 0000000..23f8866 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/_compat.py @@ -0,0 +1,623 @@ +import codecs +import io +import os +import re +import sys +import typing as t +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +WIN = sys.platform.startswith("win") +auto_wrap_for_ansi: t.Optional[t.Callable[[t.TextIO], t.TextIO]] = None +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def is_ascii_encoding(encoding: str) -> bool: + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +def get_best_encoding(stream: t.IO[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: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, + **extra: t.Any, + ) -> None: + self._stream = stream = t.cast( + t.BinaryIO, _FixupStream(stream, force_readable, force_writable) + ) + super().__init__(stream, encoding, errors, **extra) + + def __del__(self) -> None: + try: + self.detach() + except Exception: + pass + + def isatty(self) -> bool: + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream: + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__( + self, + stream: t.BinaryIO, + force_readable: bool = False, + force_writable: bool = False, + ): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + def read1(self, size: int) -> bytes: + f = getattr(self._stream, "read1", None) + + if f is not None: + return t.cast(bytes, f(size)) + + return self._stream.read(size) + + def readable(self) -> bool: + if self._force_readable: + return True + x = getattr(self._stream, "readable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self) -> bool: + if self._force_writable: + return True + x = getattr(self._stream, "writable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.write("") # type: ignore + except Exception: + try: + self._stream.write(b"") + except Exception: + return False + return True + + def seekable(self) -> bool: + x = getattr(self._stream, "seekable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +def _is_binary_reader(stream: t.IO[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.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _find_binary_writer(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _stream_is_misconfigured(stream: t.TextIO) -> bool: + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") + + +def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: t.Optional[str]) -> bool: + """A stream attribute is compatible if it is equal to the + desired value or the desired value is unset and the attribute + has a value. + """ + stream_value = getattr(stream, attr, None) + return stream_value == value or (value is None and stream_value is not None) + + +def _is_compatible_text_stream( + stream: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> bool: + """Check if a stream's encoding and errors attributes are + compatible with the desired values. + """ + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) + + +def _force_correct_text_stream( + text_stream: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + is_binary: t.Callable[[t.IO[t.Any], bool], bool], + find_binary: t.Callable[[t.IO[t.Any]], t.Optional[t.BinaryIO]], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if is_binary(text_stream, False): + binary_reader = t.cast(t.BinaryIO, text_stream) + else: + text_stream = t.cast(t.TextIO, text_stream) + # If the stream looks compatible, and won't default to a + # misconfigured ascii encoding, return it as-is. + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) + ): + return text_stream + + # Otherwise, get the underlying binary reader. + possible_binary_reader = find_binary(text_stream) + + # If that's not possible, silently use the original reader + # and get mojibake instead of exceptions. + if possible_binary_reader is None: + return text_stream + + binary_reader = possible_binary_reader + + # Default errors to replace instead of strict in order to get + # something that works. + if errors is None: + errors = "replace" + + # Wrap the binary stream in a text stream with the correct + # encoding parameters. + return _make_text_stream( + binary_reader, + encoding, + errors, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def _force_correct_text_reader( + text_reader: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_reader, + encoding, + errors, + _is_binary_reader, + _find_binary_reader, + force_readable=force_readable, + ) + + +def _force_correct_text_writer( + text_writer: t.IO[t.Any], + encoding: t.Optional[str], + errors: t.Optional[str], + force_writable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_writer, + encoding, + errors, + _is_binary_writer, + _find_binary_writer, + force_writable=force_writable, + ) + + +def get_binary_stdin() -> t.BinaryIO: + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") + return reader + + +def get_binary_stdout() -> t.BinaryIO: + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdout.") + return writer + + +def get_binary_stderr() -> t.BinaryIO: + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stderr.") + return writer + + +def get_text_stdin( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) + + +def get_text_stdout( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) + + +def get_text_stderr( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) + + +def _wrap_io_open( + file: t.Union[str, "os.PathLike[str]", int], + mode: str, + encoding: t.Optional[str], + errors: t.Optional[str], +) -> 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: "t.Union[str, os.PathLike[str]]", + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, +) -> t.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: t.Optional[int] = os.stat(filename).st_mode + except OSError: + perm = None + + flags = os.O_RDWR | os.O_CREAT | os.O_EXCL + + if binary: + flags |= getattr(os, "O_BINARY", 0) + + while True: + tmp_filename = os.path.join( + os.path.dirname(filename), + f".__atomic-write{random.randrange(1 << 32):08x}", + ) + try: + fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) + break + except OSError as e: + if e.errno == errno.EEXIST or ( + os.name == "nt" + and e.errno == errno.EACCES + and os.path.isdir(e.filename) + and os.access(e.filename, os.W_OK) + ): + continue + raise + + if perm is not None: + os.chmod(tmp_filename, perm) # in case perm includes bits in umask + + f = _wrap_io_open(fd, mode, encoding, errors) + af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) + return t.cast(t.IO[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: t.Optional[t.Type[BaseException]], *_: t.Any) -> 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.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None +) -> bool: + if color is None: + if stream is None: + stream = sys.stdin + return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not color + + +# On Windows, wrap the output streams with colorama to support ANSI +# color codes. +# NOTE: double check is needed so mypy does not analyze this on Linux +if sys.platform.startswith("win") and WIN: + from ._winconsole import _get_windows_console_stream + + def _get_argv_encoding() -> str: + import locale + + return locale.getpreferredencoding() + + _ansi_stream_wrappers: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi( # noqa: F811 + stream: t.TextIO, color: t.Optional[bool] = None + ) -> t.TextIO: + """Support ANSI color and style codes on Windows by wrapping a + stream with colorama. + """ + try: + cached = _ansi_stream_wrappers.get(stream) + except Exception: + cached = None + + if cached is not None: + return cached + + import colorama + + strip = should_strip_ansi(stream, color) + ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) + rv = t.cast(t.TextIO, ansi_wrapper.stream) + _write = rv.write + + def _safe_write(s): + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] + ) -> t.Optional[t.TextIO]: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO[t.Any]) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.Optional[t.TextIO]], + wrapper_func: t.Callable[[], t.TextIO], +) -> t.Callable[[], t.Optional[t.TextIO]]: + cache: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.Optional[t.TextIO]: + 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: t.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: t.Mapping[ + str, t.Callable[[t.Optional[str], t.Optional[str]], t.TextIO] +] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/_termui_impl.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/_termui_impl.py new file mode 100644 index 0000000..f744657 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/_termui_impl.py @@ -0,0 +1,739 @@ +""" +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. +""" +import contextlib +import math +import os +import sys +import time +import typing as t +from gettext import gettext as _ +from io import StringIO +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: t.Optional[t.Iterable[V]], + length: t.Optional[int] = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + label: t.Optional[str] = None, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, + width: int = 30, + ) -> None: + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label: 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(t.Iterable[V], range(length)) + self.iter: t.Iterable[V] = iter(iterable) + self.length = length + self.pos = 0 + self.avg: t.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: t.Optional[int] = None + self.entered: bool = False + self.current_item: t.Optional[V] = None + self.is_hidden: bool = not isatty(self.file) + self._last_line: t.Optional[str] = None + + def __enter__(self) -> "ProgressBar[V]": + self.entered = True + self.render_progress() + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self.render_finish() + + def __iter__(self) -> t.Iterator[V]: + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + self.render_progress() + return self.generator() + + def __next__(self) -> V: + # Iteration is defined in terms of a generator function, + # returned by iter(self); use that to define next(). This works + # because `self.iter` is an iterable consumed by that generator, + # so it is re-entry safe. Calling `next(self.generator())` + # twice works and does "what you want". + return next(iter(self)) + + def render_finish(self) -> None: + if self.is_hidden: + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self) -> float: + if self.finished: + return 1.0 + return min(self.pos / (float(self.length or 1) or 1), 1.0) + + @property + def time_per_iteration(self) -> float: + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self) -> float: + if self.length is not None and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self) -> str: + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" + else: + return f"{hours:02}:{minutes:02}:{seconds:02}" + return "" + + def format_pos(self) -> str: + pos = str(self.pos) + if self.length is not None: + pos += f"/{self.length}" + return pos + + def format_pct(self) -> str: + return f"{int(self.pct * 100): 4}%"[1:] + + def format_bar(self) -> str: + if self.length is not None: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + chars = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + chars[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(chars) + return bar + + def format_progress_line(self) -> str: + show_percent = self.show_percent + + info_bits = [] + if self.length is not None and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() + + def render_progress(self) -> None: + import shutil + + if self.is_hidden: + # Only output the label as it changes if the output is not a + # TTY. Use file=stderr if you expect to be piping stdout. + if self._last_line != self.label: + self._last_line = self.label + echo(self.label, file=self.file, color=self.color) + + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, shutil.get_terminal_size().columns - clutter_length) + if new_width < old_width: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) # type: ignore + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) + # Render the line only if it changed. + + if line != self._last_line: + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps: int) -> None: + self.pos += n_steps + if self.length is not None and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length is not None + + def update(self, n_steps: int, current_item: t.Optional[V] = None) -> None: + """Update the progress bar by advancing a specified number of + steps, and optionally set the ``current_item`` for this new + position. + + :param n_steps: Number of steps to advance. + :param current_item: Optional item to set as ``current_item`` + for the updated position. + + .. versionchanged:: 8.0 + Added the ``current_item`` optional parameter. + + .. versionchanged:: 8.0 + Only render when the number of steps meets the + ``update_min_steps`` threshold. + """ + if current_item is not None: + self.current_item = current_item + + self._completed_intervals += n_steps + + if self._completed_intervals >= self.update_min_steps: + self.make_step(self._completed_intervals) + self.render_progress() + self._completed_intervals = 0 + + def finish(self) -> None: + self.eta_known = False + self.current_item = None + self.finished = True + + def generator(self) -> t.Iterator[V]: + """Return a generator which yields the items added to the bar + during construction, and updates the progress bar *after* the + yielded block returns. + """ + # WARNING: the iterator interface for `ProgressBar` relies on + # this and only works because this is a simple generator which + # doesn't create or manage additional state. If this function + # changes, the impact should be evaluated both against + # `iter(bar)` and `next(bar)`. `next()` in particular may call + # `self.generator()` repeatedly, and this must remain safe in + # order for that interface to work. + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + + if self.is_hidden: + yield from self.iter + else: + for rv in self.iter: + self.current_item = rv + + # This allows show_item_func to be updated before the + # item is processed. Only trigger at the beginning of + # the update interval. + if self._completed_intervals == 0: + self.render_progress() + + yield rv + self.update(1) + + self.finish() + self.render_progress() + + +def pager(generator: t.Iterable[str], color: t.Optional[bool] = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + + # 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) + pager_cmd = (os.environ.get("PAGER", None) or "").strip() + if pager_cmd: + if WIN: + return _tempfilepager(generator, pager_cmd, color) + return _pipepager(generator, pager_cmd, color) + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if WIN or sys.platform.startswith("os2"): + return _tempfilepager(generator, "more <", color) + if hasattr(os, "system") and os.system("(less) 2>/dev/null") == 0: + return _pipepager(generator, "less", color) + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if hasattr(os, "system") and os.system(f'more "{filename}"') == 0: + return _pipepager(generator, "more", color) + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> None: + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + """ + import subprocess + + env = dict(os.environ) + + # If we're piping to less we might support colors under the + # condition that + cmd_detail = cmd.rsplit("/", 1)[-1].split() + if color is None and cmd_detail[0] == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_detail[1:])}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, env=env) + stdin = t.cast(t.BinaryIO, c.stdin) + encoding = get_best_encoding(stdin) + try: + for text in generator: + if not color: + text = strip_ansi(text) + + stdin.write(text.encode(encoding, "replace")) + except (OSError, KeyboardInterrupt): + pass + else: + stdin.close() + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + +def _tempfilepager( + generator: t.Iterable[str], cmd: str, color: t.Optional[bool] +) -> None: + """Page through text by invoking a program on a temporary file.""" + import tempfile + + fd, filename = tempfile.mkstemp() + # TODO: This never terminates if the passed generator never terminates. + text = "".join(generator) + if not color: + text = strip_ansi(text) + encoding = get_best_encoding(sys.stdout) + with open_stream(filename, "wb")[0] as f: + f.write(text.encode(encoding)) + try: + os.system(f'{cmd} "{filename}"') + finally: + os.close(fd) + os.unlink(filename) + + +def _nullpager( + stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] +) -> None: + """Simply print unformatted text. This is the ultimate fallback.""" + for text in generator: + if not color: + text = strip_ansi(text) + stream.write(text) + + +class Editor: + def __init__( + self, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + ) -> None: + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self) -> str: + if self.editor is not None: + return self.editor + for key in "VISUAL", "EDITOR": + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return "notepad" + for editor in "sensible-editor", "vim", "nano": + if os.system(f"which {editor} >/dev/null 2>&1") == 0: + return editor + return "vi" + + def edit_file(self, filename: str) -> None: + import subprocess + + editor = self.get_editor() + environ: t.Optional[t.Dict[str, str]] = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + try: + c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True) + exit_code = c.wait() + if exit_code != 0: + raise ClickException( + _("{editor}: Editing failed").format(editor=editor) + ) + except OSError as e: + raise ClickException( + _("{editor}: Editing failed: {e}").format(editor=editor, e=e) + ) from e + + def edit(self, text: t.Optional[t.AnyStr]) -> t.Optional[t.AnyStr]: + import tempfile + + if not text: + data = b"" + elif isinstance(text, (bytes, bytearray)): + data = text + else: + if text and not text.endswith("\n"): + text += "\n" + + if WIN: + data = text.replace("\n", "\r\n").encode("utf-8-sig") + else: + data = text.encode("utf-8") + + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) + f: t.BinaryIO + + try: + with os.fdopen(fd, "wb") as f: + f.write(data) + + # If the filesystem resolution is 1 second, like Mac OS + # 10.12 Extended, or 2 seconds, like FAT32, and the editor + # closes very fast, require_save can fail. Set the modified + # time to be 2 seconds in the past to work around this. + os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) + # Depending on the resolution, the exact value might not be + # recorded, so get the new recorded value. + timestamp = os.path.getmtime(name) + + self.edit_file(name) + + if self.require_save and os.path.getmtime(name) == timestamp: + return None + + with open(name, "rb") as f: + rv = f.read() + + if isinstance(text, (bytes, bytearray)): + return rv + + return rv.decode("utf-8-sig").replace("\r\n", "\n") # type: ignore + finally: + os.unlink(name) + + +def open_url(url: str, wait: bool = False, locate: bool = False) -> int: + import subprocess + + def _unquote_file(url: str) -> str: + from urllib.parse import unquote + + if url.startswith("file://"): + url = unquote(url[7:]) + + return url + + if sys.platform == "darwin": + args = ["open"] + if wait: + args.append("-W") + if locate: + args.append("-R") + args.append(_unquote_file(url)) + null = open("/dev/null", "w") + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url.replace('"', "")) + args = f'explorer /select,"{url}"' + else: + url = url.replace('"', "") + wait_str = "/WAIT" if wait else "" + args = f'start {wait_str} "" "{url}"' + return os.system(args) + elif CYGWIN: + if locate: + url = os.path.dirname(_unquote_file(url).replace('"', "")) + args = f'cygstart "{url}"' + else: + url = url.replace('"', "") + wait_str = "-w" if wait else "" + args = f'cygstart {wait_str} "{url}"' + return os.system(args) + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or "." + else: + url = _unquote_file(url) + c = subprocess.Popen(["xdg-open", url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(("http://", "https://")) and not locate and not wait: + import webbrowser + + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch: str) -> t.Optional[BaseException]: + if ch == "\x03": + raise KeyboardInterrupt() + + if ch == "\x04" and not WIN: # Unix-like, Ctrl+D + raise EOFError() + + if ch == "\x1a" and WIN: # Windows, Ctrl+Z + raise EOFError() + + return None + + +if WIN: + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + yield -1 + + def getchar(echo: bool) -> str: + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + func: t.Callable[[], str] + + if echo: + func = msvcrt.getwche # type: ignore + else: + func = msvcrt.getwch # type: ignore + + rv = func() + + if rv in ("\x00", "\xe0"): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + + _translate_ch_to_exc(rv) + return rv + +else: + import tty + import termios + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + f: t.Optional[t.TextIO] + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/_textwrap.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/_textwrap.py new file mode 100644 index 0000000..b47dcbd --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/_textwrap.py @@ -0,0 +1,49 @@ +import textwrap +import typing as t +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: t.List[str], + cur_line: t.List[str], + cur_len: int, + width: int, + ) -> None: + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = last[:space_left] + res = last[space_left:] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + @contextmanager + def extra_indent(self, indent: str) -> t.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/_winconsole.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/_winconsole.py new file mode 100644 index 0000000..6b20df3 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/_winconsole.py @@ -0,0 +1,279 @@ +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prompt. +import io +import sys +import time +import typing as t +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import Structure +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR + +from ._compat import _NonClosingTextIOWrapper + +assert sys.platform == "win32" +import msvcrt # noqa: E402 +from ctypes import windll # noqa: E402 +from ctypes import WINFUNCTYPE # noqa: E402 + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetConsoleMode = kernel32.GetConsoleMode +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) +LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b"\x1a" +MAX_BYTES_WRITTEN = 32767 + +try: + from ctypes import pythonapi +except ImportError: + # On PyPy we cannot get buffers so our ability to operate here is + # severely limited. + get_buffer = None +else: + + class Py_buffer(Structure): + _fields_ = [ + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), + ] + + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release + + def get_buffer(obj, writable=False): + buf = Py_buffer() + flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + return buffer_type.from_address(buf.buf) + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle): + self.handle = handle + + def isatty(self): + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self): + return True + + def readinto(self, b): + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError(f"Windows error: {GetLastError()}") + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + def writable(self): + return True + + @staticmethod + def _get_error_message(errno): + if errno == ERROR_SUCCESS: + return "ERROR_SUCCESS" + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return "ERROR_NOT_ENOUGH_MEMORY" + return f"Windows error {errno}" + + def write(self, b): + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream: + def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self) -> str: + return self.buffer.name + + def write(self, x: t.AnyStr) -> int: + if isinstance(x, str): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines: t.Iterable[t.AnyStr]) -> None: + for line in lines: + self.write(line) + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._text_stream, name) + + def isatty(self) -> bool: + return self.buffer.isatty() + + def __repr__(self): + return f"" + + +def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +_stream_factories: t.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _is_console(f: t.TextIO) -> bool: + if not hasattr(f, "fileno"): + return False + + try: + fileno = f.fileno() + except (OSError, io.UnsupportedOperation): + return False + + handle = msvcrt.get_osfhandle(fileno) + return bool(GetConsoleMode(handle, byref(DWORD()))) + + +def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> t.Optional[t.TextIO]: + if ( + get_buffer is not None + and encoding in {"utf-16-le", None} + and errors in {"strict", None} + and _is_console(f) + ): + func = _stream_factories.get(f.fileno()) + if func is not None: + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/core.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/core.py new file mode 100644 index 0000000..cc65e89 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/core.py @@ -0,0 +1,3042 @@ +import enum +import errno +import inspect +import os +import sys +import typing as t +from collections import abc +from contextlib import contextmanager +from contextlib import ExitStack +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat +from types import TracebackType + +from . import types +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _flag_needs_value +from .parser import OptionParser +from .parser import split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import echo +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper + +if t.TYPE_CHECKING: + import typing_extensions as te + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +V = t.TypeVar("V") + + +def _complete_visible_commands( + ctx: "Context", incomplete: str +) -> t.Iterator[t.Tuple[str, "Command"]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(MultiCommand, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_multicommand( + base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, MultiCommand): + return + if register: + hint = ( + "It is not possible to add multi commands as children to" + " another multi command that is in chain mode." + ) + else: + hint = ( + "Found a multi command as subcommand to a multi command" + " that is in chain mode. This is not supported." + ) + raise RuntimeError( + f"{hint}. Command {base_command.name!r} is set to chain and" + f" {cmd_name!r} was added as a subcommand but it in itself is a" + f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" + f" within a chained {type(base_command).__name__} named" + f" {base_command.name!r})." + ) + + +def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size))) + + +@contextmanager +def augment_usage_errors( + ctx: "Context", param: t.Optional["Parameter"] = None +) -> t.Iterator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: t.Sequence["Parameter"], + declaration_order: t.Sequence["Parameter"], +) -> t.List["Parameter"]: + """Given a sequence of parameters in the order as should be considered + for processing and an iterable of parameters that exist, this returns + a list in the correct order as they should be processed. + """ + + def sort_key(item: "Parameter") -> t.Tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.Enum): + """This is an :class:`~enum.Enum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show the default value for commands. If this + value is not set, it defaults to the value from the parent + context. ``Command.show_default`` overrides this default for the + specific command. + + .. versionchanged:: 8.1 + The ``show_default`` parameter is overridden by + ``Command.show_default``, instead of the other way around. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: t.Type["HelpFormatter"] = HelpFormatter + + def __init__( + self, + command: "Command", + parent: t.Optional["Context"] = None, + info_name: t.Optional[str] = None, + obj: t.Optional[t.Any] = None, + auto_envvar_prefix: t.Optional[str] = None, + default_map: t.Optional[t.MutableMapping[str, t.Any]] = None, + terminal_width: t.Optional[int] = None, + max_content_width: t.Optional[int] = None, + resilient_parsing: bool = False, + allow_extra_args: t.Optional[bool] = None, + allow_interspersed_args: t.Optional[bool] = None, + ignore_unknown_options: t.Optional[bool] = None, + help_option_names: t.Optional[t.List[str]] = None, + token_normalize_func: t.Optional[t.Callable[[str], str]] = None, + color: t.Optional[bool] = None, + show_default: t.Optional[bool] = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params: t.Dict[str, t.Any] = {} + #: the leftover arguments. + self.args: t.List[str] = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self.protected_args: t.List[str] = [] + #: the collected prefixes of the command's options. + self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set() + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map: t.Optional[t.MutableMapping[str, t.Any]] = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand: t.Optional[str] = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width: t.Optional[int] = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width: t.Optional[int] = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args: bool = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options: bool = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names: t.List[str] = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func: t.Optional[ + t.Callable[[str], str] + ] = token_normalize_func + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing: bool = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color: t.Optional[bool] = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default: t.Optional[bool] = show_default + + self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: t.Dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> "Context": + self._depth += 1 + push_context(self) + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> t.Dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: t.ContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._exit_stack.close() + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> "Context": + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: + """Finds the closest object of a given type.""" + node: t.Optional["Context"] = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: t.Type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.overload + def lookup_default( + self, name: str, call: "te.Literal[False]" = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: + """Get the default for a parameter from :attr:`default_map`. + + :param name: Name of the parameter. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + if self.default_map is not None: + value = self.default_map.get(name) + + if call and callable(value): + return value() + + return value + + return None + + def fail(self, message: str) -> "te.NoReturn": + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self) -> "te.NoReturn": + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> "te.NoReturn": + """Exits the application with a given exit code.""" + raise Exit(code) + + def get_usage(self) -> str: + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self) -> str: + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def _make_sub_context(self, command: "Command") -> "Context": + """Create a new context of the same type as this context, but + for a new command. + + :meta private: + """ + return type(self)(command, info_name=command.name, parent=self) + + @t.overload + def invoke( + __self, # noqa: B902 + __callback: "t.Callable[..., V]", + *args: t.Any, + **kwargs: t.Any, + ) -> V: + ... + + @t.overload + def invoke( + __self, # noqa: B902 + __callback: "Command", + *args: t.Any, + **kwargs: t.Any, + ) -> t.Any: + ... + + def invoke( + __self, # noqa: B902 + __callback: t.Union["Command", "t.Callable[..., V]"], + *args: t.Any, + **kwargs: t.Any, + ) -> t.Union[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. + + Note that before Click 3.2 keyword arguments were not properly filled + in against the intention of this code and no context was created. For + more information about this change and why it was done in a bugfix + release see :ref:`upgrade-to-3.2`. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + """ + if isinstance(__callback, Command): + other_cmd = __callback + + if other_cmd.callback is None: + raise TypeError( + "The given command does not have a callback that can be invoked." + ) + else: + __callback = 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: + kwargs[param.name] = param.type_cast_value( # type: ignore + ctx, param.get_default(ctx) + ) + + # Track all kwargs as params, so that forward() will pass + # them on in subsequent calls. + ctx.params.update(kwargs) + else: + ctx = __self + + with augment_usage_errors(__self): + with ctx: + return __callback(*args, **kwargs) + + def forward( + __self, __cmd: "Command", *args: t.Any, **kwargs: t.Any # noqa: B902 + ) -> t.Any: + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if ``forward`` is called at multiple levels. + """ + # Can only forward to other commands, not direct callbacks. + if not isinstance(__cmd, Command): + raise TypeError("Callback is not a command.") + + for param in __self.params: + if param not in kwargs: + kwargs[param] = __self.params[param] + + return __self.invoke(__cmd, *args, **kwargs) + + def set_parameter_source(self, name: str, source: ParameterSource) -> None: + """Set the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + :param name: The name of the parameter. + :param source: A member of :class:`~click.core.ParameterSource`. + """ + self._parameter_source[name] = source + + def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]: + """Get the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + This can be useful for determining when a user specified a value + on the command line that is the same as the default value. It + will be :attr:`~click.core.ParameterSource.DEFAULT` only if the + value was actually taken from the default. + + :param name: The name of the parameter. + :rtype: ParameterSource + + .. versionchanged:: 8.0 + Returns ``None`` if the parameter was not provided from any + source. + """ + return self._parameter_source.get(name) + + +class BaseCommand: + """The base command implements the minimal API contract of commands. + Most code will never use this as it does not implement a lot of useful + functionality but it can act as the direct subclass of alternative + parsing methods that do not depend on the Click parser. + + For instance, this can be used to bridge Click and other systems like + argparse or docopt. + + Because base commands do not implement a lot of the API that other + parts of Click take for granted, they are not supported for all + operations. For instance, they cannot be used with the decorators + usually and they have no built-in callback system. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: t.Type[Context] = Context + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, + ) -> None: + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + + if context_settings is None: + context_settings = {} + + #: an optional dictionary with defaults passed to the context. + self.context_settings: t.MutableMapping[str, t.Any] = context_settings + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire structure + below this command. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + :param ctx: A :class:`Context` representing this command. + + .. versionadded:: 8.0 + """ + return {"name": self.name} + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get usage") + + def get_help(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get help") + + def make_context( + self, + info_name: t.Optional[str], + args: t.List[str], + parent: t.Optional[Context] = None, + **extra: t.Any, + ) -> Context: + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + To quickly customize the context class used without overriding + this method, set the :attr:`context_class` attribute. + + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it's + the name of the command. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + + .. versionchanged:: 8.0 + Added the :attr:`context_class` attribute. + """ + for key, value in self.context_settings.items(): + if key not in extra: + extra[key] = value + + ctx = self.context_class( + self, info_name=info_name, parent=parent, **extra # type: ignore + ) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + """Given a context and a list of arguments this creates the parser + and parses the arguments, then modifies the context as necessary. + This is automatically invoked by :meth:`make_context`. + """ + raise NotImplementedError("Base commands do not know how to parse arguments.") + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the command. The default + implementation is raising a not implemented error. + """ + raise NotImplementedError("Base commands are not invocable by default") + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. Other + command classes will return more completions. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, MultiCommand) and ctx.command.chain: + results.extend( + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + if name not in ctx.protected_args + ) + + return results + + @t.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: "te.Literal[True]" = True, + **extra: t.Any, + ) -> "te.NoReturn": + ... + + @t.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: + ... + + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt) 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: t.MutableMapping[str, t.Any], + prog_name: str, + complete_var: t.Optional[str] = None, + ) -> None: + """Check if the shell is asking for tab completion, process + that, then exit early. Called from :meth:`main` before the + program is invoked. + + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. Defaults to + ``_{PROG_NAME}_COMPLETE``. + + .. 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 Command(BaseCommand): + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + + :param deprecated: issues a message indicating that + the command is deprecated. + + .. versionchanged:: 8.1 + ``help``, ``epilog``, and ``short_help`` are stored unprocessed, + all formatting is done when outputting help text, not at init, + and is done even if not using the ``@command`` decorator. + + .. versionchanged:: 8.0 + Added a ``repr`` showing the command name. + + .. versionchanged:: 7.1 + Added the ``no_args_is_help`` parameter. + + .. versionchanged:: 2.0 + Added the ``context_settings`` parameter. + """ + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, + callback: t.Optional[t.Callable[..., t.Any]] = None, + params: t.Optional[t.List["Parameter"]] = None, + help: t.Optional[str] = None, + epilog: t.Optional[str] = None, + short_help: t.Optional[str] = None, + options_metavar: t.Optional[str] = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool = False, + ) -> None: + super().__init__(name, context_settings) + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params: t.List["Parameter"] = params or [] + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + info_dict.update( + params=[param.to_info_dict() for param in self.get_params(ctx)], + help=self.help, + epilog=self.epilog, + short_help=self.short_help, + hidden=self.hidden, + deprecated=self.deprecated, + ) + return info_dict + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> t.List["Parameter"]: + rv = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + rv = [*rv, help_option] + + return rv + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> t.List[str]: + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return list(all_names) + + def get_help_option(self, ctx: Context) -> t.Optional["Option"]: + """Returns the help option object.""" + help_options = self.get_help_option_names(ctx) + + if not help_options or not self.add_help_option: + return None + + def show_help(ctx: Context, param: "Parameter", value: str) -> None: + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + return Option( + help_options, + is_flag=True, + is_eager=True, + expose_value=False, + callback=show_help, + help=_("Show this message and exit."), + ) + + def make_parser(self, ctx: Context) -> OptionParser: + """Creates the underlying option parser for this command.""" + parser = OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + if self.short_help: + text = inspect.cleandoc(self.short_help) + elif self.help: + text = make_default_short_help(self.help, limit) + else: + text = "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + 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: + text = _("(Deprecated) {text}").format(text=text) + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + epilog = inspect.cleandoc(self.epilog) + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(epilog) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + value, args = param.handle_parse_result(ctx, opts, args) + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + ctx._opt_prefixes.update(parser._opt_prefixes) + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + message = _( + "DeprecationWarning: The command {name!r} is deprecated." + ).format(name=self.name) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) # type: ignore + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class MultiCommand(Command): + """A multi command is the basic implementation of a command that + dispatches to subcommands. The most common version is the + :class:`Group`. + + :param invoke_without_command: this controls how the multi command itself + is invoked. By default it's only invoked + if a subcommand is provided. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is enabled by default if + `invoke_without_command` is disabled or disabled + if it's enabled. If enabled this will add + ``--help`` as argument if no arguments are + passed. + :param subcommand_metavar: the string that is used in the documentation + to indicate the subcommand place. + :param chain: if this is set to `True` chaining of multiple subcommands + is enabled. This restricts the form of commands in that + they cannot have optional arguments but it allows + multiple commands to be chained together. + :param result_callback: The result callback to attach to this multi + command. This can be set or changed later with the + :meth:`result_callback` decorator. + :param attrs: Other command arguments described in :class:`Command`. + """ + + allow_extra_args = True + allow_interspersed_args = False + + def __init__( + self, + name: t.Optional[str] = None, + invoke_without_command: bool = False, + no_args_is_help: t.Optional[bool] = None, + subcommand_metavar: t.Optional[str] = None, + chain: bool = False, + result_callback: t.Optional[t.Callable[..., t.Any]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + if chain: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "Multi commands in chain mode cannot have" + " optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(__value, *args, **kwargs): # type: ignore + inner = old_callback(__value, *args, **kwargs) + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv + + return decorator + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx.protected_args = rest + ctx.args = [] + elif rest: + ctx.protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx.protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with the group return value for regular + # groups, or an empty list for chained groups. + with ctx: + rv = super().invoke(ctx) + return _process_result([] if self.chain else rv) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx.protected_args, *ctx.args] + ctx.args = [] + ctx.protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: t.List[str] + ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if split_opt(cmd_name)[0]: + self.parse_args(ctx, ctx.args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + """Given a context and a command name, this returns a + :class:`Command` object if it exists or returns `None`. + """ + raise NotImplementedError + + def list_commands(self, ctx: Context) -> t.List[str]: + """Returns a list of subcommand names in the order they should + appear. + """ + return [] + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class Group(MultiCommand): + """A group allows a command to have subcommands attached. This is + the most common way to implement nesting in Click. + + :param name: The name of the group command. + :param commands: A dict mapping names to :class:`Command` objects. + Can also be a list of :class:`Command`, which will use + :attr:`Command.name` to create the dict. + :param attrs: Other command arguments described in + :class:`MultiCommand`, :class:`Command`, and + :class:`BaseCommand`. + + .. versionchanged:: 8.0 + The ``commands`` argument can be a list of command objects. + """ + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: t.Optional[t.Type[Command]] = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: t.Optional[str] = None, + commands: t.Optional[ + t.Union[t.MutableMapping[str, Command], t.Sequence[Command]] + ] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands: t.MutableMapping[str, Command] = commands + + def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_multicommand(self, name, cmd, register=True) + self.commands[name] = cmd + + @t.overload + def command(self, __func: t.Callable[..., t.Any]) -> Command: + ... + + @t.overload + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: + ... + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], Command]: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + func: t.Optional[t.Callable[..., t.Any]] = 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.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "Group"]: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + func: t.Optional[t.Callable[..., t.Any]] = None + + if args and callable(args[0]): + assert ( + len(args) == 1 and not kwargs + ), "Use 'group(**kwargs)(callable)' to provide arguments." + (func,) = args + args = () + + if self.group_class is not None and kwargs.get("cls") is None: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> "Group": + cmd: Group = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> t.List[str]: + return sorted(self.commands) + + +class CommandCollection(MultiCommand): + """A command collection is a multi command that merges multiple multi + commands together into one. This is a straightforward implementation + that accepts a list of different multi commands as sources and + provides all the commands for each of them. + + See :class:`MultiCommand` and :class:`Command` for the description of + ``name`` and ``attrs``. + """ + + def __init__( + self, + name: t.Optional[str] = None, + sources: t.Optional[t.List[MultiCommand]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + #: The list of registered multi commands. + self.sources: t.List[MultiCommand] = sources or [] + + def add_source(self, multi_cmd: MultiCommand) -> None: + """Adds a new multi command to the chain dispatcher.""" + self.sources.append(multi_cmd) + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_multicommand(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> t.List[str]: + rv: t.Set[str] = set() + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> t.Iterator[t.Any]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter: + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The 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: a string or list of strings that are environment variables + that should be checked. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + required: bool = False, + default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, + callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, + nargs: t.Optional[int] = None, + multiple: bool = False, + metavar: t.Optional[str] = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, + shell_complete: t.Optional[ + t.Callable[ + [Context, "Parameter", str], + t.Union[t.List["CompletionItem"], t.List[str]], + ] + ] = None, + ) -> None: + self.name: t.Optional[str] + self.opts: t.List[str] + self.secondary_opts: t.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 = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + self._custom_shell_complete = shell_complete + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + # Skip no default or callable default. + check_default = default if not callable(default) else None + + if check_default is not None: + if multiple: + try: + # Only check the first value against nargs. + check_default = next(_check_iter(check_default), None) + except TypeError: + raise ValueError( + "'default' must be a list when 'multiple' is true." + ) from None + + # Can be None for multiple with empty default. + if nargs != 1 and check_default is not None: + try: + _check_iter(check_default) + except TypeError: + if multiple: + message = ( + "'default' must be a list of lists when 'multiple' is" + " true and 'nargs' != 1." + ) + else: + message = "'default' must be a list when 'nargs' != 1." + + raise ValueError(message) from None + + if nargs > 1 and len(check_default) != nargs: + subject = "item length" if multiple else "length" + raise ValueError( + f"'default' {subject} must match nargs={nargs}." + ) + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + "default": self.default, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + raise NotImplementedError() + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(self) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + """Get the default for the parameter. Tries + :meth:`Context.lookup_default` first, then the local default. + + :param ctx: Current context. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0.2 + Type casting is no longer performed when getting a default. + + .. versionchanged:: 8.0.1 + Type casting can fail in resilient parsing mode. Invalid + defaults will not prevent showing help text. + + .. versionchanged:: 8.0 + Looks at ``ctx.default_map`` first. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + value = ctx.lookup_default(self.name, call=False) # type: ignore + + if value is None: + value = self.default + + if call and callable(value): + value = value() + + return value + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + raise NotImplementedError() + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, t.Any] + ) -> t.Tuple[t.Any, ParameterSource]: + value = opts.get(self.name) # type: ignore + source = ParameterSource.COMMANDLINE + + if value is None: + value = self.value_from_envvar(ctx) + source = ParameterSource.ENVIRONMENT + + if value is None: + value = ctx.lookup_default(self.name) # type: ignore + source = ParameterSource.DEFAULT_MAP + + if value is None: + value = self.get_default(ctx) + source = ParameterSource.DEFAULT + + return value, source + + def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: + """Convert and validate a value against the option's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + return () if self.multiple or self.nargs == -1 else None + + def check_iter(value: t.Any) -> t.Iterator[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 + + 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: # t.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: # t.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: + if value is None: + return True + + if (self.nargs != 1 or self.multiple) and value == (): + return True + + return False + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + value = self.type_cast_value(ctx, value) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + if self.envvar is None: + return None + + if isinstance(self.envvar, str): + rv = os.environ.get(self.envvar) + + if rv: + return rv + else: + for envvar in self.envvar: + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + rv = self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str] + ) -> t.Tuple[t.Any, t.List[str]]: + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + ctx.set_parameter_source(self.name, source) # type: ignore + + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + + value = None + + if self.expose_value: + ctx.params[self.name] = value # type: ignore + + return value, args + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + pass + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [] + + def get_error_hint(self, ctx: Context) -> str: + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + """ + hint_list = self.opts or [self.human_readable_name] + return " / ".join(f"'{x}'" for x in hint_list) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. If a + ``shell_complete`` function was given during init, it is used. + Otherwise, the :attr:`type` + :meth:`~click.types.ParamType.shell_complete` function is used. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + if self._custom_shell_complete is not None: + results = self._custom_shell_complete(ctx, self, incomplete) + + if results and isinstance(results[0], str): + from click.shell_completion import CompletionItem + + results = [CompletionItem(c) for c in results] + + return t.cast(t.List["CompletionItem"], results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: Show the default value for this option in its + help text. Values are not shown by default, unless + :attr:`Context.show_default` is ``True``. If this value is a + string, it shows that string in parentheses instead of the + actual value. This is particularly useful for dynamic options. + For single option boolean flags, the default remains hidden if + its value is ``False``. + :param show_envvar: Controls if an environment variable should be + shown on the help page. Normally, environment variables are not + shown. + :param prompt: If set to ``True`` or a non empty string then the + user will be prompted for input. If set to ``True`` the prompt + will be the option name capitalized. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: If this is ``True`` then the input on the prompt + will be hidden from the user. This is useful for password input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + :param attrs: Other command arguments described in :class:`Parameter`. + + .. versionchanged:: 8.1.0 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1.0 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1.0 + The default of a single option boolean flag is not shown if the + default value is ``False``. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + show_default: t.Union[bool, str, None] = None, + prompt: t.Union[bool, str] = False, + confirmation_prompt: t.Union[bool, str] = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: t.Optional[bool] = None, + flag_value: t.Optional[t.Any] = None, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + help: t.Optional[str] = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + **attrs: t.Any, + ) -> None: + if help: + help = inspect.cleandoc(help) + + default_is_missing = "default" not in attrs + super().__init__(param_decls, type=type, multiple=multiple, **attrs) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # If prompt is enabled but not required, then the option can be + # used as a flag to indicate using prompt or flag_value. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + if is_flag is None: + if flag_value is not None: + # Implicitly a flag because flag_value was set. + is_flag = True + elif self._flag_needs_value: + # Not a flag, but when used as a flag it shows a prompt. + is_flag = False + else: + # Implicitly a flag because flag options were given. + is_flag = bool(self.secondary_opts) + elif is_flag is False and not self._flag_needs_value: + # Not a flag, and prompt is not enabled, can be used as a + # flag if flag_value is set. + self._flag_needs_value = flag_value is not None + + self.default: t.Union[t.Any, t.Callable[[], t.Any]] + + if is_flag and default_is_missing and not self.required: + if multiple: + self.default = () + else: + self.default = False + + if flag_value is None: + flag_value = not self.default + + self.type: types.ParamType + if is_flag and type is None: + # Re-guess the type from the flag value instead of the + # default. + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = is_flag + self.is_bool_flag: bool = is_flag and isinstance(self.type, types.BoolParamType) + self.flag_value: t.Any = flag_value + + # Counting + self.count = count + if count: + if type is None: + self.type = types.IntRange(min=0) + if default_is_missing: + self.default = 0 + + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + if __debug__: + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + + if self.prompt and self.is_flag and not self.is_bool_flag: + raise TypeError("'prompt' is not valid for non-boolean flag.") + + if not self.is_bool_flag and self.secondary_opts: + raise TypeError("Secondary flag is not valid for non-boolean flag.") + + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError( + "'prompt' with 'hide_input' is not valid for boolean flag." + ) + + if self.count: + if self.multiple: + raise TypeError("'count' is not valid with 'multiple'.") + + if self.is_flag: + raise TypeError("'count' is not valid with 'is_flag'.") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + flag_value=self.flag_value, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if decl.isidentifier(): + if name is not None: + raise TypeError(f"Name '{name}' defined twice") + name = decl + else: + split_char = ";" if decl[:1] == "/" else "/" + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + if first == second: + raise ValueError( + f"Boolean option {decl!r} cannot use the" + " same flag for true/false." + ) + else: + possible_names.append(split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace("-", "_").lower() + if not name.isidentifier(): + name = None + + if name is None: + if not expose_value: + return None, opts, secondary_opts + raise TypeError("Could not determine name for option") + + if not opts and not secondary_opts: + raise TypeError( + f"No options defined but a name was passed ({name})." + " Did you mean to declare an argument instead? Did" + f" you mean to pass '--{name}'?" + ) + + return name, opts, secondary_opts + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + if self.multiple: + action = "append" + elif self.count: + action = "count" + else: + action = "store" + + if self.is_flag: + action = f"{action}_const" + + if self.is_bool_flag and self.secondary_opts: + parser.add_option( + obj=self, opts=self.opts, dest=self.name, action=action, const=True + ) + parser.add_option( + obj=self, + opts=self.secondary_opts, + dest=self.name, + action=action, + const=False, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + const=self.flag_value, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + nargs=self.nargs, + ) + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: t.Sequence[str]) -> str: + nonlocal any_prefix_is_slash + + rv, any_slashes = join_options(opts) + + if any_slashes: + any_prefix_is_slash = True + + if not self.is_flag and not self.count: + rv += f" {self.make_metavar()}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + extra = [] + + if self.show_envvar: + envvar = self.envvar + + if envvar is None: + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + + if envvar is not None: + var_str = ( + envvar + if isinstance(envvar, str) + else ", ".join(str(d) for d in envvar) + ) + extra.append(_("env var: {var}").format(var=var_str)) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default = False + show_default_is_str = False + + if self.show_default is not None: + if isinstance(self.show_default, str): + show_default_is_str = show_default = True + else: + show_default = self.show_default + elif ctx.show_default is not None: + show_default = ctx.show_default + + if show_default_is_str or (show_default and (default_value is not None)): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif inspect.isfunction(default_value): + default_string = _("(dynamic)") + elif self.is_bool_flag and self.secondary_opts: + # For boolean flags that have distinct True/False opts, + # use the opt without prefix instead of the value. + default_string = split_opt( + (self.opts if self.default else self.secondary_opts)[0] + )[1] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + else: + default_string = str(default_value) + + if default_string: + extra.append(_("default: {default}").format(default=default_string)) + + if ( + isinstance(self.type, types._NumberRangeBase) + # skip count with default range type + and not (self.count and self.type.min == 0 and self.type.max is None) + ): + range_str = self.type._describe_range() + + if range_str: + extra.append(range_str) + + if self.required: + extra.append(_("required")) + + if extra: + extra_str = "; ".join(extra) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + @t.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + # If we're a non boolean flag our default is more complex because + # we need to look at all flags in the same group to figure out + # if we're the default one in which case we return the flag + # value as default. + if self.is_flag and not self.is_bool_flag: + for param in ctx.command.params: + if param.name == self.name and param.default: + return t.cast(Option, param).flag_value + + return None + + return super().get_default(ctx, call=call) + + def prompt_for_value(self, ctx: Context) -> t.Any: + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + assert self.prompt is not None + + # Calculate the default before prompting anything to be stable. + default = self.get_default(ctx) + + # If this is a prompt for a flag we need to handle this + # differently. + if self.is_bool_flag: + return confirm(self.prompt, default) + + return prompt( + self.prompt, + default=default, + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + ) + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + return rv + + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is None: + return None + + value_depth = (self.nargs != 1) + bool(self.multiple) + + if value_depth > 0: + rv = self.type.split_envvar_value(rv) + + if self.multiple and self.nargs != 1: + rv = batch(rv, self.nargs) + + return rv + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, "Parameter"] + ) -> t.Tuple[t.Any, ParameterSource]: + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option can be + # given as a flag without a value. This is different from None + # to distinguish from the flag not being given at all. + if value is _flag_needs_value: + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + elif ( + self.multiple + and value is not None + and any(v is _flag_needs_value for v in value) + ): + value = [self.flag_value if v is _flag_needs_value else v for v in value] + source = ParameterSource.COMMANDLINE + + # The value wasn't set, or used the param's default, prompt if + # prompting is enabled. + elif ( + source in {None, ParameterSource.DEFAULT} + and self.prompt is not None + and (self.required or self.prompt_required) + and not ctx.resilient_parsing + ): + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + + return value, source + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the constructor of :class:`Parameter`. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: t.Sequence[str], + required: t.Optional[bool] = None, + **attrs: t.Any, + ) -> None: + if required is None: + if attrs.get("default") is not None: + required = False + else: + required = attrs.get("nargs", 1) > 0 + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + super().__init__(param_decls, required=required, **attrs) + + if __debug__: + if self.default is not None and self.nargs == -1: + raise TypeError("'default' is not supported for nargs=-1.") + + @property + def human_readable_name(self) -> str: + if self.metavar is not None: + return self.metavar + return self.name.upper() # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(self) + if not var: + var = self.name.upper() # type: ignore + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Could not determine name for argument") + if len(decls) == 1: + name = arg = decls[0] + name = name.replace("-", "_").lower() + else: + raise TypeError( + "Arguments take exactly one parameter declaration, got" + f" {len(decls)}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [self.make_metavar()] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar()}'" + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/decorators.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/decorators.py new file mode 100644 index 0000000..d9bba95 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/decorators.py @@ -0,0 +1,561 @@ +import inspect +import types +import typing as t +from functools import update_wrapper +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .globals import get_current_context +from .utils import echo + +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=t.Union[_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.Any, 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: t.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.Optional[T] + 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 # type: ignore[return-value] + + +def pass_meta_key( + key: str, *, doc_description: t.Optional[str] = None +) -> "t.Callable[[t.Callable[te.Concatenate[t.Any, 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.Any, 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 # type: ignore[return-value] + + +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: t.Optional[str], + cls: t.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: t.Type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: + ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def command( + name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Command]: + ... + + +def command( + name: t.Union[t.Optional[str], _AnyCallable] = None, + cls: t.Optional[t.Type[CmdType]] = None, + **attrs: t.Any, +) -> t.Union[Command, t.Callable[[_AnyCallable], t.Union[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 with + underscores replaced by dashes. If you want to change that, you can + pass the intended name as the first argument. + + All keyword arguments are forwarded to the underlying command class. + For the ``params`` argument, any decorated params are appended to + the end of the list. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: the name of the command. This defaults to the function + name with underscores replaced by dashes. + :param cls: the command class to instantiate. This defaults to + :class:`Command`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.1 + The ``params`` argument can be used. Decorated params are + appended to the end of the list. + """ + + func: t.Optional[t.Callable[[_AnyCallable], t.Any]] = None + + if callable(name): + func = name + name = None + assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." + assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." + + if cls is None: + cls = t.cast(t.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) + + cmd = cls( + name=name or f.__name__.lower().replace("_", "-"), + 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: t.Optional[str], + cls: t.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: t.Type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: + ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def group( + name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Group]: + ... + + +def group( + name: t.Union[str, _AnyCallable, None] = None, + cls: t.Optional[t.Type[GrpType]] = None, + **attrs: t.Any, +) -> t.Union[Group, t.Callable[[_AnyCallable], t.Union[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(t.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: t.Optional[t.Type[Argument]] = 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: t.Optional[t.Type[Option]] = 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: t.Optional[str] = None, + *param_decls: str, + package_name: t.Optional[str] = None, + prog_name: t.Optional[str] = None, + message: t.Optional[str] = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. On Python < 3.8, the ``importlib_metadata`` + backport must be installed. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. This will be used to detect the + version, so it must match the name of the installed package. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + f_back = frame.f_back if frame is not None else None + f_globals = f_back.f_globals if f_back is not None else None + # break reference cycle + # https://docs.python.org/3/library/inspect.html#the-interpreter-stack + del frame + + if f_globals is not None: + package_name = f_globals.get("__name__") + + if package_name == "__main__": + package_name = f_globals.get("__package__") + + if package_name: + package_name = package_name.partition(".")[0] + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + nonlocal prog_name + nonlocal version + + if prog_name is None: + prog_name = ctx.find_root().info_name + + if version is None and package_name is not None: + metadata: t.Optional[types.ModuleType] + + try: + from importlib import metadata # type: ignore + except ImportError: + # Python < 3.8 + import importlib_metadata as metadata # type: ignore + + try: + version = metadata.version(package_name) # type: ignore + except metadata.PackageNotFoundError: # type: ignore + raise RuntimeError( + f"{package_name!r} is not installed. Try passing" + " 'package_name' instead." + ) from None + + if version is None: + raise RuntimeError( + f"Could not determine the version for {package_name!r} automatically." + ) + + echo( + message % {"prog": prog_name, "package": package_name, "version": version}, + color=ctx.color, + ) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) + + +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--help`` option which immediately prints the help page + and exits the program. + + This is usually unnecessary, as the ``--help`` option is added to + each command automatically unless ``add_help_option=False`` is + passed. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + if not param_decls: + param_decls = ("--help",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/exceptions.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/exceptions.py new file mode 100644 index 0000000..fe68a36 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/exceptions.py @@ -0,0 +1,288 @@ +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +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: t.Optional[t.Union[t.Sequence[str], str]] +) -> t.Optional[str]: + if param_hint is not None and not isinstance(param_hint, str): + return " / ".join(repr(x) for x in param_hint) + + return param_hint + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception. + exit_code = 1 + + def __init__(self, message: str) -> None: + super().__init__(message) + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: + if file is None: + file = get_text_stderr() + + echo(_("Error: {message}").format(message=self.format_message()), file=file) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + + exit_code = 2 + + def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd: t.Optional["Command"] = self.ctx.command if self.ctx else None + + def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: + if file is None: + file = get_text_stderr() + color = None + hint = "" + if ( + self.ctx is not None + and self.ctx.command.get_help_option(self.ctx) is not None + ): + hint = _("Try '{command} {option}' for help.").format( + command=self.ctx.command_path, option=self.ctx.help_option_names[0] + ) + hint = f"{hint}\n" + if self.ctx is not None: + color = self.ctx.color + echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=color, + ) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + def __init__( + self, + message: str, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + ) -> None: + super().__init__(message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + return _("Invalid value: {message}").format(message=self.message) + + return _("Invalid value for {param_hint}: {message}").format( + param_hint=_join_param_hints(param_hint), message=self.message + ) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + def __init__( + self, + message: t.Optional[str] = None, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + param_type: t.Optional[str] = None, + ) -> None: + super().__init__(message or "", ctx, param, param_hint) + self.param_type = param_type + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint: t.Optional[str] = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + param_hint = None + + param_hint = _join_param_hints(param_hint) + param_hint = f" {param_hint}" if param_hint else "" + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message(self.param) + if msg_extra: + if msg: + msg += f". {msg_extra}" + else: + msg = msg_extra + + msg = f" {msg}" if msg else "" + + # Translate param_type for known types. + if param_type == "argument": + missing = _("Missing argument") + elif param_type == "option": + missing = _("Missing option") + elif param_type == "parameter": + missing = _("Missing parameter") + else: + missing = _("Missing {param_type}").format(param_type=param_type) + + return f"{missing}{param_hint}.{msg}" + + def __str__(self) -> str: + if not self.message: + param_name = self.param.name if self.param else None + return _("Missing parameter: {param_name}").format(param_name=param_name) + else: + return self.message + + +class NoSuchOption(UsageError): + """Raised if click attempted to handle an option that does not + exist. + + .. versionadded:: 4.0 + """ + + def __init__( + self, + option_name: str, + message: t.Optional[str] = None, + possibilities: t.Optional[t.Sequence[str]] = None, + ctx: t.Optional["Context"] = None, + ) -> None: + if message is None: + message = _("No such option: {name}").format(name=option_name) + + super().__init__(message, ctx) + self.option_name = option_name + self.possibilities = possibilities + + def format_message(self) -> str: + if not self.possibilities: + return self.message + + possibility_str = ", ".join(sorted(self.possibilities)) + suggest = ngettext( + "Did you mean {possibility}?", + "(Possible options: {possibilities})", + len(self.possibilities), + ).format(possibility=possibility_str, possibilities=possibility_str) + return f"{self.message} {suggest}" + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + def __init__( + self, option_name: str, message: str, ctx: t.Optional["Context"] = None + ) -> None: + super().__init__(message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: t.Optional[str] = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename: 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/flask_auth_app/authenv/lib/python3.10/site-packages/click/formatting.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/formatting.py new file mode 100644 index 0000000..ddd2a2f --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/formatting.py @@ -0,0 +1,301 @@ +import typing as t +from contextlib import contextmanager +from gettext import gettext as _ + +from ._compat import term_len +from .parser import split_opt + +# Can force a width. This is used by the test system +FORCED_WIDTH: t.Optional[int] = None + + +def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]: + widths: t.Dict[int, int] = {} + + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows( + rows: t.Iterable[t.Tuple[str, str]], col_count: int +) -> t.Iterator[t.Tuple[str, ...]]: + for row in rows: + yield row + ("",) * (col_count - len(row)) + + +def wrap_text( + text: str, + width: int = 78, + initial_indent: str = "", + subsequent_indent: str = "", + preserve_paragraphs: bool = False, +) -> str: + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + """ + from ._textwrap import TextWrapper + + text = text.expandtabs() + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) + if not preserve_paragraphs: + return wrapper.fill(text) + + p: t.List[t.Tuple[int, bool, str]] = [] + buf: t.List[str] = [] + indent = None + + def _flush_par() -> None: + if not buf: + return + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) + else: + p.append((indent or 0, False, " ".join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(" " * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return "\n\n".join(rv) + + +class HelpFormatter: + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + def __init__( + self, + indent_increment: int = 2, + width: t.Optional[int] = None, + max_width: t.Optional[int] = None, + ) -> None: + import shutil + + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + width = FORCED_WIDTH + if width is None: + width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) + self.width = width + self.current_indent = 0 + self.buffer: t.List[str] = [] + + def write(self, string: str) -> None: + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self) -> None: + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self) -> None: + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage( + self, prog: str, args: str = "", prefix: t.Optional[str] = None + ) -> None: + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: The prefix for the first line. Defaults to + ``"Usage: "``. + """ + if prefix is None: + prefix = f"{_('Usage:')} " + + usage_prefix = f"{prefix:>{self.current_indent}}{prog} " + text_width = self.width - self.current_indent + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) + + self.write("\n") + + def write_heading(self, heading: str) -> None: + """Writes a heading into the buffer.""" + self.write(f"{'':>{self.current_indent}}{heading}:\n") + + def write_paragraph(self) -> None: + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write("\n") + + def write_text(self, text: str) -> None: + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + indent = " " * self.current_indent + self.write( + wrap_text( + text, + self.width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") + + def write_dl( + self, + rows: t.Sequence[t.Tuple[str, str]], + col_max: int = 30, + col_spacing: int = 2, + ) -> None: + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError("Expected two columns for definition list") + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write(f"{'':>{self.current_indent}}{first}") + if not second: + self.write("\n") + continue + if term_len(first) <= first_col - col_spacing: + self.write(" " * (first_col - term_len(first))) + else: + self.write("\n") + self.write(" " * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) + lines = wrapped_text.splitlines() + + if lines: + self.write(f"{lines[0]}\n") + + for line in lines[1:]: + self.write(f"{'':>{first_col + self.current_indent}}{line}\n") + else: + self.write("\n") + + @contextmanager + def section(self, name: str) -> t.Iterator[None]: + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self) -> t.Iterator[None]: + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self) -> str: + """Returns the buffer contents.""" + return "".join(self.buffer) + + +def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/globals.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/globals.py new file mode 100644 index 0000000..480058f --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/globals.py @@ -0,0 +1,68 @@ +import typing as t +from threading import local + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: "te.Literal[False]" = False) -> "Context": + ... + + +@t.overload +def get_current_context(silent: bool = ...) -> t.Optional["Context"]: + ... + + +def get_current_context(silent: bool = False) -> t.Optional["Context"]: + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: if set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return t.cast("Context", _local.stack[-1]) + except (AttributeError, IndexError) as e: + if not silent: + raise RuntimeError("There is no active click context.") from e + + return None + + +def push_context(ctx: "Context") -> None: + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault("stack", []).append(ctx) + + +def pop_context() -> None: + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color: t.Optional[bool] = None) -> t.Optional[bool]: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/parser.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/parser.py new file mode 100644 index 0000000..5fa7adf --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/parser.py @@ -0,0 +1,529 @@ +""" +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright 2001-2006 Gregory P. Ward. All rights reserved. +Copyright 2002-2006 Python Software Foundation. All rights reserved. +""" +# This code uses parts of optparse written by Gregory P. Ward and +# maintained by the Python Software Foundation. +# Copyright 2001-2006 Gregory P. Ward +# Copyright 2002-2006 Python Software Foundation +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Argument as CoreArgument + from .core import Context + from .core import Option as CoreOption + from .core import Parameter as CoreParameter + +V = t.TypeVar("V") + +# Sentinel value that indicates an option was passed as a flag without a +# value but is not a flag option. Option.consume_value uses this to +# prompt or use the flag_value. +_flag_needs_value = object() + + +def _unpack_args( + args: t.Sequence[str], nargs_spec: t.Sequence[int] +) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]: + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with `None`. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] + spos: t.Optional[int] = None + + def _fetch(c: "te.Deque[V]") -> t.Optional[V]: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return None + + while nargs_spec: + nargs = _fetch(nargs_spec) + + if nargs is None: + continue + + if nargs == 1: + rv.append(_fetch(args)) + elif nargs > 1: + x = [_fetch(args) for _ in range(nargs)] + + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError("Cannot have two nargs < 0") + + spos = len(rv) + rv.append(None) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1 :] = reversed(rv[spos + 1 :]) + + return tuple(rv), list(args) + + +def split_opt(opt: str) -> t.Tuple[str, str]: + first = opt[:1] + if first.isalnum(): + return "", opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str: + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = split_opt(opt) + return f"{prefix}{ctx.token_normalize_func(opt)}" + + +def split_arg_string(string: str) -> t.List[str]: + """Split an argument string as with :func:`shlex.split`, but don't + fail if the string is incomplete. Ignores a missing closing quote or + incomplete escape sequence and uses the partial token as-is. + + .. code-block:: python + + split_arg_string("example 'my file") + ["example", "my file"] + + split_arg_string("example my\\") + ["example", "my"] + + :param string: String to split. + """ + import shlex + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = "" + out = [] + + try: + for token in lex: + out.append(token) + except ValueError: + # Raised when end-of-string is reached in an invalid state. Use + # the partial token as-is. The quote or escape character is in + # lex.state, not lex.token. + out.append(lex.token) + + return out + + +class Option: + def __init__( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes: t.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: t.Optional[str], nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], + state: "ParsingState", + ) -> None: + if self.nargs > 1: + assert value is not None + holes = sum(1 for x in value if x is None) + if holes == len(value): + value = None + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + if self.nargs == -1 and self.obj.envvar is not None and value == (): + # Replace empty tuple with None so that a value from the + # environment may be tried. + value = None + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class ParsingState: + def __init__(self, rargs: t.List[str]) -> None: + self.opts: t.Dict[str, t.Any] = {} + self.largs: t.List[str] = [] + self.rargs = rargs + self.order: t.List["CoreParameter"] = [] + + +class OptionParser: + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + """ + + def __init__(self, ctx: t.Optional["Context"] = None) -> None: + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args: 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: t.Dict[str, Option] = {} + self._long_opt: t.Dict[str, Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: t.List[Argument] = [] + + def add_option( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ) -> None: + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``append_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + opts = [normalize_opt(opt, self.ctx) for opt in opts] + option = Option(obj, opts, dest, action=action, nargs=nargs, const=const) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument( + self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1 + ) -> None: + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + self._args.append(Argument(obj, dest=dest, nargs=nargs)) + + def parse_args( + self, args: t.List[str] + ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]: + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state: ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state: ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt( + self, opt: str, explicit_value: t.Optional[str], state: ParsingState + ) -> None: + if opt not in self._long_opt: + from difflib import get_close_matches + + possibilities = get_close_matches(opt, self._long_opt) + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + value = self._get_value_from_state(opt, option, state) + + elif explicit_value is not None: + raise BadOptionUsage( + opt, _("Option {name!r} does not take a value.").format(name=opt) + ) + + else: + value = None + + option.process(value, state) + + def _match_short_opt(self, arg: str, state: ParsingState) -> None: + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = normalize_opt(f"{prefix}{ch}", self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + value = self._get_value_from_state(opt, option, state) + + else: + value = None + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we 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 + ) -> t.Any: + nargs = option.nargs + + if len(state.rargs) < nargs: + if option.obj._flag_needs_value: + # Option allows omitting the value. + value = _flag_needs_value + else: + raise BadOptionUsage( + option_name, + ngettext( + "Option {name!r} requires an argument.", + "Option {name!r} requires {nargs} arguments.", + nargs, + ).format(name=option_name, nargs=nargs), + ) + elif nargs == 1: + next_rarg = state.rargs[0] + + if ( + option.obj._flag_needs_value + and isinstance(next_rarg, str) + and next_rarg[:1] in self._opt_prefixes + and len(next_rarg) > 1 + ): + # The next arg looks like the start of an option, don't + # use it as the value if omitting the value is allowed. + value = _flag_needs_value + else: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + return value + + def _process_opts(self, arg: str, state: ParsingState) -> None: + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) + else: + long_opt = arg + norm_long_opt = normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + self._match_short_opt(arg, state) + return + + if not self.ignore_unknown_options: + raise + + state.largs.append(arg) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/py.typed b/flask_auth_app/authenv/lib/python3.10/site-packages/click/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/shell_completion.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/shell_completion.py new file mode 100644 index 0000000..dc9e00b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/shell_completion.py @@ -0,0 +1,596 @@ +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import BaseCommand +from .core import Context +from .core import MultiCommand +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .parser import split_arg_string +from .utils import echo + + +def shell_complete( + cli: BaseCommand, + ctx_args: t.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: t.Optional[str] = None, + **kwargs: t.Any, + ) -> None: + self.value: t.Any = value + self.type: str = type + self.help: t.Optional[str] = help + self._info = kwargs + + def __getattr__(self, name: str) -> t.Any: + return self._info.get(name) + + +# Only Bash >= 4.4 has the nosort option. +_SOURCE_BASH = """\ +%(complete_func)s() { + local IFS=$'\\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ +%(complete_var)s=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMPREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMPREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +%(complete_func)s_setup() { + complete -o nosort -F %(complete_func)s %(prog_name)s +} + +%(complete_func)s_setup; +""" + +_SOURCE_ZSH = """\ +#compdef %(prog_name)s + +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + (( ! $+commands[%(prog_name)s] )) && return 1 + + response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ +%(complete_var)s=zsh_complete %(prog_name)s)}") + + for type key descr in ${response}; do + if [[ "$type" == "plain" ]]; then + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + elif [[ "$type" == "dir" ]]; then + _path_files -/ + elif [[ "$type" == "file" ]]; then + _path_files -f + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -a completions + fi +} + +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: BaseCommand, + ctx_args: t.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) -> t.Dict[str, t.Any]: + """Vars for formatting :attr:`source_template`. + + By default this provides ``complete_func``, ``complete_var``, + and ``prog_name``. + """ + return { + "complete_func": self.func_name, + "complete_var": self.complete_var, + "prog_name": self.prog_name, + } + + def source(self) -> str: + """Produce the shell script that defines the completion + function. By default this ``%``-style formats + :attr:`source_template` with the dict returned by + :meth:`source_vars`. + """ + return self.source_template % self.source_vars() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + """Use the env vars defined by the shell script to return a + tuple of ``args, incomplete``. This must be implemented by + subclasses. + """ + raise NotImplementedError + + def get_completions( + self, args: t.List[str], incomplete: str + ) -> t.List[CompletionItem]: + """Determine the context and last complete command or parameter + from the complete args. Call that object's ``shell_complete`` + method to get the completions for the incomplete value. + + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) + obj, incomplete = _resolve_incomplete(ctx, args, incomplete) + return obj.shell_complete(ctx, incomplete) + + def format_completion(self, item: CompletionItem) -> str: + """Format a completion item into the form recognized by the + shell script. This must be implemented by subclasses. + + :param item: Completion item to format. + """ + raise NotImplementedError + + def complete(self) -> str: + """Produce the completion data to send back to the shell. + + By default this calls :meth:`get_completion_args`, gets the + completions, then calls :meth:`format_completion` for each + completion. + """ + args, incomplete = self.get_completion_args() + completions = self.get_completions(args, incomplete) + out = [self.format_completion(item) for item in completions] + return "\n".join(out) + + +class BashComplete(ShellComplete): + """Shell completion for Bash.""" + + name = "bash" + source_template = _SOURCE_BASH + + @staticmethod + def _check_version() -> None: + import subprocess + + output = subprocess.run( + ["bash", "-c", 'echo "${BASH_VERSION}"'], stdout=subprocess.PIPE + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) + + if match is not None: + major, minor = match.groups() + + if major < "4" or major == "4" and minor < "4": + 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) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type},{item.value}" + + +class ZshComplete(ShellComplete): + """Shell completion for Zsh.""" + + name = "zsh" + source_template = _SOURCE_ZSH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name = "fish" + source_template = _SOURCE_FISH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + args = cwords[1:] + + # Fish stores the partial word in both COMP_WORDS and + # COMP_CWORD, remove it from complete args. + if incomplete and args and args[-1] == incomplete: + args.pop() + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + if item.help: + return f"{item.type},{item.value}\t{item.help}" + + return f"{item.type},{item.value}" + + +ShellCompleteType = t.TypeVar("ShellCompleteType", bound=t.Type[ShellComplete]) + + +_available_shells: t.Dict[str, t.Type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: ShellCompleteType, name: t.Optional[str] = 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) -> t.Optional[t.Type[ShellComplete]]: + """Look up a registered :class:`ShellComplete` subclass by the name + provided by the completion instruction environment variable. If the + name isn't registered, returns ``None``. + + :param shell: Name the class is registered under. + """ + return _available_shells.get(shell) + + +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: + """Determine if the given parameter is an argument that can still + accept values. + + :param ctx: Invocation context for the command represented by the + parsed complete args. + :param param: Argument object being checked. + """ + if not isinstance(param, Argument): + return False + + assert param.name is not None + # 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: t.List[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag or param.count: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(ctx, arg): + last_option = arg + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: BaseCommand, + ctx_args: t.MutableMapping[str, t.Any], + prog_name: str, + args: t.List[str], +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + ctx = cli.make_context(prog_name, args.copy(), **ctx_args) + args = ctx.protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, MultiCommand): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) + args = ctx.protected_args + ctx.args + else: + sub_ctx = ctx + + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + sub_ctx = cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx.protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: t.List[str], incomplete: str +) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(ctx, incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(ctx, incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(ctx, args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/termui.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/termui.py new file mode 100644 index 0000000..db7a4b2 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/termui.py @@ -0,0 +1,784 @@ +import inspect +import io +import itertools +import sys +import typing as t +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 = False, + default: t.Optional[t.Any] = None, + show_choices: bool = True, + type: t.Optional[ParamType] = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + if default is not None and show_default: + prompt = f"{prompt} [{_format_default(default)}]" + return f"{prompt}{suffix}" + + +def _format_default(default: t.Any) -> t.Any: + if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): + return default.name + + return default + + +def prompt( + text: str, + default: t.Optional[t.Any] = None, + hide_input: bool = False, + confirmation_prompt: t.Union[bool, str] = False, + type: t.Optional[t.Union[ParamType, t.Any]] = None, + value_proc: t.Optional[t.Callable[[str], t.Any]] = None, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, + show_choices: bool = True, +) -> t.Any: + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending an interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: Prompt a second time to confirm the + value. Can be set to a string instead of ``True`` to customize + the message. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + + .. versionadded:: 8.0 + ``confirmation_prompt`` can be a custom string. + + .. versionadded:: 7.0 + Added the ``show_choices`` parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + """ + + def prompt_func(text: str) -> str: + f = hidden_prompt_func if hide_input else visible_prompt_func + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(text.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + return f(" ") + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() from None + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) + + if confirmation_prompt: + if confirmation_prompt is True: + confirmation_prompt = _("Repeat for confirmation") + + confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) + + while True: + while True: + value = prompt_func(prompt) + if value: + break + elif default is not None: + value = default + break + try: + result = value_proc(value) + except UsageError as e: + if hide_input: + echo(_("Error: The value you entered was invalid."), err=err) + else: + echo(_("Error: {e.message}").format(e=e), err=err) # noqa: B306 + continue + if not confirmation_prompt: + return result + while True: + value2 = prompt_func(confirmation_prompt) + is_empty = not value and not value2 + if value2 or is_empty: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: t.Optional[bool] = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + value = visible_prompt_func(" ").lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def echo_via_pager( + text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], + color: t.Optional[bool] = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast(t.Iterable[str], text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + from ._termui_impl import pager + + return pager(itertools.chain(text_generator, "\n"), color) + + +def progressbar( + iterable: t.Optional[t.Iterable[V]] = None, + length: t.Optional[int] = None, + label: t.Optional[str] = None, + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, +) -> "ProgressBar[V]": + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + Added the ``update_min_steps`` parameter. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. Added the ``update`` method to + the object. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + + # 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: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0 +) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +def style( + text: t.Any, + fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bold: t.Optional[bool] = None, + dim: t.Optional[bool] = None, + underline: t.Optional[bool] = None, + overline: t.Optional[bool] = None, + italic: t.Optional[bool] = None, + blink: t.Optional[bool] = None, + reverse: t.Optional[bool] = None, + strikethrough: t.Optional[bool] = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise TypeError(f"Unknown color {fg!r}") from None + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise TypeError(f"Unknown color {bg!r}") from None + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO[t.AnyStr]] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, + **styles: t.Any, +) -> None: + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + Non-string types will be converted to :class:`str`. However, + :class:`bytes` are passed directly to :meth:`echo` without applying + style. If you want to style bytes that represent text, call + :meth:`bytes.decode` first. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. Bytes are + passed through without style applied. + + .. versionadded:: 2.0 + """ + if message is not None and not isinstance(message, (bytes, bytearray)): + message = style(message, **styles) + + return echo(message, file=file, nl=nl, err=err, color=color) + + +def edit( + text: t.Optional[t.AnyStr] = None, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + filename: t.Optional[str] = None, +) -> t.Optional[t.AnyStr]: + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. + """ + from ._termui_impl import Editor + + ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) + + if filename is None: + return ed.edit(text) + + ed.edit_file(filename) + return None + + +def launch(url: str, wait: bool = False, locate: bool = False) -> int: + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: Wait for the program to exit before returning. This + only works if the launched program blocks. In particular, + ``xdg-open`` on Linux does not block. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar: t.Optional[t.Callable[[bool], str]] = None + + +def getchar(echo: bool = False) -> str: + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + global _getchar + + if _getchar is None: + from ._termui_impl import getchar as f + + _getchar = f + + return _getchar(echo) + + +def raw_terminal() -> t.ContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: t.Optional[str] = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/testing.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/testing.py new file mode 100644 index 0000000..e0df0d2 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/testing.py @@ -0,0 +1,479 @@ +import contextlib +import io +import os +import shlex +import shutil +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from .core import BaseCommand + + +class EchoingStdin: + def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: + self._input = input + self._output = output + self._paused = False + + def __getattr__(self, x: str) -> t.Any: + return getattr(self._input, x) + + def _echo(self, rv: bytes) -> bytes: + if not self._paused: + self._output.write(rv) + + return rv + + def read(self, n: int = -1) -> bytes: + return self._echo(self._input.read(n)) + + def read1(self, n: int = -1) -> bytes: + return self._echo(self._input.read1(n)) # type: ignore + + def readline(self, n: int = -1) -> bytes: + return self._echo(self._input.readline(n)) + + def readlines(self) -> t.List[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> t.Iterator[bytes]: + return iter(self._echo(x) for x in self._input) + + def __repr__(self) -> str: + return repr(self._input) + + +@contextlib.contextmanager +def _pause_echo(stream: t.Optional[EchoingStdin]) -> t.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class _NamedTextIOWrapper(io.TextIOWrapper): + def __init__( + self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + +def make_input_stream( + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]], 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.""" + + def __init__( + self, + runner: "CliRunner", + stdout_bytes: bytes, + stderr_bytes: t.Optional[bytes], + return_value: t.Any, + exit_code: int, + exception: t.Optional[BaseException], + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ] = None, + ): + #: The runner that created the result + self.runner = runner + #: The standard output as bytes. + self.stdout_bytes = stdout_bytes + #: The standard error as bytes, or None if not available + self.stderr_bytes = stderr_bytes + #: The value returned from the invoked command. + #: + #: .. versionadded:: 8.0 + self.return_value = return_value + #: The exit code as integer. + self.exit_code = exit_code + #: The exception that happened if one did. + self.exception = exception + #: The traceback + self.exc_info = exc_info + + @property + def output(self) -> str: + """The (standard) output as unicode string.""" + return self.stdout + + @property + def stdout(self) -> str: + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stderr(self) -> str: + """The standard error as unicode string.""" + if self.stderr_bytes is None: + raise ValueError("stderr not separately captured") + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + def __repr__(self) -> str: + exc_str = repr(self.exception) if self.exception else "okay" + return f"<{type(self).__name__} {exc_str}>" + + +class CliRunner: + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from stdin writes + to stdout. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param mix_stderr: if this is set to `False`, then stdout and stderr are + preserved as independent streams. This is useful for + Unix-philosophy apps that have predictable stdout and + noisy stderr, such that each may be measured + independently + """ + + def __init__( + self, + charset: str = "utf-8", + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + echo_stdin: bool = False, + mix_stderr: bool = True, + ) -> None: + self.charset = charset + self.env: t.Mapping[str, t.Optional[str]] = env or {} + self.echo_stdin = echo_stdin + self.mix_stderr = mix_stderr + + def get_default_prog_name(self, cli: "BaseCommand") -> str: + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or "root" + + def make_env( + self, overrides: t.Optional[t.Mapping[str, t.Optional[str]]] = None + ) -> t.Mapping[str, t.Optional[str]]: + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation( + self, + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + color: bool = False, + ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up stdin with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + :param input: the input stream to put into sys.stdin. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + ``stderr`` is opened with ``errors="backslashreplace"`` + instead of the default ``"strict"``. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + """ + bytes_input = make_input_stream(input, self.charset) + echo_input = None + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + bytes_output = io.BytesIO() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, bytes_output) + ) + + sys.stdin = text_input = _NamedTextIOWrapper( + bytes_input, encoding=self.charset, name="", mode="r" + ) + + if self.echo_stdin: + # Force unbuffered reads, otherwise TextIOWrapper reads a + # large chunk which is echoed early. + text_input._CHUNK_SIZE = 1 # type: ignore + + sys.stdout = _NamedTextIOWrapper( + bytes_output, encoding=self.charset, name="", mode="w" + ) + + bytes_error = None + if self.mix_stderr: + sys.stderr = sys.stdout + else: + bytes_error = io.BytesIO() + sys.stderr = _NamedTextIOWrapper( + bytes_error, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(prompt or "") + val = text_input.readline().rstrip("\r\n") + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + return text_input.readline().rstrip("\r\n") + + @_pause_echo(echo_input) # type: ignore + def _getchar(echo: bool) -> str: + char = sys.stdin.read(1) + + if echo: + sys.stdout.write(char) + + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi( + stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None + ) -> bool: + if color is None: + return not default_color + return not color + + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi # type: ignore + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + + old_env = {} + try: + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (bytes_output, bytes_error) + finally: + for key, value in old_env.items(): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi # type: ignore + formatting.FORCED_WIDTH = old_forced_width + + def invoke( + self, + cli: "BaseCommand", + args: t.Optional[t.Union[str, t.Sequence[str]]] = None, + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + catch_exceptions: bool = True, + color: bool = False, + **extra: t.Any, + ) -> Result: + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + The result object has the ``return_value`` attribute with + the value returned from the invoked command. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionchanged:: 3.0 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 3.0 + The result object has the ``exc_info`` attribute with the + traceback if available. + """ + exc_info = None + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: t.Optional[BaseException] = None + exit_code = 0 + + if isinstance(args, str): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + return_value = cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + e_code = t.cast(t.Optional[t.Union[int, t.Any]], e.code) + + if e_code is None: + e_code = 0 + + if e_code != 0: + exception = e + + if not isinstance(e_code, int): + sys.stdout.write(str(e_code)) + sys.stdout.write("\n") + e_code = 1 + + exit_code = e_code + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + stdout = outstreams[0].getvalue() + if self.mix_stderr: + stderr = None + else: + stderr = outstreams[1].getvalue() # type: ignore + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: t.Optional[t.Union[str, "os.PathLike[str]"]] = None + ) -> t.Iterator[str]: + """A context manager that creates a temporary directory and + changes the current working directory to it. This isolates tests + that affect the contents of the CWD to prevent them from + interfering with each other. + + :param temp_dir: Create the temporary directory under this + directory. If given, the created directory is not removed + when exiting. + + .. versionchanged:: 8.0 + Added the ``temp_dir`` parameter. + """ + cwd = os.getcwd() + dt = tempfile.mkdtemp(dir=temp_dir) + os.chdir(dt) + + try: + yield dt + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(dt) + except OSError: # noqa: B014 + pass diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/types.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/types.py new file mode 100644 index 0000000..2b1d179 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/types.py @@ -0,0 +1,1089 @@ +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 + + +class ParamType: + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + """ + + is_composite: t.ClassVar[bool] = False + arity: t.ClassVar[int] = 1 + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[t.Optional[str]] = None + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + + # Custom subclasses might not remember to set a name. + if hasattr(self, "name"): + name = self.name + else: + name = param_type + + return {"param_type": param_type, "name": name} + + def __call__( + self, + value: t.Any, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: "Parameter") -> t.Optional[str]: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: "Parameter") -> t.Optional[str]: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + return value + + def split_envvar_value(self, rv: str) -> t.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> "t.NoReturn": + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self) -> int: # type: ignore + raise NotImplementedError() + + +class FuncParamType(ParamType): + def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: + self.name: str = func.__name__ + self.func = func + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["func"] = self.func + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self.func(value) + except ValueError: + try: + value = str(value) + except UnicodeError: + value = value.decode("utf-8", "replace") + + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = 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): + """The choice type allows a value to be checked against a fixed set + of supported values. All of these values have to be strings. + + You should only pass a list or tuple of choices. Other iterables + (like generators) may lead to surprising results. + + The resulting value will always be one of the originally passed choices + regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` + being specified. + + See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + """ + + name = "choice" + + def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: + self.choices = choices + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["choices"] = self.choices + info_dict["case_sensitive"] = self.case_sensitive + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + choices_str = "|".join(self.choices) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: "Parameter") -> str: + return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + # Match through normalization and case sensitivity + # first do token_normalize_func, then lowercase + # preserve original `value` to produce an accurate message in + # `self.fail` + normed_value = value + normed_choices = {choice: choice for choice in self.choices} + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(value) + normed_choices = { + ctx.token_normalize_func(normed_choice): original + for normed_choice, original in normed_choices.items() + } + + if not self.case_sensitive: + normed_value = normed_value.casefold() + normed_choices = { + normed_choice.casefold(): original + for normed_choice, original in normed_choices.items() + } + + if normed_value in normed_choices: + return normed_choices[normed_value] + + choices_str = ", ".join(map(repr, self.choices)) + self.fail( + ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = map(str, self.choices) + + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + def __init__(self, formats: t.Optional[t.Sequence[str]] = None): + self.formats: t.Sequence[str] = formats or [ + "%Y-%m-%d", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%d %H:%M:%S", + ] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase(ParamType): + _number_class: t.ClassVar[t.Type[t.Any]] + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +class _NumberRangeBase(_NumberParamTypeBase): + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + min=self.min, + max=self.max, + min_open=self.min_open, + max_open=self.max_open, + clamp=self.clamp, + ) + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import operator + + rv = super().convert(value, param, ctx) + lt_min: bool = self.min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, self.min) + gt_max: bool = self.max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, self.max) + + if self.clamp: + if lt_min: + return self._clamp(self.min, 1, self.min_open) # type: ignore + + if gt_max: + return self._clamp(self.max, -1, self.max_open) # type: ignore + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + raise NotImplementedError + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase, IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp( # type: ignore + self, bound: int, dir: "te.Literal[1, -1]", open: bool + ) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase, FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + if not open: + return bound + + # Could use Python 3.9's math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType): + name = "boolean" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if value in {False, True}: + return bool(value) + + norm = value.strip().lower() + + if norm in {"1", "true", "t", "yes", "y", "on"}: + return True + + if norm in {"0", "false", "f", "no", "n", "off"}: + return False + + self.fail( + _("{value!r} is not a valid boolean.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import uuid + + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Starting with Click 2.0, files can also be opened atomically in which + case all writes go into a separate file in the same folder and upon + completion the file will be moved over to the original location. This + is useful if a file regularly read by other users is modified. + + See :ref:`file-args` for more information. + """ + + name = "filename" + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: t.Optional[bool] = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(mode=self.mode, encoding=self.encoding) + return info_dict + + def resolve_lazy_flag(self, value: "t.Union[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: t.Union[str, "os.PathLike[str]", t.IO[t.Any]], + param: t.Optional["Parameter"], + ctx: t.Optional["Context"], + ) -> t.IO[t.Any]: + if _is_file_like(value): + return value + + value = t.cast("t.Union[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: # noqa: B014 + self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +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: t.Optional[t.Type[t.Any]] = 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) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + exists=self.exists, + file_okay=self.file_okay, + dir_okay=self.dir_okay, + writable=self.writable, + readable=self.readable, + allow_dash=self.allow_dash, + ) + return info_dict + + def coerce_path_result( + self, value: "t.Union[str, os.PathLike[str]]" + ) -> "t.Union[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: "t.Union[str, os.PathLike[str]]", + param: t.Optional["Parameter"], + ctx: t.Optional["Context"], + ) -> "t.Union[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: + # os.path.realpath doesn't resolve symlinks on Windows + # until Python 3.8. Use pathlib for now. + import pathlib + + rv = os.fsdecode(pathlib.Path(rv).resolve()) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=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}' 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 + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide path completions for only + directories or any paths. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + type = "dir" if self.dir_okay and not self.file_okay else "file" + return [CompletionItem(incomplete, type=type)] + + +class Tuple(CompositeParamType): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types: t.Sequence[t.Union[t.Type[t.Any], ParamType]]) -> None: + self.types: t.Sequence[ParamType] = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["types"] = [t.to_info_dict() for t in self.types] + return info_dict + + @property + def name(self) -> str: # type: ignore + return f"<{' '.join(ty.name for ty in self.types)}>" + + @property + def arity(self) -> int: # type: ignore + return len(self.types) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + len_type = len(self.types) + len_value = len(value) + + if len_value != len_type: + self.fail( + ngettext( + "{len_type} values are required, but {len_value} was given.", + "{len_type} values are required, but {len_value} were given.", + len_value, + ).format(len_type=len_type, len_value=len_value), + param=param, + ctx=ctx, + ) + + return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) + + +def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> ParamType: + """Find the most appropriate :class:`ParamType` for the given Python + type. If the type isn't provided, it can be inferred from a default + value. + """ + guessed_type = False + + if ty is None and default is not None: + if isinstance(default, (tuple, list)): + # If the default is empty, ty will remain None and will + # return STRING. + if default: + item = default[0] + + # A tuple of tuples needs to detect the inner types. + # Can't call convert recursively because that would + # incorrectly unwind the tuple to a single type. + if isinstance(item, (tuple, list)): + ty = tuple(map(type, item)) + else: + ty = type(item) + else: + ty = type(default) + + guessed_type = True + + if isinstance(ty, tuple): + return Tuple(ty) + + if isinstance(ty, ParamType): + return ty + + if ty is str or ty is None: + return STRING + + if ty is int: + return INT + + if ty is float: + return FLOAT + + if ty is bool: + return BOOL + + if guessed_type: + return STRING + + if __debug__: + try: + if issubclass(ty, ParamType): + raise AssertionError( + f"Attempted to use an uninstantiated parameter type ({ty})." + ) + except TypeError: + # ty is an instance (correct), so issubclass fails. + pass + + return FuncParamType(ty) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but +#: internally no string conversion takes place if the input was bytes. +#: This is usually useful when working with file paths as they can +#: appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL = BoolParamType() + +#: A UUID parameter. +UUID = UUIDParameterType() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/click/utils.py b/flask_auth_app/authenv/lib/python3.10/site-packages/click/utils.py new file mode 100644 index 0000000..d536434 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/click/utils.py @@ -0,0 +1,624 @@ +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, t.Optional[R]]": + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> t.Optional[R]: + 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.""" + # 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: t.Union[str, "os.PathLike[str]"], + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "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.Optional[t.IO[t.Any]] + 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: # noqa: E402 + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) from e + self._f = rv + return rv + + def close(self) -> None: + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self) -> None: + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self) -> "LazyFile": + return self + + def __exit__( + self, + exc_type: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + self.close_intelligently() + + def __iter__(self) -> t.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO[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: t.Optional[t.Type[BaseException]], + exc_value: t.Optional[BaseException], + tb: t.Optional[TracebackType], + ) -> None: + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> t.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO[t.Any]] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, +) -> None: + """Print a message and newline to stdout or a file. This should be + used instead of :func:`print` because it provides better support + for different data, files, and environments. + + Compared to :func:`print`, this does the following: + + - Ensures that the output encoding is not misconfigured on Linux. + - Supports Unicode in the Windows console. + - Supports writing to binary outputs, and supports writing bytes + to text outputs. + - Supports colors and styles on Windows. + - Removes ANSI color and style codes if the output does not look + like an interactive terminal. + - Always flushes the output. + + :param message: The string or bytes to output. Other objects are + converted to strings. + :param file: The file to write to. Defaults to ``stdout``. + :param err: Write to ``stderr`` instead of ``stdout``. + :param nl: Print a newline after the message. Enabled by default. + :param color: Force showing or hiding colors and other styles. By + default Click will remove color if the output does not look like + an interactive terminal. + + .. versionchanged:: 6.0 + Support Unicode output on the Windows console. Click does not + modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` + will still not support Unicode. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionadded:: 3.0 + Added the ``err`` parameter. + + .. versionchanged:: 2.0 + Support colors on Windows if colorama is installed. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # 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: t.Optional[t.Union[str, bytes]] = str(message) + else: + out = message + + if nl: + out = out or "" + if isinstance(out, str): + out += "\n" + else: + out += b"\n" + + if not out: + file.flush() + return + + # If there is a message and the value looks like bytes, we manually + # need to find the binary stream and write the message in there. + # This is done separately so that most stream types will work as you + # would expect. Eg: you can write to StringIO for other cases. + if isinstance(out, (bytes, bytearray)): + binary_file = _find_binary_writer(file) + + if binary_file is not None: + file.flush() + binary_file.write(out) + binary_file.flush() + return + + # ANSI style code support. For no message or bytes, nothing happens. + # When outputting to a file instead of a terminal, strip codes. + else: + color = resolve_color_default(color) + + if should_strip_ansi(file, color): + out = strip_ansi(out) + elif WIN: + if auto_wrap_for_ansi is not None: + file = auto_wrap_for_ansi(file) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: "te.Literal['stdin', 'stdout', 'stderr']") -> t.BinaryIO: + """Returns a system stream for byte processing. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener() + + +def get_text_stream( + name: "te.Literal['stdin', 'stdout', 'stderr']", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", +) -> t.TextIO: + """Returns a system stream for text processing. This usually returns + a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts for already + correctly configured streams. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener(encoding, errors) + + +def open_file( + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO[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 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: "t.Union[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: t.Optional[str] = None, _main: t.Optional[ModuleType] = None +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if _main is None: + _main = sys.modules["__main__"] + + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + # 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: t.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> t.List[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This is intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionchanged:: 8.1 + Invalid glob patterns are treated as empty expansions rather + than raising an error. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + try: + matches = glob(arg, recursive=glob_recursive) + except re.error: + matches = [] + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/distutils-precedence.pth b/flask_auth_app/authenv/lib/python3.10/site-packages/distutils-precedence.pth new file mode 100644 index 0000000..6de4198 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/distutils-precedence.pth @@ -0,0 +1 @@ +import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'stdlib') == 'local'; enabled and __import__('_distutils_hack').add_shim(); diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/__init__.py new file mode 100644 index 0000000..7f4c631 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/__init__.py @@ -0,0 +1,49 @@ +from typing import Any, Optional + +from .main import (dotenv_values, find_dotenv, get_key, load_dotenv, set_key, + unset_key) + + +def load_ipython_extension(ipython: Any) -> None: + from .ipython import load_ipython_extension + load_ipython_extension(ipython) + + +def get_cli_string( + path: Optional[str] = None, + action: Optional[str] = None, + key: Optional[str] = None, + value: Optional[str] = None, + quote: Optional[str] = None, +): + """Returns a string suitable for running as a shell script. + + Useful for converting a arguments passed to a fabric task + to be passed to a `local` or `run` command. + """ + command = ['dotenv'] + if quote: + command.append(f'-q {quote}') + if path: + command.append(f'-f {path}') + if action: + command.append(action) + if key: + command.append(key) + if value: + if ' ' in value: + command.append(f'"{value}"') + else: + command.append(value) + + return ' '.join(command).strip() + + +__all__ = ['get_cli_string', + 'load_dotenv', + 'dotenv_values', + 'get_key', + 'set_key', + 'unset_key', + 'find_dotenv', + 'load_ipython_extension'] diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/__main__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/__main__.py new file mode 100644 index 0000000..3977f55 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/__main__.py @@ -0,0 +1,6 @@ +"""Entry point for cli, enables execution with `python -m dotenv`""" + +from .cli import cli + +if __name__ == "__main__": + cli() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/cli.py b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/cli.py new file mode 100644 index 0000000..65ead46 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/cli.py @@ -0,0 +1,199 @@ +import json +import os +import shlex +import sys +from contextlib import contextmanager +from subprocess import Popen +from typing import Any, Dict, IO, Iterator, List + +try: + import click +except ImportError: + sys.stderr.write('It seems python-dotenv is not installed with cli option. \n' + 'Run pip install "python-dotenv[cli]" to fix this.') + sys.exit(1) + +from .main import dotenv_values, set_key, unset_key +from .version import __version__ + + +def enumerate_env(): + """ + Return a path for the ${pwd}/.env file. + + If pwd does not exist, return None. + """ + try: + cwd = os.getcwd() + except FileNotFoundError: + return None + path = os.path.join(cwd, '.env') + return path + + +@click.group() +@click.option('-f', '--file', default=enumerate_env(), + type=click.Path(file_okay=True), + help="Location of the .env file, defaults to .env file in current working directory.") +@click.option('-q', '--quote', default='always', + type=click.Choice(['always', 'never', 'auto']), + help="Whether to quote or not the variable values. Default mode is always. This does not affect parsing.") +@click.option('-e', '--export', default=False, + type=click.BOOL, + help="Whether to write the dot file as an executable bash script.") +@click.version_option(version=__version__) +@click.pass_context +def cli(ctx: click.Context, file: Any, quote: Any, export: Any) -> None: + """This script is used to set, get or unset values from a .env file.""" + ctx.obj = {'QUOTE': quote, 'EXPORT': export, 'FILE': file} + + +@contextmanager +def stream_file(path: os.PathLike) -> Iterator[IO[str]]: + """ + Open a file and yield the corresponding (decoded) stream. + + Exits with error code 2 if the file cannot be opened. + """ + + try: + with open(path) as stream: + yield stream + except OSError as exc: + print(f"Error opening env file: {exc}", file=sys.stderr) + exit(2) + + +@cli.command() +@click.pass_context +@click.option('--format', default='simple', + type=click.Choice(['simple', 'json', 'shell', 'export']), + help="The format in which to display the list. Default format is simple, " + "which displays name=value without quotes.") +def list(ctx: click.Context, format: bool) -> None: + """Display all the stored key/value.""" + file = ctx.obj['FILE'] + + with stream_file(file) as stream: + values = dotenv_values(stream=stream) + + if format == 'json': + click.echo(json.dumps(values, indent=2, sort_keys=True)) + else: + prefix = 'export ' if format == 'export' else '' + for k in sorted(values): + v = values[k] + if v is not None: + if format in ('export', 'shell'): + v = shlex.quote(v) + click.echo(f'{prefix}{k}={v}') + + +@cli.command() +@click.pass_context +@click.argument('key', required=True) +@click.argument('value', required=True) +def set(ctx: click.Context, key: Any, value: Any) -> None: + """Store the given key/value.""" + file = ctx.obj['FILE'] + quote = ctx.obj['QUOTE'] + export = ctx.obj['EXPORT'] + success, key, value = set_key(file, key, value, quote, export) + if success: + click.echo(f'{key}={value}') + else: + exit(1) + + +@cli.command() +@click.pass_context +@click.argument('key', required=True) +def get(ctx: click.Context, key: Any) -> None: + """Retrieve the value for the given key.""" + file = ctx.obj['FILE'] + + with stream_file(file) as stream: + values = dotenv_values(stream=stream) + + stored_value = values.get(key) + if stored_value: + click.echo(stored_value) + else: + exit(1) + + +@cli.command() +@click.pass_context +@click.argument('key', required=True) +def unset(ctx: click.Context, key: Any) -> None: + """Removes the given key.""" + file = ctx.obj['FILE'] + quote = ctx.obj['QUOTE'] + success, key = unset_key(file, key, quote) + if success: + click.echo(f"Successfully removed {key}") + else: + exit(1) + + +@cli.command(context_settings={'ignore_unknown_options': True}) +@click.pass_context +@click.option( + "--override/--no-override", + default=True, + help="Override variables from the environment file with those from the .env file.", +) +@click.argument('commandline', nargs=-1, type=click.UNPROCESSED) +def run(ctx: click.Context, override: bool, commandline: List[str]) -> None: + """Run command with environment variables present.""" + file = ctx.obj['FILE'] + if not os.path.isfile(file): + raise click.BadParameter( + f'Invalid value for \'-f\' "{file}" does not exist.', + ctx=ctx + ) + dotenv_as_dict = { + k: v + for (k, v) in dotenv_values(file).items() + if v is not None and (override or k not in os.environ) + } + + if not commandline: + click.echo('No command given.') + exit(1) + ret = run_command(commandline, dotenv_as_dict) + exit(ret) + + +def run_command(command: List[str], env: Dict[str, str]) -> int: + """Run command in sub process. + + Runs the command in a sub process with the variables from `env` + added in the current environment variables. + + Parameters + ---------- + command: List[str] + The command and it's parameters + env: Dict + The additional environment variables + + Returns + ------- + int + The return code of the command + + """ + # copy the current environment variables and add the vales from + # `env` + cmd_env = os.environ.copy() + cmd_env.update(env) + + p = Popen(command, + universal_newlines=True, + bufsize=0, + shell=False, + env=cmd_env) + _, _ = p.communicate() + + return p.returncode diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/ipython.py b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/ipython.py new file mode 100644 index 0000000..7df727c --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/ipython.py @@ -0,0 +1,39 @@ +from IPython.core.magic import Magics, line_magic, magics_class # type: ignore +from IPython.core.magic_arguments import (argument, magic_arguments, # type: ignore + parse_argstring) # type: ignore + +from .main import find_dotenv, load_dotenv + + +@magics_class +class IPythonDotEnv(Magics): + + @magic_arguments() + @argument( + '-o', '--override', action='store_true', + help="Indicate to override existing variables" + ) + @argument( + '-v', '--verbose', action='store_true', + help="Indicate function calls to be verbose" + ) + @argument('dotenv_path', nargs='?', type=str, default='.env', + help='Search in increasingly higher folders for the `dotenv_path`') + @line_magic + def dotenv(self, line): + args = parse_argstring(self.dotenv, line) + # Locate the .env file + dotenv_path = args.dotenv_path + try: + dotenv_path = find_dotenv(dotenv_path, True, True) + except IOError: + print("cannot find .env file") + return + + # Load the .env file + load_dotenv(dotenv_path, verbose=args.verbose, override=args.override) + + +def load_ipython_extension(ipython): + """Register the %dotenv magic.""" + ipython.register_magics(IPythonDotEnv) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/main.py b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/main.py new file mode 100644 index 0000000..7bc5428 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/main.py @@ -0,0 +1,392 @@ +import io +import logging +import os +import pathlib +import shutil +import sys +import tempfile +from collections import OrderedDict +from contextlib import contextmanager +from typing import (IO, Dict, Iterable, Iterator, Mapping, Optional, Tuple, + Union) + +from .parser import Binding, parse_stream +from .variables import parse_variables + +# A type alias for a string path to be used for the paths in this file. +# These paths may flow to `open()` and `shutil.move()`; `shutil.move()` +# only accepts string paths, not byte paths or file descriptors. See +# https://github.com/python/typeshed/pull/6832. +StrPath = Union[str, 'os.PathLike[str]'] + +logger = logging.getLogger(__name__) + + +def with_warn_for_invalid_lines(mappings: Iterator[Binding]) -> Iterator[Binding]: + for mapping in mappings: + if mapping.error: + logger.warning( + "Python-dotenv could not parse statement starting at line %s", + mapping.original.line, + ) + yield mapping + + +class DotEnv: + def __init__( + self, + dotenv_path: Optional[StrPath], + stream: Optional[IO[str]] = None, + verbose: bool = False, + encoding: Optional[str] = None, + interpolate: bool = True, + override: bool = True, + ) -> None: + self.dotenv_path: Optional[StrPath] = dotenv_path + self.stream: Optional[IO[str]] = stream + self._dict: Optional[Dict[str, Optional[str]]] = None + self.verbose: bool = verbose + self.encoding: Optional[str] = encoding + self.interpolate: bool = interpolate + self.override: bool = override + + @contextmanager + def _get_stream(self) -> Iterator[IO[str]]: + if self.dotenv_path and os.path.isfile(self.dotenv_path): + with open(self.dotenv_path, encoding=self.encoding) as stream: + yield stream + elif self.stream is not None: + yield self.stream + else: + if self.verbose: + logger.info( + "Python-dotenv could not find configuration file %s.", + self.dotenv_path or '.env', + ) + yield io.StringIO('') + + def dict(self) -> Dict[str, Optional[str]]: + """Return dotenv as dict""" + if self._dict: + return self._dict + + raw_values = self.parse() + + if self.interpolate: + self._dict = OrderedDict(resolve_variables(raw_values, override=self.override)) + else: + self._dict = OrderedDict(raw_values) + + return self._dict + + def parse(self) -> Iterator[Tuple[str, Optional[str]]]: + with self._get_stream() as stream: + for mapping in with_warn_for_invalid_lines(parse_stream(stream)): + if mapping.key is not None: + yield mapping.key, mapping.value + + def set_as_environment_variables(self) -> bool: + """ + Load the current dotenv as system environment variable. + """ + if not self.dict(): + return False + + for k, v in self.dict().items(): + if k in os.environ and not self.override: + continue + if v is not None: + os.environ[k] = v + + return True + + def get(self, key: str) -> Optional[str]: + """ + """ + data = self.dict() + + if key in data: + return data[key] + + if self.verbose: + logger.warning("Key %s not found in %s.", key, self.dotenv_path) + + return None + + +def get_key( + dotenv_path: StrPath, + key_to_get: str, + encoding: Optional[str] = "utf-8", +) -> Optional[str]: + """ + Get the value of a given key from the given .env. + + Returns `None` if the key isn't found or doesn't have a value. + """ + return DotEnv(dotenv_path, verbose=True, encoding=encoding).get(key_to_get) + + +@contextmanager +def rewrite( + path: StrPath, + encoding: Optional[str], +) -> Iterator[Tuple[IO[str], IO[str]]]: + pathlib.Path(path).touch() + + with tempfile.NamedTemporaryFile(mode="w", encoding=encoding, delete=False) as dest: + error = None + try: + with open(path, encoding=encoding) as source: + yield (source, dest) + except BaseException as err: + error = err + + if error is None: + shutil.move(dest.name, path) + else: + os.unlink(dest.name) + raise error from None + + +def set_key( + dotenv_path: StrPath, + key_to_set: str, + value_to_set: str, + quote_mode: str = "always", + export: bool = False, + encoding: Optional[str] = "utf-8", +) -> Tuple[Optional[bool], str, str]: + """ + Adds or Updates a key/value to the given .env + + If the .env path given doesn't exist, fails instead of risking creating + an orphan .env somewhere in the filesystem + """ + if quote_mode not in ("always", "auto", "never"): + raise ValueError(f"Unknown quote_mode: {quote_mode}") + + quote = ( + quote_mode == "always" + or (quote_mode == "auto" and not value_to_set.isalnum()) + ) + + if quote: + value_out = "'{}'".format(value_to_set.replace("'", "\\'")) + else: + value_out = value_to_set + if export: + line_out = f'export {key_to_set}={value_out}\n' + else: + line_out = f"{key_to_set}={value_out}\n" + + with rewrite(dotenv_path, encoding=encoding) as (source, dest): + replaced = False + missing_newline = False + for mapping in with_warn_for_invalid_lines(parse_stream(source)): + if mapping.key == key_to_set: + dest.write(line_out) + replaced = True + else: + dest.write(mapping.original.string) + missing_newline = not mapping.original.string.endswith("\n") + if not replaced: + if missing_newline: + dest.write("\n") + dest.write(line_out) + + return True, key_to_set, value_to_set + + +def unset_key( + dotenv_path: StrPath, + key_to_unset: str, + quote_mode: str = "always", + encoding: Optional[str] = "utf-8", +) -> Tuple[Optional[bool], str]: + """ + Removes a given key from the given `.env` file. + + If the .env path given doesn't exist, fails. + If the given key doesn't exist in the .env, fails. + """ + if not os.path.exists(dotenv_path): + logger.warning("Can't delete from %s - it doesn't exist.", dotenv_path) + return None, key_to_unset + + removed = False + with rewrite(dotenv_path, encoding=encoding) as (source, dest): + for mapping in with_warn_for_invalid_lines(parse_stream(source)): + if mapping.key == key_to_unset: + removed = True + else: + dest.write(mapping.original.string) + + if not removed: + logger.warning("Key %s not removed from %s - key doesn't exist.", key_to_unset, dotenv_path) + return None, key_to_unset + + return removed, key_to_unset + + +def resolve_variables( + values: Iterable[Tuple[str, Optional[str]]], + override: bool, +) -> Mapping[str, Optional[str]]: + new_values: Dict[str, Optional[str]] = {} + + for (name, value) in values: + if value is None: + result = None + else: + atoms = parse_variables(value) + env: Dict[str, Optional[str]] = {} + if override: + env.update(os.environ) # type: ignore + env.update(new_values) + else: + env.update(new_values) + env.update(os.environ) # type: ignore + result = "".join(atom.resolve(env) for atom in atoms) + + new_values[name] = result + + return new_values + + +def _walk_to_root(path: str) -> Iterator[str]: + """ + Yield directories starting from the given directory up to the root + """ + if not os.path.exists(path): + raise IOError('Starting path not found') + + if os.path.isfile(path): + path = os.path.dirname(path) + + last_dir = None + current_dir = os.path.abspath(path) + while last_dir != current_dir: + yield current_dir + parent_dir = os.path.abspath(os.path.join(current_dir, os.path.pardir)) + last_dir, current_dir = current_dir, parent_dir + + +def find_dotenv( + filename: str = '.env', + raise_error_if_not_found: bool = False, + usecwd: bool = False, +) -> str: + """ + Search in increasingly higher folders for the given file + + Returns path to the file if found, or an empty string otherwise + """ + + def _is_interactive(): + """ Decide whether this is running in a REPL or IPython notebook """ + try: + main = __import__('__main__', None, None, fromlist=['__file__']) + except ModuleNotFoundError: + return False + return not hasattr(main, '__file__') + + if usecwd or _is_interactive() or getattr(sys, 'frozen', False): + # Should work without __file__, e.g. in REPL or IPython notebook. + path = os.getcwd() + else: + # will work for .py files + frame = sys._getframe() + current_file = __file__ + + while frame.f_code.co_filename == current_file or not os.path.exists( + frame.f_code.co_filename + ): + assert frame.f_back is not None + frame = frame.f_back + frame_filename = frame.f_code.co_filename + path = os.path.dirname(os.path.abspath(frame_filename)) + + for dirname in _walk_to_root(path): + check_path = os.path.join(dirname, filename) + if os.path.isfile(check_path): + return check_path + + if raise_error_if_not_found: + raise IOError('File not found') + + return '' + + +def load_dotenv( + dotenv_path: Optional[StrPath] = None, + stream: Optional[IO[str]] = None, + verbose: bool = False, + override: bool = False, + interpolate: bool = True, + encoding: Optional[str] = "utf-8", +) -> bool: + """Parse a .env file and then load all the variables found as environment variables. + + Parameters: + dotenv_path: Absolute or relative path to .env file. + stream: Text stream (such as `io.StringIO`) with .env content, used if + `dotenv_path` is `None`. + verbose: Whether to output a warning the .env file is missing. + override: Whether to override the system environment variables with the variables + from the `.env` file. + encoding: Encoding to be used to read the file. + Returns: + Bool: True if at least one environment variable is set else False + + If both `dotenv_path` and `stream` are `None`, `find_dotenv()` is used to find the + .env file. + """ + if dotenv_path is None and stream is None: + dotenv_path = find_dotenv() + + dotenv = DotEnv( + dotenv_path=dotenv_path, + stream=stream, + verbose=verbose, + interpolate=interpolate, + override=override, + encoding=encoding, + ) + return dotenv.set_as_environment_variables() + + +def dotenv_values( + dotenv_path: Optional[StrPath] = None, + stream: Optional[IO[str]] = None, + verbose: bool = False, + interpolate: bool = True, + encoding: Optional[str] = "utf-8", +) -> Dict[str, Optional[str]]: + """ + Parse a .env file and return its content as a dict. + + The returned dict will have `None` values for keys without values in the .env file. + For example, `foo=bar` results in `{"foo": "bar"}` whereas `foo` alone results in + `{"foo": None}` + + Parameters: + dotenv_path: Absolute or relative path to the .env file. + stream: `StringIO` object with .env content, used if `dotenv_path` is `None`. + verbose: Whether to output a warning if the .env file is missing. + encoding: Encoding to be used to read the file. + + If both `dotenv_path` and `stream` are `None`, `find_dotenv()` is used to find the + .env file. + """ + if dotenv_path is None and stream is None: + dotenv_path = find_dotenv() + + return DotEnv( + dotenv_path=dotenv_path, + stream=stream, + verbose=verbose, + interpolate=interpolate, + override=True, + encoding=encoding, + ).dict() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/parser.py b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/parser.py new file mode 100644 index 0000000..735f14a --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/parser.py @@ -0,0 +1,175 @@ +import codecs +import re +from typing import (IO, Iterator, Match, NamedTuple, Optional, # noqa:F401 + Pattern, Sequence, Tuple) + + +def make_regex(string: str, extra_flags: int = 0) -> Pattern[str]: + return re.compile(string, re.UNICODE | extra_flags) + + +_newline = make_regex(r"(\r\n|\n|\r)") +_multiline_whitespace = make_regex(r"\s*", extra_flags=re.MULTILINE) +_whitespace = make_regex(r"[^\S\r\n]*") +_export = make_regex(r"(?:export[^\S\r\n]+)?") +_single_quoted_key = make_regex(r"'([^']+)'") +_unquoted_key = make_regex(r"([^=\#\s]+)") +_equal_sign = make_regex(r"(=[^\S\r\n]*)") +_single_quoted_value = make_regex(r"'((?:\\'|[^'])*)'") +_double_quoted_value = make_regex(r'"((?:\\"|[^"])*)"') +_unquoted_value = make_regex(r"([^\r\n]*)") +_comment = make_regex(r"(?:[^\S\r\n]*#[^\r\n]*)?") +_end_of_line = make_regex(r"[^\S\r\n]*(?:\r\n|\n|\r|$)") +_rest_of_line = make_regex(r"[^\r\n]*(?:\r|\n|\r\n)?") +_double_quote_escapes = make_regex(r"\\[\\'\"abfnrtv]") +_single_quote_escapes = make_regex(r"\\[\\']") + + +class Original(NamedTuple): + string: str + line: int + + +class Binding(NamedTuple): + key: Optional[str] + value: Optional[str] + original: Original + error: bool + + +class Position: + def __init__(self, chars: int, line: int) -> None: + self.chars = chars + self.line = line + + @classmethod + def start(cls) -> "Position": + return cls(chars=0, line=1) + + def set(self, other: "Position") -> None: + self.chars = other.chars + self.line = other.line + + def advance(self, string: str) -> None: + self.chars += len(string) + self.line += len(re.findall(_newline, string)) + + +class Error(Exception): + pass + + +class Reader: + def __init__(self, stream: IO[str]) -> None: + self.string = stream.read() + self.position = Position.start() + self.mark = Position.start() + + def has_next(self) -> bool: + return self.position.chars < len(self.string) + + def set_mark(self) -> None: + self.mark.set(self.position) + + def get_marked(self) -> Original: + return Original( + string=self.string[self.mark.chars:self.position.chars], + line=self.mark.line, + ) + + def peek(self, count: int) -> str: + return self.string[self.position.chars:self.position.chars + count] + + def read(self, count: int) -> str: + result = self.string[self.position.chars:self.position.chars + count] + if len(result) < count: + raise Error("read: End of string") + self.position.advance(result) + return result + + def read_regex(self, regex: Pattern[str]) -> Sequence[str]: + match = regex.match(self.string, self.position.chars) + if match is None: + raise Error("read_regex: Pattern not found") + self.position.advance(self.string[match.start():match.end()]) + return match.groups() + + +def decode_escapes(regex: Pattern[str], string: str) -> str: + def decode_match(match: Match[str]) -> str: + return codecs.decode(match.group(0), 'unicode-escape') # type: ignore + + return regex.sub(decode_match, string) + + +def parse_key(reader: Reader) -> Optional[str]: + char = reader.peek(1) + if char == "#": + return None + elif char == "'": + (key,) = reader.read_regex(_single_quoted_key) + else: + (key,) = reader.read_regex(_unquoted_key) + return key + + +def parse_unquoted_value(reader: Reader) -> str: + (part,) = reader.read_regex(_unquoted_value) + return re.sub(r"\s+#.*", "", part).rstrip() + + +def parse_value(reader: Reader) -> str: + char = reader.peek(1) + if char == u"'": + (value,) = reader.read_regex(_single_quoted_value) + return decode_escapes(_single_quote_escapes, value) + elif char == u'"': + (value,) = reader.read_regex(_double_quoted_value) + return decode_escapes(_double_quote_escapes, value) + elif char in (u"", u"\n", u"\r"): + return u"" + else: + return parse_unquoted_value(reader) + + +def parse_binding(reader: Reader) -> Binding: + reader.set_mark() + try: + reader.read_regex(_multiline_whitespace) + if not reader.has_next(): + return Binding( + key=None, + value=None, + original=reader.get_marked(), + error=False, + ) + reader.read_regex(_export) + key = parse_key(reader) + reader.read_regex(_whitespace) + if reader.peek(1) == "=": + reader.read_regex(_equal_sign) + value: Optional[str] = parse_value(reader) + else: + value = None + reader.read_regex(_comment) + reader.read_regex(_end_of_line) + return Binding( + key=key, + value=value, + original=reader.get_marked(), + error=False, + ) + except Error: + reader.read_regex(_rest_of_line) + return Binding( + key=None, + value=None, + original=reader.get_marked(), + error=True, + ) + + +def parse_stream(stream: IO[str]) -> Iterator[Binding]: + reader = Reader(stream) + while reader.has_next(): + yield parse_binding(reader) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/py.typed b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/py.typed new file mode 100644 index 0000000..7632ecf --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/variables.py b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/variables.py new file mode 100644 index 0000000..667f2f2 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/variables.py @@ -0,0 +1,86 @@ +import re +from abc import ABCMeta, abstractmethod +from typing import Iterator, Mapping, Optional, Pattern + +_posix_variable: Pattern[str] = re.compile( + r""" + \$\{ + (?P[^\}:]*) + (?::- + (?P[^\}]*) + )? + \} + """, + re.VERBOSE, +) + + +class Atom(metaclass=ABCMeta): + def __ne__(self, other: object) -> bool: + result = self.__eq__(other) + if result is NotImplemented: + return NotImplemented + return not result + + @abstractmethod + def resolve(self, env: Mapping[str, Optional[str]]) -> str: ... + + +class Literal(Atom): + def __init__(self, value: str) -> None: + self.value = value + + def __repr__(self) -> str: + return f"Literal(value={self.value})" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return self.value == other.value + + def __hash__(self) -> int: + return hash((self.__class__, self.value)) + + def resolve(self, env: Mapping[str, Optional[str]]) -> str: + return self.value + + +class Variable(Atom): + def __init__(self, name: str, default: Optional[str]) -> None: + self.name = name + self.default = default + + def __repr__(self) -> str: + return f"Variable(name={self.name}, default={self.default})" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return (self.name, self.default) == (other.name, other.default) + + def __hash__(self) -> int: + return hash((self.__class__, self.name, self.default)) + + def resolve(self, env: Mapping[str, Optional[str]]) -> str: + default = self.default if self.default is not None else "" + result = env.get(self.name, default) + return result if result is not None else "" + + +def parse_variables(value: str) -> Iterator[Atom]: + cursor = 0 + + for match in _posix_variable.finditer(value): + (start, end) = match.span() + name = match["name"] + default = match["default"] + + if start > cursor: + yield Literal(value=value[cursor:start]) + + yield Variable(name=name, default=default) + cursor = end + + length = len(value) + if cursor < length: + yield Literal(value=value[cursor:length]) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/version.py b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/version.py new file mode 100644 index 0000000..5c4105c --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/dotenv/version.py @@ -0,0 +1 @@ +__version__ = "1.0.1" diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/INSTALLER b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/LICENSE.txt b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/LICENSE.txt new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/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/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/METADATA b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/METADATA new file mode 100644 index 0000000..5a02107 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/METADATA @@ -0,0 +1,101 @@ +Metadata-Version: 2.1 +Name: Flask +Version: 3.0.3 +Summary: A simple framework for building complex web applications. +Maintainer-email: Pallets +Requires-Python: >=3.8 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Flask +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Classifier: Typing :: Typed +Requires-Dist: Werkzeug>=3.0.0 +Requires-Dist: Jinja2>=3.1.2 +Requires-Dist: itsdangerous>=2.1.2 +Requires-Dist: click>=8.1.3 +Requires-Dist: blinker>=1.6.2 +Requires-Dist: importlib-metadata>=3.6.0; python_version < '3.10' +Requires-Dist: asgiref>=3.2 ; extra == "async" +Requires-Dist: python-dotenv ; extra == "dotenv" +Project-URL: Changes, https://flask.palletsprojects.com/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/ + + +## Installing + +Install and update from [PyPI][] using an installer such as [pip][]: + +``` +$ pip install -U Flask +``` + +[PyPI]: https://pypi.org/project/Flask/ +[pip]: https://pip.pypa.io/en/stable/getting-started/ + + +## 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) +``` + + +## Contributing + +For guidance on setting up a development environment and how to make a +contribution to Flask, see the [contributing guidelines][]. + +[contributing guidelines]: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst + + +## Donate + +The Pallets organization develops and supports Flask and the libraries +it uses. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/RECORD b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/RECORD new file mode 100644 index 0000000..135dcc4 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/RECORD @@ -0,0 +1,58 @@ +../../../bin/flask,sha256=WGkUeszXfSRxQKPMcg5-RJwbk3v8E5yJViSaAaGdwNE,249 +flask-3.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +flask-3.0.3.dist-info/LICENSE.txt,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +flask-3.0.3.dist-info/METADATA,sha256=exPahy4aahjV-mYqd9qb5HNP8haB_IxTuaotoSvCtag,3177 +flask-3.0.3.dist-info/RECORD,, +flask-3.0.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask-3.0.3.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +flask-3.0.3.dist-info/entry_points.txt,sha256=bBP7hTOS5fz9zLtC7sPofBZAlMkEvBxu7KqS6l5lvc4,40 +flask/__init__.py,sha256=6xMqdVA0FIQ2U1KVaGX3lzNCdXPzoHPaa0hvQCNcfSk,2625 +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=7-lh6cIj27riTE1Q18Ok1p5nOZ8qYiMux4Btc6o6mNc,60143 +flask/blueprints.py,sha256=7INXPwTkUxfOQXOOv1yu52NpHPmPGI5fMTMFZ-BG9yY,4430 +flask/cli.py,sha256=OOaf_Efqih1i2in58j-5ZZZmQnPpaSfiUFbEjlL9bzw,35825 +flask/config.py,sha256=bLzLVAj-cq-Xotu9erqOFte0xSFaVXyfz0AkP4GbwmY,13312 +flask/ctx.py,sha256=4atDhJJ_cpV1VMq4qsfU4E_61M1oN93jlS2H9gjrl58,15120 +flask/debughelpers.py,sha256=PGIDhStW_efRjpaa3zHIpo-htStJOR41Ip3OJWPYBwo,6080 +flask/globals.py,sha256=XdQZmStBmPIs8t93tjx6pO7Bm3gobAaONWkFcUHaGas,1713 +flask/helpers.py,sha256=tYrcQ_73GuSZVEgwFr-eMmV69UriFQDBmt8wZJIAqvg,23084 +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=q6iB83lSiopy80DZPrU-9mGcWwrD0mvLjiv9fHrRZgc,7646 +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=YG5Gf7JVf1c0yccWDZ86q5VSfJUidOVp27HFxFNxC7U,38053 +flask/sansio/blueprints.py,sha256=Tqe-7EkZ-tbWchm8iDoCfD848f0_3nLv6NNjeIPvHwM,24637 +flask/sansio/scaffold.py,sha256=WLV9TRQMMhGlXz-1OKtQ3lv6mtIBQZxdW2HezYrGxoI,30633 +flask/sessions.py,sha256=RU4lzm9MQW9CtH8rVLRTDm8USMJyT4LbvYe7sxM2__k,14807 +flask/signals.py,sha256=V7lMUww7CqgJ2ThUBn1PiatZtQanOyt7OZpu2GZI-34,750 +flask/templating.py,sha256=2TcXLT85Asflm2W9WOSFxKCmYn5e49w_Jkg9-NaaJWo,7537 +flask/testing.py,sha256=3BFXb3bP7R5r-XLBuobhczbxDu8-1LWRzYuhbr-lwaE,10163 +flask/typing.py,sha256=ZavK-wV28Yv8CQB7u73qZp_jLalpbWdrXS37QR1ftN0,3190 +flask/views.py,sha256=B66bTvYBBcHMYk4dA1ScZD0oTRTBl0I5smp1lRm9riI,6939 +flask/wrappers.py,sha256=m1j5tIJxIu8_sPPgTAB_G4TTh52Q-HoDuw_qHV5J59g,5831 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/REQUESTED b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/WHEEL b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/WHEEL new file mode 100644 index 0000000..3b5e64b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.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/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/entry_points.txt b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/entry_points.txt new file mode 100644 index 0000000..eec6733 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask-3.0.3.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +flask=flask.cli:main + diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/__init__.py new file mode 100644 index 0000000..e86eb43 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/__init__.py @@ -0,0 +1,60 @@ +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 + + +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.1. Use feature detection or" + " 'importlib.metadata.version(\"flask\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("flask") + + raise AttributeError(name) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask/__main__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/__main__.py new file mode 100644 index 0000000..4e28416 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/__main__.py @@ -0,0 +1,3 @@ +from .cli import main + +main() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask/app.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/app.py new file mode 100644 index 0000000..7622b5e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/app.py @@ -0,0 +1,1498 @@ +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 . 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 + +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, + "PERMANENT_SESSION_LIFETIME": timedelta(days=31), + "USE_X_SENDFILE": False, + "SERVER_NAME": None, + "APPLICATION_ROOT": "/", + "SESSION_COOKIE_NAME": "session", + "SESSION_COOKIE_DOMAIN": None, + "SESSION_COOKIE_PATH": None, + "SESSION_COOKIE_HTTPONLY": True, + "SESSION_COOKIE_SECURE": False, + "SESSION_COOKIE_SAMESITE": None, + "SESSION_REFRESH_EACH_REQUEST": True, + "MAX_CONTENT_LENGTH": None, + "SEND_FILE_MAX_AGE_DEFAULT": None, + "TRAP_BAD_REQUEST_ERRORS": None, + "TRAP_HTTP_EXCEPTIONS": False, + "EXPLAIN_TEMPLATE_LOADING": False, + "PREFERRED_URL_SCHEME": "http", + "TEMPLATES_AUTO_RELOAD": None, + "MAX_COOKIE_SIZE": 4093, + } + ) + + #: The 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 # noqa: B950 + ) + + 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") -> 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". + + Note this is a duplicate of the same method in the Flask + class. + + """ + if mode not in {"r", "rt", "rb"}: + raise ValueError("Resources can only be opened for reading.") + + return open(os.path.join(self.root_path, resource), mode) + + def open_instance_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: + """Opens a resource from the application's instance folder + (:attr:`instance_path`). Otherwise works like + :meth:`open_resource`. Instance resources can also be opened for + writing. + + :param resource: the name of the resource. To access resources within + subfolders use forward slashes as separator. + :param mode: resource file opening mode, default is 'rb'. + """ + return open(os.path.join(self.instance_path, resource), mode) + + def create_jinja_environment(self) -> Environment: + """Create the Jinja environment based on :attr:`jinja_options` + and the various Jinja-related methods of the app. Changing + :attr:`jinja_options` after this will have no effect. Also adds + Flask-related globals and filters to the environment. + + .. versionchanged:: 0.11 + ``Environment.auto_reload`` set in accordance with + ``TEMPLATES_AUTO_RELOAD`` configuration option. + + .. versionadded:: 0.5 + """ + options = dict(self.jinja_options) + + if "autoescape" not in options: + options["autoescape"] = self.select_jinja_autoescape + + if "auto_reload" not in options: + auto_reload = self.config["TEMPLATES_AUTO_RELOAD"] + + if auto_reload is None: + auto_reload = self.debug + + options["auto_reload"] = auto_reload + + rv = self.jinja_environment(self, **options) + rv.globals.update( + url_for=self.url_for, + get_flashed_messages=get_flashed_messages, + config=self.config, + # request, session and g are normally added with the + # context processor for efficiency reasons but for imported + # templates we also want the proxies in there. + request=request, + session=session, + g=g, + ) + rv.policies["json.dumps_function"] = self.json.dumps + return rv + + def create_url_adapter(self, request: Request | None) -> MapAdapter | None: + """Creates a URL adapter for the given request. The URL adapter + is created at a point where the request context is not yet set + up so the request is passed explicitly. + + .. versionadded:: 0.6 + + .. versionchanged:: 0.9 + This can now also be called without a request object when the + URL adapter is created for the application context. + + .. versionchanged:: 1.0 + :data:`SERVER_NAME` no longer implicitly enables subdomain + matching. Use :attr:`subdomain_matching` instead. + """ + if request is not None: + # If subdomain matching is disabled (the default), use the + # default subdomain in all cases. This should be the default + # in Werkzeug but it currently does not have that feature. + if not self.subdomain_matching: + subdomain = self.url_map.default_subdomain or None + else: + subdomain = None + + return self.url_map.bind_to_environ( + request.environ, + server_name=self.config["SERVER_NAME"], + subdomain=subdomain, + ) + # We need at the very least the server name to be set for this + # to work. + if self.config["SERVER_NAME"] is not None: + return self.url_map.bind( + self.config["SERVER_NAME"], + script_name=self.config["APPLICATION_ROOT"], + url_scheme=self.config["PREFERRED_URL_SCHEME"], + ) + + return None + + def 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 = headers = None + + # unpack tuple returns + if isinstance(rv, tuple): + len_rv = len(rv) + + # a 3-tuple is unpacked directly + if len_rv == 3: + rv, status, headers = rv # type: ignore[misc] + # decide if a 2-tuple has status or headers + elif len_rv == 2: + if isinstance(rv[1], (Headers, dict, tuple, list)): + rv, headers = rv + else: + rv, status = rv # type: ignore[assignment,misc] + # other sized tuples are not allowed + else: + raise TypeError( + "The view function did not return a valid response tuple." + " The tuple must have the form (body, status, headers)," + " (body, status), or (body, headers)." + ) + + # the body must not be None + if rv is None: + raise TypeError( + f"The view function for {request.endpoint!r} did not" + " return a valid response. The function either returned" + " None or ended without a return statement." + ) + + # make sure the body is an instance of the response class + if not isinstance(rv, self.response_class): + if isinstance(rv, (str, bytes, bytearray)) or isinstance(rv, 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, + 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) # type: ignore[arg-type] + + 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: # noqa: B001 + error = sys.exc_info()[1] + raise + return response(environ, start_response) + finally: + if "werkzeug.debug.preserve_context" in environ: + environ["werkzeug.debug.preserve_context"](_cv_app.get()) + environ["werkzeug.debug.preserve_context"](_cv_request.get()) + + if error is not None and self.should_ignore_error(error): + error = None + + ctx.pop(error) + + def __call__( + self, environ: 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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/blueprints.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/blueprints.py new file mode 100644 index 0000000..aa9eacf --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/blueprints.py @@ -0,0 +1,129 @@ +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") -> 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". + + Note this is a duplicate of the same method in the Flask + class. + + """ + if mode not in {"r", "rt", "rb"}: + raise ValueError("Resources can only be opened for reading.") + + return open(os.path.join(self.root_path, resource), mode) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask/cli.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/cli.py new file mode 100644 index 0000000..ecb292a --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/cli.py @@ -0,0 +1,1109 @@ +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. + """ + + def __init__( + self, + app_import_path: str | None = None, + create_app: t.Callable[..., Flask] | None = None, + set_debug_flag: bool = True, + ) -> None: + #: Optionally the import path for the Flask application. + self.app_import_path = app_import_path + #: Optionally a function that is passed the script info to create + #: the instance of the application. + self.create_app = create_app + #: A dictionary with arbitrary data that can be associated with + #: this script info. + self.data: dict[t.Any, t.Any] = {} + self.set_debug_flag = set_debug_flag + self._loaded_app: Flask | None = None + + def load_app(self) -> Flask: + """Loads the Flask app (if not yet loaded) and returns it. Calling + this multiple times will just result in the already loaded app to + be returned. + """ + if self._loaded_app is not None: + return self._loaded_app + + if self.create_app is not None: + app: Flask | None = 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: + if value is None: + return None + + import importlib + + try: + importlib.import_module("dotenv") + except ImportError: + raise click.BadParameter( + "python-dotenv must be installed to load an env file.", + ctx=ctx, + param=param, + ) from None + + # Don't check FLASK_SKIP_DOTENV, that only disables automatically + # loading .env and .flaskenv files. + load_dotenv(value) + return value + + +# This option is eager so env vars are loaded as early as possible to be +# used by other options. +_env_file_option = click.Option( + ["-e", "--env-file"], + type=click.Path(exists=True, dir_okay=False), + help="Load environment variables from this file. python-dotenv must be installed.", + is_eager=True, + expose_value=False, + callback=_env_file_callback, +) + + +class FlaskGroup(AppGroup): + """Special subclass of the :class:`AppGroup` group that supports + loading more commands from the configured Flask app. Normally a + developer does not have to interface with this class but there are + some very advanced use cases for which it makes sense to create an + instance of this. see :ref:`custom-scripts`. + + :param add_default_commands: if this is True then the default run and + shell commands will be added. + :param add_version_option: adds the ``--version`` option. + :param create_app: an optional callback that is passed the script info and + returns the loaded app. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param set_debug_flag: Set the app's debug flag. + + .. versionchanged:: 2.2 + Added the ``-A/--app``, ``--debug/--no-debug``, ``-e/--env-file`` options. + + .. versionchanged:: 2.2 + An app context is pushed when running ``app.cli`` commands, so + ``@with_appcontext`` is no longer required for those commands. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment variables + from :file:`.env` and :file:`.flaskenv` files. + """ + + def __init__( + self, + add_default_commands: bool = True, + create_app: t.Callable[..., Flask] | None = None, + add_version_option: bool = True, + load_dotenv: bool = True, + set_debug_flag: bool = True, + **extra: t.Any, + ) -> None: + params = list(extra.pop("params", None) or ()) + # Processing is done with option callbacks instead of a group + # callback. This allows users to make a custom group callback + # without losing the behavior. --env-file must come first so + # that it is eagerly evaluated before --app. + params.extend((_env_file_option, _app_option, _debug_option)) + + if add_version_option: + params.append(version_option) + + if "context_settings" not in extra: + extra["context_settings"] = {} + + extra["context_settings"].setdefault("auto_envvar_prefix", "FLASK") + + super().__init__(params=params, **extra) + + self.create_app = create_app + self.load_dotenv = load_dotenv + self.set_debug_flag = set_debug_flag + + if add_default_commands: + self.add_command(run_command) + self.add_command(shell_command) + self.add_command(routes_command) + + self._loaded_plugin_commands = False + + def _load_plugin_commands(self) -> 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 + + 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" + + # Attempt to load .env and .flask env files. The --env-file + # option can cause another file to be loaded. + if get_load_dotenv(self.load_dotenv): + load_dotenv() + + if "obj" not in extra and "obj" not in self.context_settings: + extra["obj"] = ScriptInfo( + create_app=self.create_app, set_debug_flag=self.set_debug_flag + ) + + return super().make_context(info_name, args, parent=parent, **extra) + + def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help: + # Attempt to load --env-file and --app early in case they + # were given as env vars. Otherwise no_args_is_help will not + # see commands from app.cli. + _env_file_option.handle_parse_result(ctx, {}, []) + _app_option.handle_parse_result(ctx, {}, []) + + return super().parse_args(ctx, args) + + +def _path_is_ancestor(path: 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) -> bool: + """Load "dotenv" files in order of precedence to set environment variables. + + If an env var is already set it is not overwritten, so earlier files in the + list are preferred over later files. + + This is a no-op if `python-dotenv`_ is not installed. + + .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme + + :param path: Load the file at this location instead of searching. + :return: ``True`` if a file was loaded. + + .. versionchanged:: 2.0 + The current directory is not changed to the location of the + loaded file. + + .. versionchanged:: 2.0 + When loading the env files, set the default encoding to UTF-8. + + .. versionchanged:: 1.1.0 + Returns ``False`` when python-dotenv is not installed, or when + the given path isn't a file. + + .. versionadded:: 1.0 + """ + try: + import dotenv + except ImportError: + if path or os.path.isfile(".env") or os.path.isfile(".flaskenv"): + click.secho( + " * Tip: There are .env or .flaskenv files present." + ' Do "pip install python-dotenv" to use them.', + fg="yellow", + err=True, + ) + + return False + + # Always return after attempting to load a given path, don't load + # the default files. + if path is not None: + if os.path.isfile(path): + return dotenv.load_dotenv(path, encoding="utf-8") + + return False + + loaded = False + + for name in (".env", ".flaskenv"): + path = dotenv.find_dotenv(name, usecwd=True) + + if not path: + continue + + dotenv.load_dotenv(path, encoding="utf-8") + loaded = True + + return loaded # True if at least one file was located and loaded. + + +def show_server_banner(debug: 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() + 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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/config.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/config.py new file mode 100644 index 0000000..7e3ba17 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/config.py @@ -0,0 +1,370 @@ +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}_" + len_prefix = len(prefix) + + for key in sorted(os.environ): + if not key.startswith(prefix): + continue + + value = os.environ[key] + + try: + value = loads(value) + except Exception: + # Keep the value as a string if loading failed. + pass + + # Change to key.removeprefix(prefix) on Python >= 3.9. + key = key[len_prefix:] + + if "__" not in key: + # A non-nested key, set directly. + self[key] = value + continue + + # Traverse nested dictionaries with keys separated by "__". + current = self + *parts, tail = key.split("__") + + for part in parts: + # If an intermediate dict does not exist, create it. + if part not in current: + current[part] = {} + + current = current[part] + + current[tail] = value + + return True + + def from_pyfile( + self, filename: str | 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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/ctx.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/ctx.py new file mode 100644 index 0000000..9b164d3 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/ctx.py @@ -0,0 +1,449 @@ +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: # type: ignore[union-attr] + return ctx.app.ensure_sync(f)(*args, **kwargs) # type: ignore[union-attr] + + 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 + + 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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/debughelpers.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/debughelpers.py new file mode 100644 index 0000000..2c8c4c4 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/globals.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/globals.py new file mode 100644 index 0000000..e2c410c --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/helpers.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/helpers.py new file mode 100644 index 0000000..359a842 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/helpers.py @@ -0,0 +1,621 @@ +from __future__ import annotations + +import importlib.util +import os +import sys +import typing as t +from datetime import datetime +from functools import lru_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_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") + + +def stream_with_context( + generator_or_function: t.Iterator[t.AnyStr] | t.Callable[..., t.Iterator[t.AnyStr]], +) -> t.Iterator[t.AnyStr]: + """Request contexts disappear when the response is started on the server. + This is done for efficiency reasons and to make it less likely to encounter + memory leaks with badly written WSGI middlewares. The downside is that if + you are using streamed responses, the generator cannot access request bound + information any more. + + This function however can help you keep the context around for longer:: + + from flask import stream_with_context, request, Response + + @app.route('/stream') + def streamed_response(): + @stream_with_context + def generate(): + yield 'Hello ' + yield request.args['name'] + yield '!' + return Response(generate()) + + Alternatively it can also be used around a specific generator:: + + from flask import stream_with_context, request, Response + + @app.route('/stream') + def streamed_response(): + def generate(): + yield 'Hello ' + yield request.args['name'] + yield '!' + return Response(stream_with_context(generate())) + + .. versionadded:: 0.9 + """ + try: + gen = iter(generator_or_function) # type: ignore[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 | None]: + ctx = _cv_request.get(None) + if ctx is None: + raise RuntimeError( + "'stream_with_context' can only be used when a request" + " context is active, such as in a view function." + ) + with ctx: + # Dummy sentinel. Has to be inside the context block or we're + # not actually keeping the context around. + yield None + + # The try/finally is here so that if someone passes a WSGI level + # iterator in we're still running the cleanup logic. Generators + # don't need that because they are closed on their destruction + # automatically. + try: + yield from gen + finally: + if hasattr(gen, "close"): + gen.close() + + # The trick is to start the generator. Then the code execution runs until + # the first dummy None is yielded at which point the context was already + # pushed. This item is discarded. Then when the iteration continues the + # real generator is executed. + wrapped_g = generator() + next(wrapped_g) + return wrapped_g # type: ignore[return-value] + + +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, # type: ignore + ) + return kwargs + + +def send_file( + path_or_file: os.PathLike[t.AnyStr] | str | t.BinaryIO, + mimetype: str | None = None, + as_attachment: bool = False, + download_name: str | None = None, + conditional: bool = True, + etag: bool | str = True, + last_modified: datetime | int | float | None = None, + max_age: None | (int | t.Callable[[str | None], int | None]) = None, +) -> Response: + """Send the contents of a file to the client. + + The first argument can be a file path or a file-like object. Paths + are preferred in most cases because Werkzeug can manage the file and + get extra information from the path. Passing a file-like object + requires that the file is opened in binary mode, and is mostly + useful when building a file in memory with :class:`io.BytesIO`. + + Never pass file paths provided by a user. The path is assumed to be + trusted, so a user could craft a path to access a file you didn't + intend. Use :func:`send_from_directory` to safely serve + user-requested paths from within a directory. + + If the WSGI server sets a ``file_wrapper`` in ``environ``, it is + used, otherwise Werkzeug's built-in wrapper is used. Alternatively, + if the HTTP server supports ``X-Sendfile``, configuring Flask with + ``USE_X_SENDFILE = True`` will tell the server to send the given + path, which is much more efficient than reading it in Python. + + :param path_or_file: The path to the file to send, relative to the + current working directory if a relative path is given. + Alternatively, a file-like object opened in binary mode. Make + sure the file pointer is seeked to the start of the data. + :param mimetype: The MIME type to send for the file. If not + provided, it will try to detect it from the file name. + :param as_attachment: Indicate to a browser that it should offer to + save the file instead of displaying it. + :param download_name: The default name browsers will use when saving + the file. Defaults to the passed file name. + :param conditional: Enable conditional and range responses based on + request headers. Requires passing a file path and ``environ``. + :param etag: Calculate an ETag for the file, which requires passing + a file path. Can also be a string to use instead. + :param last_modified: The last modified time to send for the file, + in seconds. If not provided, it will try to detect it from the + file path. + :param max_age: How long the client should cache the file, in + seconds. If set, ``Cache-Control`` will be ``public``, otherwise + it will be ``no-cache`` to prefer conditional caching. + + .. versionchanged:: 2.0 + ``download_name`` replaces the ``attachment_filename`` + parameter. If ``as_attachment=False``, it is passed with + ``Content-Disposition: inline`` instead. + + .. versionchanged:: 2.0 + ``max_age`` replaces the ``cache_timeout`` parameter. + ``conditional`` is enabled and ``max_age`` is not set by + default. + + .. versionchanged:: 2.0 + ``etag`` replaces the ``add_etags`` parameter. It can be a + string to use instead of generating one. + + .. versionchanged:: 2.0 + Passing a file-like object that inherits from + :class:`~io.TextIOBase` will raise a :exc:`ValueError` rather + than sending an empty file. + + .. versionadded:: 2.0 + Moved the implementation to Werkzeug. This is now a wrapper to + pass some Flask-specific arguments. + + .. versionchanged:: 1.1 + ``filename`` may be a :class:`~os.PathLike` object. + + .. versionchanged:: 1.1 + Passing a :class:`~io.BytesIO` object supports range requests. + + .. versionchanged:: 1.0.3 + Filenames are encoded with ASCII instead of Latin-1 for broader + compatibility with WSGI servers. + + .. versionchanged:: 1.0 + UTF-8 filenames as specified in :rfc:`2231` are supported. + + .. versionchanged:: 0.12 + The filename is no longer automatically inferred from file + objects. If you want to use automatic MIME and etag support, + pass a filename via ``filename_or_fp`` or + ``attachment_filename``. + + .. versionchanged:: 0.12 + ``attachment_filename`` is preferred over ``filename`` for MIME + detection. + + .. versionchanged:: 0.9 + ``cache_timeout`` defaults to + :meth:`Flask.get_send_file_max_age`. + + .. versionchanged:: 0.7 + MIME guessing and etag support for file-like objects was + 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. + :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) + 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] + + +@lru_cache(maxsize=None) +def _split_blueprint_path(name: str) -> list[str]: + out: list[str] = [name] + + if "." in name: + out.extend(_split_blueprint_path(name.rpartition(".")[0])) + + return out diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask/json/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/json/__init__.py new file mode 100644 index 0000000..c0941d0 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/json/provider.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/json/provider.py new file mode 100644 index 0000000..f9b2e8f --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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) + + 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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/json/tag.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/json/tag.py new file mode 100644 index 0000000..8dc3629 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/logging.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/logging.py new file mode 100644 index 0000000..0cb8f43 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/py.typed b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/README.md b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/README.md new file mode 100644 index 0000000..623ac19 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/app.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/app.py new file mode 100644 index 0000000..01fd5db --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/app.py @@ -0,0 +1,964 @@ +from __future__ import annotations + +import logging +import os +import sys +import typing as t +from datetime import timedelta +from itertools import chain + +from werkzeug.exceptions import Aborter +from werkzeug.exceptions import BadRequest +from werkzeug.exceptions import BadRequestKeyError +from werkzeug.routing import BuildError +from werkzeug.routing import Map +from werkzeug.routing import Rule +from werkzeug.sansio.response import Response +from werkzeug.utils import cached_property +from werkzeug.utils import redirect as _wz_redirect + +from .. import typing as ft +from ..config import Config +from ..config import ConfigAttribute +from ..ctx import _AppCtxGlobals +from ..helpers import _split_blueprint_path +from ..helpers import get_debug_flag +from ..json.provider import DefaultJSONProvider +from ..json.provider import JSONProvider +from ..logging import create_logger +from ..templating import DispatchingJinjaLoader +from ..templating import Environment +from .scaffold import _endpoint_from_view_func +from .scaffold import find_package +from .scaffold import Scaffold +from .scaffold import setupmethod + +if t.TYPE_CHECKING: # pragma: no cover + from werkzeug.wrappers import Response as BaseResponse + + from ..testing import FlaskClient + from ..testing import FlaskCliRunner + from .blueprints import Blueprint + +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 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, + ): + 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: # type: ignore + """The name of the application. This is usually the import name + with the difference that it's guessed from the run file if the + import name is main. This name is used as a display name when + Flask needs the name of the application. It can be set and overridden + to change the value. + + .. versionadded:: 0.8 + """ + if self.import_name == "__main__": + fn: 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 Jinja2 environment. Can be used to + override just the loader and keeping the rest unchanged. It's + discouraged to override this function. Instead one should override + the :meth:`jinja_loader` function instead. + + The global loader dispatches between the loaders of the application + and the individual blueprints. + + .. versionadded:: 0.7 + """ + return DispatchingJinjaLoader(self) + + def select_jinja_autoescape(self, filename: str) -> bool: + """Returns ``True`` if autoescaping should be active for the given + template name. If no template name is given, returns `True`. + + .. versionchanged:: 2.2 + Autoescaping is now enabled by default for ``.svg`` files. + + .. versionadded:: 0.5 + """ + if filename is None: + return True + return filename.endswith((".html", ".htm", ".xml", ".xhtml", ".svg")) + + @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(getattr(view_func, "required_methods", ())) + + # starting with Flask 0.8 the view_func object can disable and + # force-enable the automatic options handling. + if provide_automatic_options is None: + provide_automatic_options = getattr( + view_func, "provide_automatic_options", None + ) + + if provide_automatic_options is None: + if "OPTIONS" not in methods: + provide_automatic_options = True + required_methods.add("OPTIONS") + else: + provide_automatic_options = False + + # Add the required methods now. + methods |= required_methods + + rule_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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/blueprints.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/blueprints.py new file mode 100644 index 0000000..4f912cc --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/scaffold.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/scaffold.py new file mode 100644 index 0000000..69e33a0 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sansio/scaffold.py @@ -0,0 +1,801 @@ +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 # type: ignore + self.static_url_path = static_url_path + + #: The path to the templates folder, relative to + #: :attr:`root_path`, to add to the template loader. ``None`` if + #: templates should not be added. + self.template_folder = template_folder + + if root_path is None: + root_path = get_root_path(self.import_name) + + #: Absolute path to the package on the filesystem. Used to look + #: up resources contained in the package. + self.root_path = root_path + + #: 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 _path_is_relative_to(path: pathlib.PurePath, base: str) -> bool: + # Path.is_relative_to doesn't exist until Python 3.9 + try: + path.relative_to(base) + return True + except ValueError: + return False + + +def _find_package_path(import_name: 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 _path_is_relative_to(package_path, 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 _path_is_relative_to(pathlib.PurePath(package_path), py_prefix): + return py_prefix, package_path + + site_parent, site_folder = os.path.split(package_path) + + # installed to a virtualenv + if site_folder.lower() == "site-packages": + parent, folder = os.path.split(site_parent) + + # Windows (prefix/lib/site-packages) + if folder.lower() == "lib": + return parent, package_path + + # Unix (prefix/lib/pythonX.Y/site-packages) + if os.path.basename(parent).lower() == "lib": + return os.path.dirname(parent), package_path + + # something else (prefix/site-packages) + return site_parent, package_path + + # not installed + return None, package_path diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sessions.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sessions.py new file mode 100644 index 0000000..ee19ad6 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/sessions.py @@ -0,0 +1,379 @@ +from __future__ import annotations + +import hashlib +import typing as t +from collections.abc import MutableMapping +from datetime import datetime +from datetime import timezone + +from itsdangerous import BadSignature +from itsdangerous import URLSafeTimedSerializer +from werkzeug.datastructures import CallbackDict + +from .json.tag import TaggedJSONSerializer + +if t.TYPE_CHECKING: # pragma: no cover + import typing_extensions as te + + from .app import Flask + from .wrappers import Request + from .wrappers import Response + + +# TODO generic when Python > 3.8 +class SessionMixin(MutableMapping): # type: ignore[type-arg] + """Expands a basic dictionary with session attributes.""" + + @property + def permanent(self) -> bool: + """This reflects the ``'_permanent'`` key in the dict.""" + return self.get("_permanent", False) + + @permanent.setter + def permanent(self, value: bool) -> None: + self["_permanent"] = bool(value) + + #: Some implementations can detect whether a session is newly + #: created, but that is not guaranteed. Use with caution. The mixin + # default is hard-coded ``False``. + new = False + + #: Some implementations can detect changes to the session and set + #: this when that happens. The mixin default is hard coded to + #: ``True``. + modified = True + + #: Some implementations can detect when session data is read or + #: written and set this when that happens. The mixin default is hard + #: coded to ``True``. + accessed = True + + +# TODO generic when Python > 3.8 +class SecureCookieSession(CallbackDict, SessionMixin): # type: ignore[type-arg] + """Base class for sessions based on signed cookies. + + This session backend will set the :attr:`modified` and + :attr:`accessed` attributes. It cannot reliably track whether a + session is new (vs. empty), so :attr:`new` remains hard coded to + ``False``. + """ + + #: When data is changed, this is set to ``True``. Only the session + #: dictionary itself is tracked; if the session contains mutable + #: data (for example a nested dict) then this must be set to + #: ``True`` manually when modifying that data. The session cookie + #: will only be written to the response if this is ``True``. + modified = False + + #: When data is read or written, this is set to ``True``. Used by + # :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie`` + #: header, which allows caching proxies to cache different pages for + #: different users. + accessed = False + + def __init__(self, initial: t.Any = None) -> None: + def on_update(self: te.Self) -> None: + self.modified = True + self.accessed = True + + super().__init__(initial, on_update) + + def __getitem__(self, key: str) -> t.Any: + self.accessed = True + return super().__getitem__(key) + + def get(self, key: str, default: t.Any = None) -> t.Any: + self.accessed = True + return super().get(key, default) + + def setdefault(self, key: str, default: t.Any = None) -> t.Any: + self.accessed = True + return super().setdefault(key, default) + + +class NullSession(SecureCookieSession): + """Class used to generate nicer error messages if sessions are not + available. Will still allow read-only access to the empty session + but fail on setting. + """ + + def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn: + raise RuntimeError( + "The session is unavailable because no secret " + "key was set. Set the secret_key on the " + "application to something unique and secret." + ) + + __setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail # type: ignore # noqa: B950 + del _fail + + +class SessionInterface: + """The basic interface you have to implement in order to replace the + default session interface which uses werkzeug's securecookie + implementation. The only methods you have to implement are + :meth:`open_session` and :meth:`save_session`, the others have + useful defaults which you don't need to change. + + The session object returned by the :meth:`open_session` method has to + provide a dictionary like interface plus the properties and methods + from the :class:`SessionMixin`. We recommend just subclassing a dict + and adding that mixin:: + + class Session(dict, SessionMixin): + pass + + If :meth:`open_session` returns ``None`` Flask will call into + :meth:`make_null_session` to create a session that acts as replacement + if the session support cannot work because some requirement is not + fulfilled. The default :class:`NullSession` class that is created + will complain that the secret key was not set. + + To replace the session interface on an application all you have to do + is to assign :attr:`flask.Flask.session_interface`:: + + app = Flask(__name__) + app.session_interface = MySessionInterface() + + Multiple requests with the same session may be sent and handled + concurrently. When implementing a new session interface, consider + whether reads or writes to the backing store must be synchronized. + There is no guarantee on the order in which the session for each + request is opened or saved, it will occur in the order that requests + begin and end processing. + + .. versionadded:: 0.8 + """ + + #: :meth:`make_null_session` will look here for the class that should + #: be created when a null session is requested. Likewise the + #: :meth:`is_null_session` method will perform a typecheck against + #: this type. + null_session_class = NullSession + + #: A flag that indicates if the session interface is pickle based. + #: This can be used by Flask extensions to make a decision in regards + #: to how to deal with the session object. + #: + #: .. versionadded:: 0.10 + pickle_based = False + + def make_null_session(self, app: Flask) -> NullSession: + """Creates a null session which acts as a replacement object if the + real session support could not be loaded due to a configuration + error. This mainly aids the user experience because the job of the + null session is to still support lookup without complaining but + modifications are answered with a helpful error message of what + failed. + + This creates an instance of :attr:`null_session_class` by default. + """ + return self.null_session_class() + + def is_null_session(self, obj: object) -> bool: + """Checks if a given object is a null session. Null sessions are + not asked to be saved. + + This checks if the object is an instance of :attr:`null_session_class` + by default. + """ + return isinstance(obj, self.null_session_class) + + def get_cookie_name(self, app: Flask) -> str: + """The name of the session cookie. Uses``app.config["SESSION_COOKIE_NAME"]``.""" + return app.config["SESSION_COOKIE_NAME"] # 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_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 + signer_kwargs = dict( + key_derivation=self.key_derivation, digest_method=self.digest_method + ) + return URLSafeTimedSerializer( + app.secret_key, + salt=self.salt, + serializer=self.serializer, + signer_kwargs=signer_kwargs, + ) + + def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None: + s = self.get_signing_serializer(app) + if s is None: + return None + val = request.cookies.get(self.get_cookie_name(app)) + if not val: + return self.session_class() + max_age = int(app.permanent_session_lifetime.total_seconds()) + try: + data = s.loads(val, max_age=max_age) + return self.session_class(data) + except BadSignature: + return self.session_class() + + def save_session( + self, app: Flask, session: SessionMixin, response: Response + ) -> None: + name = self.get_cookie_name(app) + domain = self.get_cookie_domain(app) + path = self.get_cookie_path(app) + secure = self.get_cookie_secure(app) + samesite = self.get_cookie_samesite(app) + httponly = self.get_cookie_httponly(app) + + # Add a "Vary: Cookie" header if the session was accessed at all. + if session.accessed: + response.vary.add("Cookie") + + # If the session is modified to be empty, remove the cookie. + # If the session is empty, return without setting the cookie. + if not session: + if session.modified: + response.delete_cookie( + name, + domain=domain, + path=path, + secure=secure, + samesite=samesite, + httponly=httponly, + ) + response.vary.add("Cookie") + + return + + if not self.should_set_cookie(app, session): + return + + expires = self.get_expiration_time(app, session) + val = self.get_signing_serializer(app).dumps(dict(session)) # type: ignore + response.set_cookie( + name, + val, # type: ignore + expires=expires, + httponly=httponly, + domain=domain, + path=path, + secure=secure, + samesite=samesite, + ) + response.vary.add("Cookie") diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask/signals.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/signals.py new file mode 100644 index 0000000..444fda9 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/templating.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/templating.py new file mode 100644 index 0000000..618a3b3 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/templating.py @@ -0,0 +1,219 @@ +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. Injects `request`, + `session` and `g`. + """ + appctx = _cv_app.get(None) + reqctx = _cv_request.get(None) + rv: dict[str, t.Any] = {} + if appctx is not None: + rv["g"] = appctx.g + if reqctx is not None: + rv["request"] = reqctx.request + rv["session"] = reqctx.session + return rv + + +class Environment(BaseEnvironment): + """Works like a regular Jinja2 environment but has some additional + knowledge of how Flask's blueprint works so that it can prepend the + name of the blueprint to referenced templates if necessary. + """ + + def __init__(self, app: 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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/testing.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/testing.py new file mode 100644 index 0000000..a27b7c8 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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 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: + sep = b"?" if isinstance(url.query, bytes) else "?" + path += sep + url.query + + self.app = app + super().__init__(path, base_url, *args, **kwargs) + + def json_dumps(self, obj: t.Any, **kwargs: t.Any) -> str: # type: ignore + """Serialize ``obj`` to a JSON-formatted string. + + The serialization will be configured according to the config associated + with this EnvironBuilder's ``app``. + """ + return self.app.json.dumps(obj, **kwargs) + + +_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. + while self._new_contexts: + cm = self._new_contexts.pop() + self._context_stack.enter_context(cm) + + return response + + def __enter__(self) -> FlaskClient: + if self.preserve_context: + raise RuntimeError("Cannot nest client invocations") + self.preserve_context = True + return self + + def __exit__( + self, + exc_type: type | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.preserve_context = False + self._context_stack.close() + + +class FlaskCliRunner(CliRunner): + """A :class:`~click.testing.CliRunner` for testing a Flask app's + CLI commands. Typically created using + :meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`. + """ + + def __init__(self, app: Flask, **kwargs: t.Any) -> None: + self.app = app + super().__init__(**kwargs) + + def invoke( # type: ignore + self, cli: t.Any = None, args: t.Any = None, **kwargs: t.Any + ) -> t.Any: + """Invokes a CLI command in an isolated environment. See + :meth:`CliRunner.invoke ` for + full method documentation. See :ref:`testing-cli` for examples. + + If the ``obj`` argument is not given, passes an instance of + :class:`~flask.cli.ScriptInfo` that knows how to load the Flask + app being tested. + + :param cli: Command object to invoke. Default is the app's + :attr:`~flask.app.Flask.cli` group. + :param args: List of strings to invoke the command with. + + :return: a :class:`~click.testing.Result` object. + """ + if cli is None: + cli = self.app.cli + + if "obj" not in kwargs: + kwargs["obj"] = ScriptInfo(create_app=lambda: self.app) + + return super().invoke(cli, args, **kwargs) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask/typing.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/typing.py new file mode 100644 index 0000000..cf6d4ae --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/typing.py @@ -0,0 +1,90 @@ +from __future__ import annotations + +import typing as t + +if t.TYPE_CHECKING: # pragma: no cover + from _typeshed.wsgi import WSGIApplication # noqa: F401 + from werkzeug.datastructures import Headers # noqa: F401 + from werkzeug.sansio.response import Response # noqa: F401 + +# The possible types that are directly convertible or are a Response object. +ResponseValue = t.Union[ + "Response", + str, + bytes, + t.List[t.Any], + # Only dict is actually accepted, but Mapping allows for TypedDict. + t.Mapping[str, t.Any], + t.Iterator[str], + t.Iterator[bytes], +] + +# the possible types for an individual HTTP header +# This should be a Union, but mypy doesn't pass unless it's a TypeVar. +HeaderValue = t.Union[str, t.List[str], t.Tuple[str, ...]] + +# the possible types for HTTP headers +HeadersValue = t.Union[ + "Headers", + t.Mapping[str, HeaderValue], + t.Sequence[t.Tuple[str, HeaderValue]], +] + +# The possible types returned by a route function. +ResponseReturnValue = t.Union[ + ResponseValue, + t.Tuple[ResponseValue, HeadersValue], + t.Tuple[ResponseValue, int], + t.Tuple[ResponseValue, int, HeadersValue], + "WSGIApplication", +] + +# Allow any subclass of werkzeug.Response, such as the one from Flask, +# as a callback argument. Using werkzeug.Response directly makes a +# callback annotated with flask.Response fail type checking. +ResponseClass = t.TypeVar("ResponseClass", bound="Response") + +AppOrBlueprintKey = t.Optional[str] # The App key is None, whereas blueprints are named +AfterRequestCallable = t.Union[ + t.Callable[[ResponseClass], ResponseClass], + t.Callable[[ResponseClass], t.Awaitable[ResponseClass]], +] +BeforeFirstRequestCallable = t.Union[ + t.Callable[[], None], t.Callable[[], t.Awaitable[None]] +] +BeforeRequestCallable = t.Union[ + t.Callable[[], t.Optional[ResponseReturnValue]], + t.Callable[[], t.Awaitable[t.Optional[ResponseReturnValue]]], +] +ShellContextProcessorCallable = t.Callable[[], t.Dict[str, t.Any]] +TeardownCallable = t.Union[ + t.Callable[[t.Optional[BaseException]], None], + t.Callable[[t.Optional[BaseException]], t.Awaitable[None]], +] +TemplateContextProcessorCallable = t.Union[ + t.Callable[[], t.Dict[str, t.Any]], + t.Callable[[], t.Awaitable[t.Dict[str, t.Any]]], +] +TemplateFilterCallable = t.Callable[..., t.Any] +TemplateGlobalCallable = t.Callable[..., t.Any] +TemplateTestCallable = t.Callable[..., bool] +URLDefaultCallable = t.Callable[[str, t.Dict[str, t.Any]], None] +URLValuePreprocessorCallable = t.Callable[ + [t.Optional[str], t.Optional[t.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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/views.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/views.py new file mode 100644 index 0000000..794fdc0 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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[[F], F]]] = [] + + #: 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) + + 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/flask_auth_app/authenv/lib/python3.10/site-packages/flask/wrappers.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/wrappers.py new file mode 100644 index 0000000..c1eca80 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask/wrappers.py @@ -0,0 +1,174 @@ +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 + + @property + def max_content_length(self) -> int | None: # type: ignore[override] + """Read-only view of the ``MAX_CONTENT_LENGTH`` config key.""" + if current_app: + return current_app.config["MAX_CONTENT_LENGTH"] # type: ignore[no-any-return] + else: + return None + + @property + def endpoint(self) -> str | None: + """The endpoint that matched the request URL. + + This will be ``None`` if matching failed or has not been + performed yet. + + This in combination with :attr:`view_args` can be used to + reconstruct the same URL or a modified URL. + """ + if self.url_rule is not None: + return self.url_rule.endpoint + + return None + + @property + def blueprint(self) -> str | None: + """The registered name of the current blueprint. + + This will be ``None`` if the endpoint is not part of a + blueprint, or if URL matching failed or has not been performed + yet. + + This does not necessarily match the name the blueprint was + created with. It may have been nested, or registered with a + different name. + """ + endpoint = self.endpoint + + if endpoint is not None and "." in endpoint: + return endpoint.rpartition(".")[0] + + return None + + @property + def blueprints(self) -> list[str]: + """The registered names of the current blueprint upwards through + parent blueprints. + + This will be an empty list if there is no current blueprint, or + if URL matching failed. + + .. versionadded:: 2.0.1 + """ + name = self.blueprint + + if name is None: + return [] + + return _split_blueprint_path(name) + + def _load_form_data(self) -> None: + super()._load_form_data() + + # In debug mode we're replacing the files multidict with an ad-hoc + # subclass that raises a different error for key errors. + if ( + current_app + and current_app.debug + and self.mimetype != "multipart/form-data" + and not self.files + ): + from .debughelpers import attach_enctype_error_multidict + + attach_enctype_error_multidict(self) + + def on_json_loading_failed(self, e: ValueError | None) -> t.Any: + try: + return super().on_json_loading_failed(e) + except BadRequest as e: + if current_app and current_app.debug: + raise + + raise BadRequest() from e + + +class Response(ResponseBase): + """The response object that is used by default in Flask. Works like the + response object from Werkzeug but is set to have an HTML mimetype by + default. Quite often you don't have to create this object yourself because + :meth:`~flask.Flask.make_response` will take care of that for you. + + If you want to replace the response object used you can subclass this and + set :attr:`~flask.Flask.response_class` to your subclass. + + .. versionchanged:: 1.0 + JSON support is added to the response, like the request. This is useful + when testing to get the test client response data as JSON. + + .. versionchanged:: 1.0 + + Added :attr:`max_cookie_size`. + """ + + default_mimetype: str | None = "text/html" + + json_module = json + + autocorrect_location_header = False + + @property + def max_cookie_size(self) -> int: # type: ignore + """Read-only view of the :data:`MAX_COOKIE_SIZE` config key. + + See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in + Werkzeug's docs. + """ + if current_app: + return current_app.config["MAX_COOKIE_SIZE"] # type: ignore[no-any-return] + + # return Werkzeug's default when not in an app context + return super().max_cookie_size diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/__about__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/__about__.py new file mode 100644 index 0000000..1918d54 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/__about__.py @@ -0,0 +1,10 @@ +__title__ = "Flask-Login" +__description__ = "User session management for Flask" +__url__ = "https://github.com/maxcountryman/flask-login" +__version_info__ = ("0", "6", "3") +__version__ = ".".join(__version_info__) +__author__ = "Matthew Frazier" +__author_email__ = "leafstormrush@gmail.com" +__maintainer__ = "Max Countryman" +__license__ = "MIT" +__copyright__ = "(c) 2011 by Matthew Frazier" diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/__init__.py new file mode 100644 index 0000000..fbe9c3e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/__init__.py @@ -0,0 +1,94 @@ +from .__about__ import __version__ +from .config import AUTH_HEADER_NAME +from .config import COOKIE_DURATION +from .config import COOKIE_HTTPONLY +from .config import COOKIE_NAME +from .config import COOKIE_SECURE +from .config import ID_ATTRIBUTE +from .config import LOGIN_MESSAGE +from .config import LOGIN_MESSAGE_CATEGORY +from .config import REFRESH_MESSAGE +from .config import REFRESH_MESSAGE_CATEGORY +from .login_manager import LoginManager +from .mixins import AnonymousUserMixin +from .mixins import UserMixin +from .signals import session_protected +from .signals import user_accessed +from .signals import user_loaded_from_cookie +from .signals import user_loaded_from_request +from .signals import user_logged_in +from .signals import user_logged_out +from .signals import user_login_confirmed +from .signals import user_needs_refresh +from .signals import user_unauthorized +from .test_client import FlaskLoginClient +from .utils import confirm_login +from .utils import current_user +from .utils import decode_cookie +from .utils import encode_cookie +from .utils import fresh_login_required +from .utils import login_fresh +from .utils import login_remembered +from .utils import login_required +from .utils import login_url +from .utils import login_user +from .utils import logout_user +from .utils import make_next_param +from .utils import set_login_view + +__all__ = [ + "__version__", + "AUTH_HEADER_NAME", + "COOKIE_DURATION", + "COOKIE_HTTPONLY", + "COOKIE_NAME", + "COOKIE_SECURE", + "ID_ATTRIBUTE", + "LOGIN_MESSAGE", + "LOGIN_MESSAGE_CATEGORY", + "REFRESH_MESSAGE", + "REFRESH_MESSAGE_CATEGORY", + "LoginManager", + "AnonymousUserMixin", + "UserMixin", + "session_protected", + "user_accessed", + "user_loaded_from_cookie", + "user_loaded_from_request", + "user_logged_in", + "user_logged_out", + "user_login_confirmed", + "user_needs_refresh", + "user_unauthorized", + "FlaskLoginClient", + "confirm_login", + "current_user", + "decode_cookie", + "encode_cookie", + "fresh_login_required", + "login_fresh", + "login_remembered", + "login_required", + "login_url", + "login_user", + "logout_user", + "make_next_param", + "set_login_view", +] + + +def __getattr__(name): + if name == "user_loaded_from_header": + import warnings + from .signals import _user_loaded_from_header + + warnings.warn( + "'user_loaded_from_header' is deprecated and will be" + " removed in Flask-Login 0.7. Use" + " 'user_loaded_from_request' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _user_loaded_from_header + + raise AttributeError(name) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/config.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/config.py new file mode 100644 index 0000000..fe2db2c --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/config.py @@ -0,0 +1,55 @@ +from datetime import timedelta + +#: The default name of the "remember me" cookie (``remember_token``) +COOKIE_NAME = "remember_token" + +#: The default time before the "remember me" cookie expires (365 days). +COOKIE_DURATION = timedelta(days=365) + +#: Whether the "remember me" cookie requires Secure; defaults to ``False`` +COOKIE_SECURE = False + +#: Whether the "remember me" cookie uses HttpOnly or not; defaults to ``True`` +COOKIE_HTTPONLY = True + +#: Whether the "remember me" cookie requires same origin; defaults to ``None`` +COOKIE_SAMESITE = None + +#: The default flash message to display when users need to log in. +LOGIN_MESSAGE = "Please log in to access this page." + +#: The default flash message category to display when users need to log in. +LOGIN_MESSAGE_CATEGORY = "message" + +#: The default flash message to display when users need to reauthenticate. +REFRESH_MESSAGE = "Please reauthenticate to access this page." + +#: The default flash message category to display when users need to +#: reauthenticate. +REFRESH_MESSAGE_CATEGORY = "message" + +#: The default attribute to retreive the str id of the user +ID_ATTRIBUTE = "get_id" + +#: Default name of the auth header (``Authorization``) +AUTH_HEADER_NAME = "Authorization" + +#: A set of session keys that are populated by Flask-Login. Use this set to +#: purge keys safely and accurately. +SESSION_KEYS = { + "_user_id", + "_remember", + "_remember_seconds", + "_id", + "_fresh", + "next", +} + +#: A set of HTTP methods which are exempt from `login_required` and +#: `fresh_login_required`. By default, this is just ``OPTIONS``. +EXEMPT_METHODS = {"OPTIONS"} + +#: If true, the page the user is attempting to access is stored in the session +#: rather than a url parameter when redirecting to the login view; defaults to +#: ``False``. +USE_SESSION_FOR_NEXT = False diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/login_manager.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/login_manager.py new file mode 100644 index 0000000..49d0844 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/login_manager.py @@ -0,0 +1,543 @@ +from datetime import datetime +from datetime import timedelta + +from flask import abort +from flask import current_app +from flask import flash +from flask import g +from flask import has_app_context +from flask import redirect +from flask import request +from flask import session + +from .config import AUTH_HEADER_NAME +from .config import COOKIE_DURATION +from .config import COOKIE_HTTPONLY +from .config import COOKIE_NAME +from .config import COOKIE_SAMESITE +from .config import COOKIE_SECURE +from .config import ID_ATTRIBUTE +from .config import LOGIN_MESSAGE +from .config import LOGIN_MESSAGE_CATEGORY +from .config import REFRESH_MESSAGE +from .config import REFRESH_MESSAGE_CATEGORY +from .config import SESSION_KEYS +from .config import USE_SESSION_FOR_NEXT +from .mixins import AnonymousUserMixin +from .signals import session_protected +from .signals import user_accessed +from .signals import user_loaded_from_cookie +from .signals import user_loaded_from_request +from .signals import user_needs_refresh +from .signals import user_unauthorized +from .utils import _create_identifier +from .utils import _user_context_processor +from .utils import decode_cookie +from .utils import encode_cookie +from .utils import expand_login_view +from .utils import login_url as make_login_url +from .utils import make_next_param + + +class LoginManager: + """This object is used to hold the settings used for logging in. Instances + of :class:`LoginManager` are *not* bound to specific apps, so you can + create one in the main body of your code and then bind it to your + app in a factory function. + """ + + def __init__(self, app=None, add_context_processor=True): + #: A class or factory function that produces an anonymous user, which + #: is used when no one is logged in. + self.anonymous_user = AnonymousUserMixin + + #: The name of the view to redirect to when the user needs to log in. + #: (This can be an absolute URL as well, if your authentication + #: machinery is external to your application.) + self.login_view = None + + #: Names of views to redirect to when the user needs to log in, + #: per blueprint. If the key value is set to None the value of + #: :attr:`login_view` will be used instead. + self.blueprint_login_views = {} + + #: The message to flash when a user is redirected to the login page. + self.login_message = LOGIN_MESSAGE + + #: The message category to flash when a user is redirected to the login + #: page. + self.login_message_category = LOGIN_MESSAGE_CATEGORY + + #: The name of the view to redirect to when the user needs to + #: reauthenticate. + self.refresh_view = None + + #: The message to flash when a user is redirected to the 'needs + #: refresh' page. + self.needs_refresh_message = REFRESH_MESSAGE + + #: The message category to flash when a user is redirected to the + #: 'needs refresh' page. + self.needs_refresh_message_category = REFRESH_MESSAGE_CATEGORY + + #: The mode to use session protection in. This can be either + #: ``'basic'`` (the default) or ``'strong'``, or ``None`` to disable + #: it. + self.session_protection = "basic" + + #: If present, used to translate flash messages ``self.login_message`` + #: and ``self.needs_refresh_message`` + self.localize_callback = None + + self.unauthorized_callback = None + + self.needs_refresh_callback = None + + self.id_attribute = ID_ATTRIBUTE + + self._user_callback = None + + self._header_callback = None + + self._request_callback = None + + self._session_identifier_generator = _create_identifier + + if app is not None: + self.init_app(app, add_context_processor) + + def setup_app(self, app, add_context_processor=True): # pragma: no cover + """ + This method has been deprecated. Please use + :meth:`LoginManager.init_app` instead. + """ + import warnings + + warnings.warn( + "'setup_app' is deprecated and will be removed in" + " Flask-Login 0.7. Use 'init_app' instead.", + DeprecationWarning, + stacklevel=2, + ) + self.init_app(app, add_context_processor) + + def init_app(self, app, add_context_processor=True): + """ + Configures an application. This registers an `after_request` call, and + attaches this `LoginManager` to it as `app.login_manager`. + + :param app: The :class:`flask.Flask` object to configure. + :type app: :class:`flask.Flask` + :param add_context_processor: Whether to add a context processor to + the app that adds a `current_user` variable to the template. + Defaults to ``True``. + :type add_context_processor: bool + """ + app.login_manager = self + app.after_request(self._update_remember_cookie) + + if add_context_processor: + app.context_processor(_user_context_processor) + + def unauthorized(self): + """ + This is called when the user is required to log in. If you register a + callback with :meth:`LoginManager.unauthorized_handler`, then it will + be called. Otherwise, it will take the following actions: + + - Flash :attr:`LoginManager.login_message` to the user. + + - If the app is using blueprints find the login view for + the current blueprint using `blueprint_login_views`. If the app + is not using blueprints or the login view for the current + blueprint is not specified use the value of `login_view`. + + - Redirect the user to the login view. (The page they were + attempting to access will be passed in the ``next`` query + string variable, so you can redirect there if present instead + of the homepage. Alternatively, it will be added to the session + as ``next`` if USE_SESSION_FOR_NEXT is set.) + + If :attr:`LoginManager.login_view` is not defined, then it will simply + raise a HTTP 401 (Unauthorized) error instead. + + This should be returned from a view or before/after_request function, + otherwise the redirect will have no effect. + """ + user_unauthorized.send(current_app._get_current_object()) + + if self.unauthorized_callback: + return self.unauthorized_callback() + + if request.blueprint in self.blueprint_login_views: + login_view = self.blueprint_login_views[request.blueprint] + else: + login_view = self.login_view + + if not login_view: + abort(401) + + if self.login_message: + if self.localize_callback is not None: + flash( + self.localize_callback(self.login_message), + category=self.login_message_category, + ) + else: + flash(self.login_message, category=self.login_message_category) + + config = current_app.config + if config.get("USE_SESSION_FOR_NEXT", USE_SESSION_FOR_NEXT): + login_url = expand_login_view(login_view) + session["_id"] = self._session_identifier_generator() + session["next"] = make_next_param(login_url, request.url) + redirect_url = make_login_url(login_view) + else: + redirect_url = make_login_url(login_view, next_url=request.url) + + return redirect(redirect_url) + + def user_loader(self, callback): + """ + This sets the callback for reloading a user from the session. The + function you set should take a user ID (a ``str``) and return a + user object, or ``None`` if the user does not exist. + + :param callback: The callback for retrieving a user object. + :type callback: callable + """ + self._user_callback = callback + return self.user_callback + + @property + def user_callback(self): + """Gets the user_loader callback set by user_loader decorator.""" + return self._user_callback + + def request_loader(self, callback): + """ + This sets the callback for loading a user from a Flask request. + The function you set should take Flask request object and + return a user object, or `None` if the user does not exist. + + :param callback: The callback for retrieving a user object. + :type callback: callable + """ + self._request_callback = callback + return self.request_callback + + @property + def request_callback(self): + """Gets the request_loader callback set by request_loader decorator.""" + return self._request_callback + + def unauthorized_handler(self, callback): + """ + This will set the callback for the `unauthorized` method, which among + other things is used by `login_required`. It takes no arguments, and + should return a response to be sent to the user instead of their + normal view. + + :param callback: The callback for unauthorized users. + :type callback: callable + """ + self.unauthorized_callback = callback + return callback + + def needs_refresh_handler(self, callback): + """ + This will set the callback for the `needs_refresh` method, which among + other things is used by `fresh_login_required`. It takes no arguments, + and should return a response to be sent to the user instead of their + normal view. + + :param callback: The callback for unauthorized users. + :type callback: callable + """ + self.needs_refresh_callback = callback + return callback + + def needs_refresh(self): + """ + This is called when the user is logged in, but they need to be + reauthenticated because their session is stale. If you register a + callback with `needs_refresh_handler`, then it will be called. + Otherwise, it will take the following actions: + + - Flash :attr:`LoginManager.needs_refresh_message` to the user. + + - Redirect the user to :attr:`LoginManager.refresh_view`. (The page + they were attempting to access will be passed in the ``next`` + query string variable, so you can redirect there if present + instead of the homepage.) + + If :attr:`LoginManager.refresh_view` is not defined, then it will + simply raise a HTTP 401 (Unauthorized) error instead. + + This should be returned from a view or before/after_request function, + otherwise the redirect will have no effect. + """ + user_needs_refresh.send(current_app._get_current_object()) + + if self.needs_refresh_callback: + return self.needs_refresh_callback() + + if not self.refresh_view: + abort(401) + + if self.needs_refresh_message: + if self.localize_callback is not None: + flash( + self.localize_callback(self.needs_refresh_message), + category=self.needs_refresh_message_category, + ) + else: + flash( + self.needs_refresh_message, + category=self.needs_refresh_message_category, + ) + + config = current_app.config + if config.get("USE_SESSION_FOR_NEXT", USE_SESSION_FOR_NEXT): + login_url = expand_login_view(self.refresh_view) + session["_id"] = self._session_identifier_generator() + session["next"] = make_next_param(login_url, request.url) + redirect_url = make_login_url(self.refresh_view) + else: + login_url = self.refresh_view + redirect_url = make_login_url(login_url, next_url=request.url) + + return redirect(redirect_url) + + def header_loader(self, callback): + """ + This function has been deprecated. Please use + :meth:`LoginManager.request_loader` instead. + + This sets the callback for loading a user from a header value. + The function you set should take an authentication token and + return a user object, or `None` if the user does not exist. + + :param callback: The callback for retrieving a user object. + :type callback: callable + """ + import warnings + + warnings.warn( + "'header_loader' is deprecated and will be removed in" + " Flask-Login 0.7. Use 'request_loader' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._header_callback = callback + return callback + + def _update_request_context_with_user(self, user=None): + """Store the given user as ctx.user.""" + + if user is None: + user = self.anonymous_user() + + g._login_user = user + + def _load_user(self): + """Loads user from session or remember_me cookie as applicable""" + + if self._user_callback is None and self._request_callback is None: + raise Exception( + "Missing user_loader or request_loader. Refer to " + "http://flask-login.readthedocs.io/#how-it-works " + "for more info." + ) + + user_accessed.send(current_app._get_current_object()) + + # Check SESSION_PROTECTION + if self._session_protection_failed(): + return self._update_request_context_with_user() + + user = None + + # Load user from Flask Session + user_id = session.get("_user_id") + if user_id is not None and self._user_callback is not None: + user = self._user_callback(user_id) + + # Load user from Remember Me Cookie or Request Loader + if user is None: + config = current_app.config + cookie_name = config.get("REMEMBER_COOKIE_NAME", COOKIE_NAME) + header_name = config.get("AUTH_HEADER_NAME", AUTH_HEADER_NAME) + has_cookie = ( + cookie_name in request.cookies and session.get("_remember") != "clear" + ) + if has_cookie: + cookie = request.cookies[cookie_name] + user = self._load_user_from_remember_cookie(cookie) + elif self._request_callback: + user = self._load_user_from_request(request) + elif header_name in request.headers: + header = request.headers[header_name] + user = self._load_user_from_header(header) + + return self._update_request_context_with_user(user) + + def _session_protection_failed(self): + sess = session._get_current_object() + ident = self._session_identifier_generator() + + app = current_app._get_current_object() + mode = app.config.get("SESSION_PROTECTION", self.session_protection) + + if not mode or mode not in ["basic", "strong"]: + return False + + # if the sess is empty, it's an anonymous user or just logged out + # so we can skip this + if sess and ident != sess.get("_id", None): + if mode == "basic" or sess.permanent: + if sess.get("_fresh") is not False: + sess["_fresh"] = False + session_protected.send(app) + return False + elif mode == "strong": + for k in SESSION_KEYS: + sess.pop(k, None) + + sess["_remember"] = "clear" + session_protected.send(app) + return True + + return False + + def _load_user_from_remember_cookie(self, cookie): + user_id = decode_cookie(cookie) + if user_id is not None: + session["_user_id"] = user_id + session["_fresh"] = False + user = None + if self._user_callback: + user = self._user_callback(user_id) + if user is not None: + app = current_app._get_current_object() + user_loaded_from_cookie.send(app, user=user) + return user + return None + + def _load_user_from_header(self, header): + if self._header_callback: + user = self._header_callback(header) + if user is not None: + app = current_app._get_current_object() + + from .signals import _user_loaded_from_header + + _user_loaded_from_header.send(app, user=user) + return user + return None + + def _load_user_from_request(self, request): + if self._request_callback: + user = self._request_callback(request) + if user is not None: + app = current_app._get_current_object() + user_loaded_from_request.send(app, user=user) + return user + return None + + def _update_remember_cookie(self, response): + # Don't modify the session unless there's something to do. + if "_remember" not in session and current_app.config.get( + "REMEMBER_COOKIE_REFRESH_EACH_REQUEST" + ): + session["_remember"] = "set" + + if "_remember" in session: + operation = session.pop("_remember", None) + + if operation == "set" and "_user_id" in session: + self._set_cookie(response) + elif operation == "clear": + self._clear_cookie(response) + + return response + + def _set_cookie(self, response): + # cookie settings + config = current_app.config + cookie_name = config.get("REMEMBER_COOKIE_NAME", COOKIE_NAME) + domain = config.get("REMEMBER_COOKIE_DOMAIN") + path = config.get("REMEMBER_COOKIE_PATH", "/") + + secure = config.get("REMEMBER_COOKIE_SECURE", COOKIE_SECURE) + httponly = config.get("REMEMBER_COOKIE_HTTPONLY", COOKIE_HTTPONLY) + samesite = config.get("REMEMBER_COOKIE_SAMESITE", COOKIE_SAMESITE) + + if "_remember_seconds" in session: + duration = timedelta(seconds=session["_remember_seconds"]) + else: + duration = config.get("REMEMBER_COOKIE_DURATION", COOKIE_DURATION) + + # prepare data + data = encode_cookie(str(session["_user_id"])) + + if isinstance(duration, int): + duration = timedelta(seconds=duration) + + try: + expires = datetime.utcnow() + duration + except TypeError as e: + raise Exception( + "REMEMBER_COOKIE_DURATION must be a datetime.timedelta," + f" instead got: {duration}" + ) from e + + # actually set it + response.set_cookie( + cookie_name, + value=data, + expires=expires, + domain=domain, + path=path, + secure=secure, + httponly=httponly, + samesite=samesite, + ) + + def _clear_cookie(self, response): + config = current_app.config + cookie_name = config.get("REMEMBER_COOKIE_NAME", COOKIE_NAME) + domain = config.get("REMEMBER_COOKIE_DOMAIN") + path = config.get("REMEMBER_COOKIE_PATH", "/") + response.delete_cookie(cookie_name, domain=domain, path=path) + + @property + def _login_disabled(self): + """Legacy property, use app.config['LOGIN_DISABLED'] instead.""" + import warnings + + warnings.warn( + "'_login_disabled' is deprecated and will be removed in" + " Flask-Login 0.7. Use 'LOGIN_DISABLED' in 'app.config'" + " instead.", + DeprecationWarning, + stacklevel=2, + ) + + if has_app_context(): + return current_app.config.get("LOGIN_DISABLED", False) + return False + + @_login_disabled.setter + def _login_disabled(self, newvalue): + """Legacy property setter, use app.config['LOGIN_DISABLED'] instead.""" + import warnings + + warnings.warn( + "'_login_disabled' is deprecated and will be removed in" + " Flask-Login 0.7. Use 'LOGIN_DISABLED' in 'app.config'" + " instead.", + DeprecationWarning, + stacklevel=2, + ) + current_app.config["LOGIN_DISABLED"] = newvalue diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/mixins.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/mixins.py new file mode 100644 index 0000000..0b3a71b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/mixins.py @@ -0,0 +1,65 @@ +class UserMixin: + """ + This provides default implementations for the methods that Flask-Login + expects user objects to have. + """ + + # Python 3 implicitly set __hash__ to None if we override __eq__ + # We set it back to its default implementation + __hash__ = object.__hash__ + + @property + def is_active(self): + return True + + @property + def is_authenticated(self): + return self.is_active + + @property + def is_anonymous(self): + return False + + def get_id(self): + try: + return str(self.id) + except AttributeError: + raise NotImplementedError("No `id` attribute - override `get_id`") from None + + def __eq__(self, other): + """ + Checks the equality of two `UserMixin` objects using `get_id`. + """ + if isinstance(other, UserMixin): + return self.get_id() == other.get_id() + return NotImplemented + + def __ne__(self, other): + """ + Checks the inequality of two `UserMixin` objects using `get_id`. + """ + equal = self.__eq__(other) + if equal is NotImplemented: + return NotImplemented + return not equal + + +class AnonymousUserMixin: + """ + This is the default object for representing an anonymous user. + """ + + @property + def is_authenticated(self): + return False + + @property + def is_active(self): + return False + + @property + def is_anonymous(self): + return True + + def get_id(self): + return diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/signals.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/signals.py new file mode 100644 index 0000000..cf9157f --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/signals.py @@ -0,0 +1,61 @@ +from flask.signals import Namespace + +_signals = Namespace() + +#: Sent when a user is logged in. In addition to the app (which is the +#: sender), it is passed `user`, which is the user being logged in. +user_logged_in = _signals.signal("logged-in") + +#: Sent when a user is logged out. In addition to the app (which is the +#: sender), it is passed `user`, which is the user being logged out. +user_logged_out = _signals.signal("logged-out") + +#: Sent when the user is loaded from the cookie. In addition to the app (which +#: is the sender), it is passed `user`, which is the user being reloaded. +user_loaded_from_cookie = _signals.signal("loaded-from-cookie") + +#: Sent when the user is loaded from the header. In addition to the app (which +#: is the #: sender), it is passed `user`, which is the user being reloaded. +_user_loaded_from_header = _signals.signal("loaded-from-header") + +#: Sent when the user is loaded from the request. In addition to the app (which +#: is the #: sender), it is passed `user`, which is the user being reloaded. +user_loaded_from_request = _signals.signal("loaded-from-request") + +#: Sent when a user's login is confirmed, marking it as fresh. (It is not +#: called for a normal login.) +#: It receives no additional arguments besides the app. +user_login_confirmed = _signals.signal("login-confirmed") + +#: Sent when the `unauthorized` method is called on a `LoginManager`. It +#: receives no additional arguments besides the app. +user_unauthorized = _signals.signal("unauthorized") + +#: Sent when the `needs_refresh` method is called on a `LoginManager`. It +#: receives no additional arguments besides the app. +user_needs_refresh = _signals.signal("needs-refresh") + +#: Sent whenever the user is accessed/loaded +#: receives no additional arguments besides the app. +user_accessed = _signals.signal("accessed") + +#: Sent whenever session protection takes effect, and a session is either +#: marked non-fresh or deleted. It receives no additional arguments besides +#: the app. +session_protected = _signals.signal("session-protected") + + +def __getattr__(name): + if name == "user_loaded_from_header": + import warnings + + warnings.warn( + "'user_loaded_from_header' is deprecated and will be" + " removed in Flask-Login 0.7. Use" + " 'user_loaded_from_request' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _user_loaded_from_header + + raise AttributeError(name) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/test_client.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/test_client.py new file mode 100644 index 0000000..be2a8bf --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/test_client.py @@ -0,0 +1,19 @@ +from flask.testing import FlaskClient + + +class FlaskLoginClient(FlaskClient): + """ + A Flask test client that knows how to log in users + using the Flask-Login extension. + """ + + def __init__(self, *args, **kwargs): + user = kwargs.pop("user", None) + fresh = kwargs.pop("fresh_login", True) + + super().__init__(*args, **kwargs) + + if user: + with self.session_transaction() as sess: + sess["_user_id"] = user.get_id() + sess["_fresh"] = fresh diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/utils.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/utils.py new file mode 100644 index 0000000..37b2056 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_login/utils.py @@ -0,0 +1,415 @@ +import hmac +from functools import wraps +from hashlib import sha512 +from urllib.parse import parse_qs +from urllib.parse import urlencode +from urllib.parse import urlsplit +from urllib.parse import urlunsplit + +from flask import current_app +from flask import g +from flask import has_request_context +from flask import request +from flask import session +from flask import url_for +from werkzeug.local import LocalProxy + +from .config import COOKIE_NAME +from .config import EXEMPT_METHODS +from .signals import user_logged_in +from .signals import user_logged_out +from .signals import user_login_confirmed + +#: A proxy for the current user. If no user is logged in, this will be an +#: anonymous user +current_user = LocalProxy(lambda: _get_user()) + + +def encode_cookie(payload, key=None): + """ + This will encode a ``str`` value into a cookie, and sign that cookie + with the app's secret key. + + :param payload: The value to encode, as `str`. + :type payload: str + + :param key: The key to use when creating the cookie digest. If not + specified, the SECRET_KEY value from app config will be used. + :type key: str + """ + return f"{payload}|{_cookie_digest(payload, key=key)}" + + +def decode_cookie(cookie, key=None): + """ + This decodes a cookie given by `encode_cookie`. If verification of the + cookie fails, ``None`` will be implicitly returned. + + :param cookie: An encoded cookie. + :type cookie: str + + :param key: The key to use when creating the cookie digest. If not + specified, the SECRET_KEY value from app config will be used. + :type key: str + """ + try: + payload, digest = cookie.rsplit("|", 1) + if hasattr(digest, "decode"): + digest = digest.decode("ascii") # pragma: no cover + except ValueError: + return + + if hmac.compare_digest(_cookie_digest(payload, key=key), digest): + return payload + + +def make_next_param(login_url, current_url): + """ + Reduces the scheme and host from a given URL so it can be passed to + the given `login` URL more efficiently. + + :param login_url: The login URL being redirected to. + :type login_url: str + :param current_url: The URL to reduce. + :type current_url: str + """ + l_url = urlsplit(login_url) + c_url = urlsplit(current_url) + + if (not l_url.scheme or l_url.scheme == c_url.scheme) and ( + not l_url.netloc or l_url.netloc == c_url.netloc + ): + return urlunsplit(("", "", c_url.path, c_url.query, "")) + return current_url + + +def expand_login_view(login_view): + """ + Returns the url for the login view, expanding the view name to a url if + needed. + + :param login_view: The name of the login view or a URL for the login view. + :type login_view: str + """ + if login_view.startswith(("https://", "http://", "/")): + return login_view + + return url_for(login_view) + + +def login_url(login_view, next_url=None, next_field="next"): + """ + Creates a URL for redirecting to a login page. If only `login_view` is + provided, this will just return the URL for it. If `next_url` is provided, + however, this will append a ``next=URL`` parameter to the query string + so that the login view can redirect back to that URL. Flask-Login's default + unauthorized handler uses this function when redirecting to your login url. + To force the host name used, set `FORCE_HOST_FOR_REDIRECTS` to a host. This + prevents from redirecting to external sites if request headers Host or + X-Forwarded-For are present. + + :param login_view: The name of the login view. (Alternately, the actual + URL to the login view.) + :type login_view: str + :param next_url: The URL to give the login view for redirection. + :type next_url: str + :param next_field: What field to store the next URL in. (It defaults to + ``next``.) + :type next_field: str + """ + base = expand_login_view(login_view) + + if next_url is None: + return base + + parsed_result = urlsplit(base) + md = parse_qs(parsed_result.query, keep_blank_values=True) + md[next_field] = make_next_param(base, next_url) + netloc = current_app.config.get("FORCE_HOST_FOR_REDIRECTS") or parsed_result.netloc + parsed_result = parsed_result._replace( + netloc=netloc, query=urlencode(md, doseq=True) + ) + return urlunsplit(parsed_result) + + +def login_fresh(): + """ + This returns ``True`` if the current login is fresh. + """ + return session.get("_fresh", False) + + +def login_remembered(): + """ + This returns ``True`` if the current login is remembered across sessions. + """ + config = current_app.config + cookie_name = config.get("REMEMBER_COOKIE_NAME", COOKIE_NAME) + has_cookie = cookie_name in request.cookies and session.get("_remember") != "clear" + if has_cookie: + cookie = request.cookies[cookie_name] + user_id = decode_cookie(cookie) + return user_id is not None + return False + + +def login_user(user, remember=False, duration=None, force=False, fresh=True): + """ + Logs a user in. You should pass the actual user object to this. If the + user's `is_active` property is ``False``, they will not be logged in + unless `force` is ``True``. + + This will return ``True`` if the log in attempt succeeds, and ``False`` if + it fails (i.e. because the user is inactive). + + :param user: The user object to log in. + :type user: object + :param remember: Whether to remember the user after their session expires. + Defaults to ``False``. + :type remember: bool + :param duration: The amount of time before the remember cookie expires. If + ``None`` the value set in the settings is used. Defaults to ``None``. + :type duration: :class:`datetime.timedelta` + :param force: If the user is inactive, setting this to ``True`` will log + them in regardless. Defaults to ``False``. + :type force: bool + :param fresh: setting this to ``False`` will log in the user with a session + marked as not "fresh". Defaults to ``True``. + :type fresh: bool + """ + if not force and not user.is_active: + return False + + user_id = getattr(user, current_app.login_manager.id_attribute)() + session["_user_id"] = user_id + session["_fresh"] = fresh + session["_id"] = current_app.login_manager._session_identifier_generator() + + if remember: + session["_remember"] = "set" + if duration is not None: + try: + # equal to timedelta.total_seconds() but works with Python 2.6 + session["_remember_seconds"] = ( + duration.microseconds + + (duration.seconds + duration.days * 24 * 3600) * 10**6 + ) / 10.0**6 + except AttributeError as e: + raise Exception( + f"duration must be a datetime.timedelta, instead got: {duration}" + ) from e + + current_app.login_manager._update_request_context_with_user(user) + user_logged_in.send(current_app._get_current_object(), user=_get_user()) + return True + + +def logout_user(): + """ + Logs a user out. (You do not need to pass the actual user.) This will + also clean up the remember me cookie if it exists. + """ + + user = _get_user() + + if "_user_id" in session: + session.pop("_user_id") + + if "_fresh" in session: + session.pop("_fresh") + + if "_id" in session: + session.pop("_id") + + cookie_name = current_app.config.get("REMEMBER_COOKIE_NAME", COOKIE_NAME) + if cookie_name in request.cookies: + session["_remember"] = "clear" + if "_remember_seconds" in session: + session.pop("_remember_seconds") + + user_logged_out.send(current_app._get_current_object(), user=user) + + current_app.login_manager._update_request_context_with_user() + return True + + +def confirm_login(): + """ + This sets the current session as fresh. Sessions become stale when they + are reloaded from a cookie. + """ + session["_fresh"] = True + session["_id"] = current_app.login_manager._session_identifier_generator() + user_login_confirmed.send(current_app._get_current_object()) + + +def login_required(func): + """ + If you decorate a view with this, it will ensure that the current user is + logged in and authenticated before calling the actual view. (If they are + not, it calls the :attr:`LoginManager.unauthorized` callback.) For + example:: + + @app.route('/post') + @login_required + def post(): + pass + + If there are only certain times you need to require that your user is + logged in, you can do so with:: + + if not current_user.is_authenticated: + return current_app.login_manager.unauthorized() + + ...which is essentially the code that this function adds to your views. + + It can be convenient to globally turn off authentication when unit testing. + To enable this, if the application configuration variable `LOGIN_DISABLED` + is set to `True`, this decorator will be ignored. + + .. Note :: + + Per `W3 guidelines for CORS preflight requests + `_, + HTTP ``OPTIONS`` requests are exempt from login checks. + + :param func: The view function to decorate. + :type func: function + """ + + @wraps(func) + def decorated_view(*args, **kwargs): + if request.method in EXEMPT_METHODS or current_app.config.get("LOGIN_DISABLED"): + pass + elif not current_user.is_authenticated: + return current_app.login_manager.unauthorized() + + # flask 1.x compatibility + # current_app.ensure_sync is only available in Flask >= 2.0 + if callable(getattr(current_app, "ensure_sync", None)): + return current_app.ensure_sync(func)(*args, **kwargs) + return func(*args, **kwargs) + + return decorated_view + + +def fresh_login_required(func): + """ + If you decorate a view with this, it will ensure that the current user's + login is fresh - i.e. their session was not restored from a 'remember me' + cookie. Sensitive operations, like changing a password or e-mail, should + be protected with this, to impede the efforts of cookie thieves. + + If the user is not authenticated, :meth:`LoginManager.unauthorized` is + called as normal. If they are authenticated, but their session is not + fresh, it will call :meth:`LoginManager.needs_refresh` instead. (In that + case, you will need to provide a :attr:`LoginManager.refresh_view`.) + + Behaves identically to the :func:`login_required` decorator with respect + to configuration variables. + + .. Note :: + + Per `W3 guidelines for CORS preflight requests + `_, + HTTP ``OPTIONS`` requests are exempt from login checks. + + :param func: The view function to decorate. + :type func: function + """ + + @wraps(func) + def decorated_view(*args, **kwargs): + if request.method in EXEMPT_METHODS or current_app.config.get("LOGIN_DISABLED"): + pass + elif not current_user.is_authenticated: + return current_app.login_manager.unauthorized() + elif not login_fresh(): + return current_app.login_manager.needs_refresh() + try: + # current_app.ensure_sync available in Flask >= 2.0 + return current_app.ensure_sync(func)(*args, **kwargs) + except AttributeError: # pragma: no cover + return func(*args, **kwargs) + + return decorated_view + + +def set_login_view(login_view, blueprint=None): + """ + Sets the login view for the app or blueprint. If a blueprint is passed, + the login view is set for this blueprint on ``blueprint_login_views``. + + :param login_view: The user object to log in. + :type login_view: str + :param blueprint: The blueprint which this login view should be set on. + Defaults to ``None``. + :type blueprint: object + """ + + num_login_views = len(current_app.login_manager.blueprint_login_views) + if blueprint is not None or num_login_views != 0: + (current_app.login_manager.blueprint_login_views[blueprint.name]) = login_view + + if ( + current_app.login_manager.login_view is not None + and None not in current_app.login_manager.blueprint_login_views + ): + ( + current_app.login_manager.blueprint_login_views[None] + ) = current_app.login_manager.login_view + + current_app.login_manager.login_view = None + else: + current_app.login_manager.login_view = login_view + + +def _get_user(): + if has_request_context(): + if "_login_user" not in g: + current_app.login_manager._load_user() + + return g._login_user + + return None + + +def _cookie_digest(payload, key=None): + key = _secret_key(key) + + return hmac.new(key, payload.encode("utf-8"), sha512).hexdigest() + + +def _get_remote_addr(): + address = request.headers.get("X-Forwarded-For", request.remote_addr) + if address is not None: + # An 'X-Forwarded-For' header includes a comma separated list of the + # addresses, the first address being the actual remote address. + address = address.encode("utf-8").split(b",")[0].strip() + return address + + +def _create_identifier(): + user_agent = request.headers.get("User-Agent") + if user_agent is not None: + user_agent = user_agent.encode("utf-8") + base = f"{_get_remote_addr()}|{user_agent}" + if str is bytes: + base = str(base, "utf-8", errors="replace") # pragma: no cover + h = sha512() + h.update(base.encode("utf8")) + return h.hexdigest() + + +def _user_context_processor(): + return dict(current_user=_get_user()) + + +def _secret_key(key=None): + if key is None: + key = current_app.config["SECRET_KEY"] + + if isinstance(key, str): # pragma: no cover + key = key.encode("latin1") # ensure bytes + + return key diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/INSTALLER b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/LICENSE.rst b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/LICENSE.rst new file mode 100644 index 0000000..9d227a0 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/METADATA b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/METADATA new file mode 100644 index 0000000..92f239c --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/METADATA @@ -0,0 +1,109 @@ +Metadata-Version: 2.1 +Name: Flask-SQLAlchemy +Version: 3.1.1 +Summary: Add SQLAlchemy support to your Flask application. +Maintainer-email: Pallets +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +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 +Requires-Dist: flask>=2.2.5 +Requires-Dist: sqlalchemy>=2.0.16 +Project-URL: Changes, https://flask-sqlalchemy.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://flask-sqlalchemy.palletsprojects.com +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Issue Tracker, https://github.com/pallets-eco/flask-sqlalchemy/issues/ +Project-URL: Source Code, https://github.com/pallets-eco/flask-sqlalchemy/ + +Flask-SQLAlchemy +================ + +Flask-SQLAlchemy is an extension for `Flask`_ that adds support for +`SQLAlchemy`_ to your application. It aims to simplify using SQLAlchemy +with Flask by providing useful defaults and extra helpers that make it +easier to accomplish common tasks. + +.. _Flask: https://palletsprojects.com/p/flask/ +.. _SQLAlchemy: https://www.sqlalchemy.org + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Flask-SQLAlchemy + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + from flask import Flask + from flask_sqlalchemy import SQLAlchemy + from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column + + app = Flask(__name__) + app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.sqlite" + + class Base(DeclarativeBase): + pass + + db = SQLAlchemy(app, model_class=Base) + + class User(db.Model): + id: Mapped[int] = mapped_column(db.Integer, primary_key=True) + username: Mapped[str] = mapped_column(db.String, unique=True, nullable=False) + + with app.app_context(): + db.create_all() + + db.session.add(User(username="example")) + db.session.commit() + + users = db.session.execute(db.select(User)).scalars() + + +Contributing +------------ + +For guidance on setting up a development environment and how to make a +contribution to Flask-SQLAlchemy, see the `contributing guidelines`_. + +.. _contributing guidelines: https://github.com/pallets-eco/flask-sqlalchemy/blob/main/CONTRIBUTING.rst + + +Donate +------ + +The Pallets organization develops and supports Flask-SQLAlchemy and +other popular packages. In order to grow the community of contributors +and users, and allow the maintainers to devote more time to the +projects, `please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://flask-sqlalchemy.palletsprojects.com/ +- Changes: https://flask-sqlalchemy.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Flask-SQLAlchemy/ +- Source Code: https://github.com/pallets-eco/flask-sqlalchemy/ +- Issue Tracker: https://github.com/pallets-eco/flask-sqlalchemy/issues/ +- Website: https://palletsprojects.com/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/RECORD b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/RECORD new file mode 100644 index 0000000..bf20e0e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/RECORD @@ -0,0 +1,27 @@ +flask_sqlalchemy-3.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +flask_sqlalchemy-3.1.1.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +flask_sqlalchemy-3.1.1.dist-info/METADATA,sha256=lBxR1akBt7n9XBjIVTL2OV52OhCfFrb-Mqtoe0DCbR8,3432 +flask_sqlalchemy-3.1.1.dist-info/RECORD,, +flask_sqlalchemy-3.1.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask_sqlalchemy-3.1.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +flask_sqlalchemy/__init__.py,sha256=he_w4qQQVS2Z1ms5GCTptDTXNOXBXw0n8zSuWCp8n6Y,653 +flask_sqlalchemy/__pycache__/__init__.cpython-310.pyc,, +flask_sqlalchemy/__pycache__/cli.cpython-310.pyc,, +flask_sqlalchemy/__pycache__/extension.cpython-310.pyc,, +flask_sqlalchemy/__pycache__/model.cpython-310.pyc,, +flask_sqlalchemy/__pycache__/pagination.cpython-310.pyc,, +flask_sqlalchemy/__pycache__/query.cpython-310.pyc,, +flask_sqlalchemy/__pycache__/record_queries.cpython-310.pyc,, +flask_sqlalchemy/__pycache__/session.cpython-310.pyc,, +flask_sqlalchemy/__pycache__/table.cpython-310.pyc,, +flask_sqlalchemy/__pycache__/track_modifications.cpython-310.pyc,, +flask_sqlalchemy/cli.py,sha256=pg3QDxP36GW2qnwe_CpPtkRhPchyVSGM6zlBNWuNCFE,484 +flask_sqlalchemy/extension.py,sha256=71tP_kNtb5VgZdafy_OH1sWdZOA6PaT7cJqX7tKgZ-k,38261 +flask_sqlalchemy/model.py,sha256=_mSisC2Eni0TgTyFWeN_O4LIexTeP_sVTdxh03yMK50,11461 +flask_sqlalchemy/pagination.py,sha256=JFpllrqkRkwacb8DAmQWaz9wsvQa0dypfSkhUDSC2ws,11119 +flask_sqlalchemy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask_sqlalchemy/query.py,sha256=Uls9qbmnpb9Vba43EDfsRP17eHJ0X4VG7SE22tH5R3g,3748 +flask_sqlalchemy/record_queries.py,sha256=ouS1ayj16h76LJprx13iYdoFZbm6m8OncrOgAVbG1Sk,3520 +flask_sqlalchemy/session.py,sha256=pBbtN8iDc8yuGVt0k18BvZHh2uEI7QPzZXO7eXrRi1g,3426 +flask_sqlalchemy/table.py,sha256=wAPOy8qwyAxpMwOIUJY4iMOultzz2W0D6xvBkQ7U2CE,859 +flask_sqlalchemy/track_modifications.py,sha256=yieyozj7IiVzwnAGZ-ZrgqrzjrUfG0kPrXBfW_hStSU,2755 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/REQUESTED b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/WHEEL b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.dist-info/WHEEL new file mode 100644 index 0000000..3b5e64b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy-3.1.1.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/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/__init__.py new file mode 100644 index 0000000..c2fa059 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/__init__.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +import typing as t + +from .extension import SQLAlchemy + +__all__ = [ + "SQLAlchemy", +] + + +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-SQLAlchemy 3.2. Use feature detection or" + " 'importlib.metadata.version(\"flask-sqlalchemy\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("flask-sqlalchemy") + + raise AttributeError(name) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/cli.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/cli.py new file mode 100644 index 0000000..d7d7e4b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/cli.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +import typing as t + +from flask import current_app + + +def add_models_to_shell() -> dict[str, t.Any]: + """Registered with :meth:`~flask.Flask.shell_context_processor` if + ``add_models_to_shell`` is enabled. Adds the ``db`` instance and all model classes + to ``flask shell``. + """ + db = current_app.extensions["sqlalchemy"] + out = {m.class_.__name__: m.class_ for m in db.Model._sa_registry.mappers} + out["db"] = db + return out diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/extension.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/extension.py new file mode 100644 index 0000000..43e1b9a --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/extension.py @@ -0,0 +1,1008 @@ +from __future__ import annotations + +import os +import types +import typing as t +import warnings +from weakref import WeakKeyDictionary + +import sqlalchemy as sa +import sqlalchemy.event as sa_event +import sqlalchemy.exc as sa_exc +import sqlalchemy.orm as sa_orm +from flask import abort +from flask import current_app +from flask import Flask +from flask import has_app_context + +from .model import _QueryProperty +from .model import BindMixin +from .model import DefaultMeta +from .model import DefaultMetaNoName +from .model import Model +from .model import NameMixin +from .pagination import Pagination +from .pagination import SelectPagination +from .query import Query +from .session import _app_ctx_id +from .session import Session +from .table import _Table + +_O = t.TypeVar("_O", bound=object) # Based on sqlalchemy.orm._typing.py + + +# Type accepted for model_class argument +_FSA_MCT = t.TypeVar( + "_FSA_MCT", + bound=t.Union[ + t.Type[Model], + sa_orm.DeclarativeMeta, + t.Type[sa_orm.DeclarativeBase], + t.Type[sa_orm.DeclarativeBaseNoMeta], + ], +) + + +# Type returned by make_declarative_base +class _FSAModel(Model): + metadata: sa.MetaData + + +def _get_2x_declarative_bases( + model_class: _FSA_MCT, +) -> list[t.Type[t.Union[sa_orm.DeclarativeBase, sa_orm.DeclarativeBaseNoMeta]]]: + return [ + b + for b in model_class.__bases__ + if issubclass(b, (sa_orm.DeclarativeBase, sa_orm.DeclarativeBaseNoMeta)) + ] + + +class SQLAlchemy: + """Integrates SQLAlchemy with Flask. This handles setting up one or more engines, + associating tables and models with specific engines, and cleaning up connections and + sessions after each request. + + Only the engine configuration is specific to each application, other things like + the model, table, metadata, and session are shared for all applications using that + extension instance. Call :meth:`init_app` to configure the extension on an + application. + + After creating the extension, create model classes by subclassing :attr:`Model`, and + table classes with :attr:`Table`. These can be accessed before :meth:`init_app` is + called, making it possible to define the models separately from the application. + + Accessing :attr:`session` and :attr:`engine` requires an active Flask application + context. This includes methods like :meth:`create_all` which use the engine. + + This class also provides access to names in SQLAlchemy's ``sqlalchemy`` and + ``sqlalchemy.orm`` modules. For example, you can use ``db.Column`` and + ``db.relationship`` instead of importing ``sqlalchemy.Column`` and + ``sqlalchemy.orm.relationship``. This can be convenient when defining models. + + :param app: Call :meth:`init_app` on this Flask application now. + :param metadata: Use this as the default :class:`sqlalchemy.schema.MetaData`. Useful + for setting a naming convention. + :param session_options: Arguments used by :attr:`session` to create each session + instance. A ``scopefunc`` key will be passed to the scoped session, not the + session instance. See :class:`sqlalchemy.orm.sessionmaker` for a list of + arguments. + :param query_class: Use this as the default query class for models and dynamic + relationships. The query interface is considered legacy in SQLAlchemy. + :param model_class: Use this as the model base class when creating the declarative + model class :attr:`Model`. Can also be a fully created declarative model class + for further customization. + :param engine_options: Default arguments used when creating every engine. These are + lower precedence than application config. See :func:`sqlalchemy.create_engine` + for a list of arguments. + :param add_models_to_shell: Add the ``db`` instance and all model classes to + ``flask shell``. + + .. versionchanged:: 3.1.0 + The ``metadata`` parameter can still be used with SQLAlchemy 1.x classes, + but is ignored when using SQLAlchemy 2.x style of declarative classes. + Instead, specify metadata on your Base class. + + .. versionchanged:: 3.1.0 + Added the ``disable_autonaming`` parameter. + + .. versionchanged:: 3.1.0 + Changed ``model_class`` parameter to accepta SQLAlchemy 2.x + declarative base subclass. + + .. versionchanged:: 3.0 + An active Flask application context is always required to access ``session`` and + ``engine``. + + .. versionchanged:: 3.0 + Separate ``metadata`` are used for each bind key. + + .. versionchanged:: 3.0 + The ``engine_options`` parameter is applied as defaults before per-engine + configuration. + + .. versionchanged:: 3.0 + The session class can be customized in ``session_options``. + + .. versionchanged:: 3.0 + Added the ``add_models_to_shell`` parameter. + + .. versionchanged:: 3.0 + Engines are created when calling ``init_app`` rather than the first time they + are accessed. + + .. versionchanged:: 3.0 + All parameters except ``app`` are keyword-only. + + .. versionchanged:: 3.0 + The extension instance is stored directly as ``app.extensions["sqlalchemy"]``. + + .. versionchanged:: 3.0 + Setup methods are renamed with a leading underscore. They are considered + internal interfaces which may change at any time. + + .. versionchanged:: 3.0 + Removed the ``use_native_unicode`` parameter and config. + + .. versionchanged:: 2.4 + Added the ``engine_options`` parameter. + + .. versionchanged:: 2.1 + Added the ``metadata``, ``query_class``, and ``model_class`` parameters. + + .. versionchanged:: 2.1 + Use the same query class across ``session``, ``Model.query`` and + ``Query``. + + .. versionchanged:: 0.16 + ``scopefunc`` is accepted in ``session_options``. + + .. versionchanged:: 0.10 + Added the ``session_options`` parameter. + """ + + def __init__( + self, + app: Flask | None = None, + *, + metadata: sa.MetaData | None = None, + session_options: dict[str, t.Any] | None = None, + query_class: type[Query] = Query, + model_class: _FSA_MCT = Model, # type: ignore[assignment] + engine_options: dict[str, t.Any] | None = None, + add_models_to_shell: bool = True, + disable_autonaming: bool = False, + ): + if session_options is None: + session_options = {} + + self.Query = query_class + """The default query class used by ``Model.query`` and ``lazy="dynamic"`` + relationships. + + .. warning:: + The query interface is considered legacy in SQLAlchemy. + + Customize this by passing the ``query_class`` parameter to the extension. + """ + + self.session = self._make_scoped_session(session_options) + """A :class:`sqlalchemy.orm.scoping.scoped_session` that creates instances of + :class:`.Session` scoped to the current Flask application context. The session + will be removed, returning the engine connection to the pool, when the + application context exits. + + Customize this by passing ``session_options`` to the extension. + + This requires that a Flask application context is active. + + .. versionchanged:: 3.0 + The session is scoped to the current app context. + """ + + self.metadatas: dict[str | None, sa.MetaData] = {} + """Map of bind keys to :class:`sqlalchemy.schema.MetaData` instances. The + ``None`` key refers to the default metadata, and is available as + :attr:`metadata`. + + Customize the default metadata by passing the ``metadata`` parameter to the + extension. This can be used to set a naming convention. When metadata for + another bind key is created, it copies the default's naming convention. + + .. versionadded:: 3.0 + """ + + if metadata is not None: + if len(_get_2x_declarative_bases(model_class)) > 0: + warnings.warn( + "When using SQLAlchemy 2.x style of declarative classes," + " the `metadata` should be an attribute of the base class." + "The metadata passed into SQLAlchemy() is ignored.", + DeprecationWarning, + stacklevel=2, + ) + else: + metadata.info["bind_key"] = None + self.metadatas[None] = metadata + + self.Table = self._make_table_class() + """A :class:`sqlalchemy.schema.Table` class that chooses a metadata + automatically. + + Unlike the base ``Table``, the ``metadata`` argument is not required. If it is + not given, it is selected based on the ``bind_key`` argument. + + :param bind_key: Used to select a different metadata. + :param args: Arguments passed to the base class. These are typically the table's + name, columns, and constraints. + :param kwargs: Arguments passed to the base class. + + .. versionchanged:: 3.0 + This is a subclass of SQLAlchemy's ``Table`` rather than a function. + """ + + self.Model = self._make_declarative_base( + model_class, disable_autonaming=disable_autonaming + ) + """A SQLAlchemy declarative model class. Subclass this to define database + models. + + If a model does not set ``__tablename__``, it will be generated by converting + the class name from ``CamelCase`` to ``snake_case``. It will not be generated + if the model looks like it uses single-table inheritance. + + If a model or parent class sets ``__bind_key__``, it will use that metadata and + database engine. Otherwise, it will use the default :attr:`metadata` and + :attr:`engine`. This is ignored if the model sets ``metadata`` or ``__table__``. + + For code using the SQLAlchemy 1.x API, customize this model by subclassing + :class:`.Model` and passing the ``model_class`` parameter to the extension. + A fully created declarative model class can be + passed as well, to use a custom metaclass. + + For code using the SQLAlchemy 2.x API, customize this model by subclassing + :class:`sqlalchemy.orm.DeclarativeBase` or + :class:`sqlalchemy.orm.DeclarativeBaseNoMeta` + and passing the ``model_class`` parameter to the extension. + """ + + if engine_options is None: + engine_options = {} + + self._engine_options = engine_options + self._app_engines: WeakKeyDictionary[Flask, dict[str | None, sa.engine.Engine]] + self._app_engines = WeakKeyDictionary() + self._add_models_to_shell = add_models_to_shell + + if app is not None: + self.init_app(app) + + def __repr__(self) -> str: + if not has_app_context(): + return f"<{type(self).__name__}>" + + message = f"{type(self).__name__} {self.engine.url}" + + if len(self.engines) > 1: + message = f"{message} +{len(self.engines) - 1}" + + return f"<{message}>" + + def init_app(self, app: Flask) -> None: + """Initialize a Flask application for use with this extension instance. This + must be called before accessing the database engine or session with the app. + + This sets default configuration values, then configures the extension on the + application and creates the engines for each bind key. Therefore, this must be + called after the application has been configured. Changes to application config + after this call will not be reflected. + + The following keys from ``app.config`` are used: + + - :data:`.SQLALCHEMY_DATABASE_URI` + - :data:`.SQLALCHEMY_ENGINE_OPTIONS` + - :data:`.SQLALCHEMY_ECHO` + - :data:`.SQLALCHEMY_BINDS` + - :data:`.SQLALCHEMY_RECORD_QUERIES` + - :data:`.SQLALCHEMY_TRACK_MODIFICATIONS` + + :param app: The Flask application to initialize. + """ + if "sqlalchemy" in app.extensions: + raise RuntimeError( + "A 'SQLAlchemy' instance has already been registered on this Flask app." + " Import and use that instance instead." + ) + + app.extensions["sqlalchemy"] = self + app.teardown_appcontext(self._teardown_session) + + if self._add_models_to_shell: + from .cli import add_models_to_shell + + app.shell_context_processor(add_models_to_shell) + + basic_uri: str | sa.engine.URL | None = app.config.setdefault( + "SQLALCHEMY_DATABASE_URI", None + ) + basic_engine_options = self._engine_options.copy() + basic_engine_options.update( + app.config.setdefault("SQLALCHEMY_ENGINE_OPTIONS", {}) + ) + echo: bool = app.config.setdefault("SQLALCHEMY_ECHO", False) + config_binds: dict[ + str | None, str | sa.engine.URL | dict[str, t.Any] + ] = app.config.setdefault("SQLALCHEMY_BINDS", {}) + engine_options: dict[str | None, dict[str, t.Any]] = {} + + # Build the engine config for each bind key. + for key, value in config_binds.items(): + engine_options[key] = self._engine_options.copy() + + if isinstance(value, (str, sa.engine.URL)): + engine_options[key]["url"] = value + else: + engine_options[key].update(value) + + # Build the engine config for the default bind key. + if basic_uri is not None: + basic_engine_options["url"] = basic_uri + + if "url" in basic_engine_options: + engine_options.setdefault(None, {}).update(basic_engine_options) + + if not engine_options: + raise RuntimeError( + "Either 'SQLALCHEMY_DATABASE_URI' or 'SQLALCHEMY_BINDS' must be set." + ) + + engines = self._app_engines.setdefault(app, {}) + + # Dispose existing engines in case init_app is called again. + if engines: + for engine in engines.values(): + engine.dispose() + + engines.clear() + + # Create the metadata and engine for each bind key. + for key, options in engine_options.items(): + self._make_metadata(key) + options.setdefault("echo", echo) + options.setdefault("echo_pool", echo) + self._apply_driver_defaults(options, app) + engines[key] = self._make_engine(key, options, app) + + if app.config.setdefault("SQLALCHEMY_RECORD_QUERIES", False): + from . import record_queries + + for engine in engines.values(): + record_queries._listen(engine) + + if app.config.setdefault("SQLALCHEMY_TRACK_MODIFICATIONS", False): + from . import track_modifications + + track_modifications._listen(self.session) + + def _make_scoped_session( + self, options: dict[str, t.Any] + ) -> sa_orm.scoped_session[Session]: + """Create a :class:`sqlalchemy.orm.scoping.scoped_session` around the factory + from :meth:`_make_session_factory`. The result is available as :attr:`session`. + + The scope function can be customized using the ``scopefunc`` key in the + ``session_options`` parameter to the extension. By default it uses the current + thread or greenlet id. + + This method is used for internal setup. Its signature may change at any time. + + :meta private: + + :param options: The ``session_options`` parameter from ``__init__``. Keyword + arguments passed to the session factory. A ``scopefunc`` key is popped. + + .. versionchanged:: 3.0 + The session is scoped to the current app context. + + .. versionchanged:: 3.0 + Renamed from ``create_scoped_session``, this method is internal. + """ + scope = options.pop("scopefunc", _app_ctx_id) + factory = self._make_session_factory(options) + return sa_orm.scoped_session(factory, scope) + + def _make_session_factory( + self, options: dict[str, t.Any] + ) -> sa_orm.sessionmaker[Session]: + """Create the SQLAlchemy :class:`sqlalchemy.orm.sessionmaker` used by + :meth:`_make_scoped_session`. + + To customize, pass the ``session_options`` parameter to :class:`SQLAlchemy`. To + customize the session class, subclass :class:`.Session` and pass it as the + ``class_`` key. + + This method is used for internal setup. Its signature may change at any time. + + :meta private: + + :param options: The ``session_options`` parameter from ``__init__``. Keyword + arguments passed to the session factory. + + .. versionchanged:: 3.0 + The session class can be customized. + + .. versionchanged:: 3.0 + Renamed from ``create_session``, this method is internal. + """ + options.setdefault("class_", Session) + options.setdefault("query_cls", self.Query) + return sa_orm.sessionmaker(db=self, **options) + + def _teardown_session(self, exc: BaseException | None) -> None: + """Remove the current session at the end of the request. + + :meta private: + + .. versionadded:: 3.0 + """ + self.session.remove() + + def _make_metadata(self, bind_key: str | None) -> sa.MetaData: + """Get or create a :class:`sqlalchemy.schema.MetaData` for the given bind key. + + This method is used for internal setup. Its signature may change at any time. + + :meta private: + + :param bind_key: The name of the metadata being created. + + .. versionadded:: 3.0 + """ + if bind_key in self.metadatas: + return self.metadatas[bind_key] + + if bind_key is not None: + # Copy the naming convention from the default metadata. + naming_convention = self._make_metadata(None).naming_convention + else: + naming_convention = None + + # Set the bind key in info to be used by session.get_bind. + metadata = sa.MetaData( + naming_convention=naming_convention, info={"bind_key": bind_key} + ) + self.metadatas[bind_key] = metadata + return metadata + + def _make_table_class(self) -> type[_Table]: + """Create a SQLAlchemy :class:`sqlalchemy.schema.Table` class that chooses a + metadata automatically based on the ``bind_key``. The result is available as + :attr:`Table`. + + This method is used for internal setup. Its signature may change at any time. + + :meta private: + + .. versionadded:: 3.0 + """ + + class Table(_Table): + def __new__( + cls, *args: t.Any, bind_key: str | None = None, **kwargs: t.Any + ) -> Table: + # If a metadata arg is passed, go directly to the base Table. Also do + # this for no args so the correct error is shown. + if not args or (len(args) >= 2 and isinstance(args[1], sa.MetaData)): + return super().__new__(cls, *args, **kwargs) + + metadata = self._make_metadata(bind_key) + return super().__new__(cls, *[args[0], metadata, *args[1:]], **kwargs) + + return Table + + def _make_declarative_base( + self, + model_class: _FSA_MCT, + disable_autonaming: bool = False, + ) -> t.Type[_FSAModel]: + """Create a SQLAlchemy declarative model class. The result is available as + :attr:`Model`. + + To customize, subclass :class:`.Model` and pass it as ``model_class`` to + :class:`SQLAlchemy`. To customize at the metaclass level, pass an already + created declarative model class as ``model_class``. + + This method is used for internal setup. Its signature may change at any time. + + :meta private: + + :param model_class: A model base class, or an already created declarative model + class. + + :param disable_autonaming: Turns off automatic tablename generation in models. + + .. versionchanged:: 3.1.0 + Added support for passing SQLAlchemy 2.x base class as model class. + Added optional ``disable_autonaming`` parameter. + + .. versionchanged:: 3.0 + Renamed with a leading underscore, this method is internal. + + .. versionchanged:: 2.3 + ``model`` can be an already created declarative model class. + """ + model: t.Type[_FSAModel] + declarative_bases = _get_2x_declarative_bases(model_class) + if len(declarative_bases) > 1: + # raise error if more than one declarative base is found + raise ValueError( + "Only one declarative base can be passed to SQLAlchemy." + " Got: {}".format(model_class.__bases__) + ) + elif len(declarative_bases) == 1: + body = dict(model_class.__dict__) + body["__fsa__"] = self + mixin_classes = [BindMixin, NameMixin, Model] + if disable_autonaming: + mixin_classes.remove(NameMixin) + model = types.new_class( + "FlaskSQLAlchemyBase", + (*mixin_classes, *model_class.__bases__), + {"metaclass": type(declarative_bases[0])}, + lambda ns: ns.update(body), + ) + elif not isinstance(model_class, sa_orm.DeclarativeMeta): + metadata = self._make_metadata(None) + metaclass = DefaultMetaNoName if disable_autonaming else DefaultMeta + model = sa_orm.declarative_base( + metadata=metadata, cls=model_class, name="Model", metaclass=metaclass + ) + else: + model = model_class # type: ignore[assignment] + + if None not in self.metadatas: + # Use the model's metadata as the default metadata. + model.metadata.info["bind_key"] = None + self.metadatas[None] = model.metadata + else: + # Use the passed in default metadata as the model's metadata. + model.metadata = self.metadatas[None] + + model.query_class = self.Query + model.query = _QueryProperty() # type: ignore[assignment] + model.__fsa__ = self + return model + + def _apply_driver_defaults(self, options: dict[str, t.Any], app: Flask) -> None: + """Apply driver-specific configuration to an engine. + + SQLite in-memory databases use ``StaticPool`` and disable ``check_same_thread``. + File paths are relative to the app's :attr:`~flask.Flask.instance_path`, + which is created if it doesn't exist. + + MySQL sets ``charset="utf8mb4"``, and ``pool_timeout`` defaults to 2 hours. + + This method is used for internal setup. Its signature may change at any time. + + :meta private: + + :param options: Arguments passed to the engine. + :param app: The application that the engine configuration belongs to. + + .. versionchanged:: 3.0 + SQLite paths are relative to ``app.instance_path``. It does not use + ``NullPool`` if ``pool_size`` is 0. Driver-level URIs are supported. + + .. versionchanged:: 3.0 + MySQL sets ``charset="utf8mb4". It does not set ``pool_size`` to 10. It + does not set ``pool_recycle`` if not using a queue pool. + + .. versionchanged:: 3.0 + Renamed from ``apply_driver_hacks``, this method is internal. It does not + return anything. + + .. versionchanged:: 2.5 + Returns ``(sa_url, options)``. + """ + url = sa.engine.make_url(options["url"]) + + if url.drivername in {"sqlite", "sqlite+pysqlite"}: + if url.database is None or url.database in {"", ":memory:"}: + options["poolclass"] = sa.pool.StaticPool + + if "connect_args" not in options: + options["connect_args"] = {} + + options["connect_args"]["check_same_thread"] = False + else: + # the url might look like sqlite:///file:path?uri=true + is_uri = url.query.get("uri", False) + + if is_uri: + db_str = url.database[5:] + else: + db_str = url.database + + if not os.path.isabs(db_str): + os.makedirs(app.instance_path, exist_ok=True) + db_str = os.path.join(app.instance_path, db_str) + + if is_uri: + db_str = f"file:{db_str}" + + options["url"] = url.set(database=db_str) + elif url.drivername.startswith("mysql"): + # set queue defaults only when using queue pool + if ( + "pool_class" not in options + or options["pool_class"] is sa.pool.QueuePool + ): + options.setdefault("pool_recycle", 7200) + + if "charset" not in url.query: + options["url"] = url.update_query_dict({"charset": "utf8mb4"}) + + def _make_engine( + self, bind_key: str | None, options: dict[str, t.Any], app: Flask + ) -> sa.engine.Engine: + """Create the :class:`sqlalchemy.engine.Engine` for the given bind key and app. + + To customize, use :data:`.SQLALCHEMY_ENGINE_OPTIONS` or + :data:`.SQLALCHEMY_BINDS` config. Pass ``engine_options`` to :class:`SQLAlchemy` + to set defaults for all engines. + + This method is used for internal setup. Its signature may change at any time. + + :meta private: + + :param bind_key: The name of the engine being created. + :param options: Arguments passed to the engine. + :param app: The application that the engine configuration belongs to. + + .. versionchanged:: 3.0 + Renamed from ``create_engine``, this method is internal. + """ + return sa.engine_from_config(options, prefix="") + + @property + def metadata(self) -> sa.MetaData: + """The default metadata used by :attr:`Model` and :attr:`Table` if no bind key + is set. + """ + return self.metadatas[None] + + @property + def engines(self) -> t.Mapping[str | None, sa.engine.Engine]: + """Map of bind keys to :class:`sqlalchemy.engine.Engine` instances for current + application. The ``None`` key refers to the default engine, and is available as + :attr:`engine`. + + To customize, set the :data:`.SQLALCHEMY_BINDS` config, and set defaults by + passing the ``engine_options`` parameter to the extension. + + This requires that a Flask application context is active. + + .. versionadded:: 3.0 + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + + if app not in self._app_engines: + raise RuntimeError( + "The current Flask app is not registered with this 'SQLAlchemy'" + " instance. Did you forget to call 'init_app', or did you create" + " multiple 'SQLAlchemy' instances?" + ) + + return self._app_engines[app] + + @property + def engine(self) -> sa.engine.Engine: + """The default :class:`~sqlalchemy.engine.Engine` for the current application, + used by :attr:`session` if the :attr:`Model` or :attr:`Table` being queried does + not set a bind key. + + To customize, set the :data:`.SQLALCHEMY_ENGINE_OPTIONS` config, and set + defaults by passing the ``engine_options`` parameter to the extension. + + This requires that a Flask application context is active. + """ + return self.engines[None] + + def get_engine( + self, bind_key: str | None = None, **kwargs: t.Any + ) -> sa.engine.Engine: + """Get the engine for the given bind key for the current application. + This requires that a Flask application context is active. + + :param bind_key: The name of the engine. + + .. deprecated:: 3.0 + Will be removed in Flask-SQLAlchemy 3.2. Use ``engines[key]`` instead. + + .. versionchanged:: 3.0 + Renamed the ``bind`` parameter to ``bind_key``. Removed the ``app`` + parameter. + """ + warnings.warn( + "'get_engine' is deprecated and will be removed in Flask-SQLAlchemy" + " 3.2. Use 'engine' or 'engines[key]' instead. If you're using" + " Flask-Migrate or Alembic, you'll need to update your 'env.py' file.", + DeprecationWarning, + stacklevel=2, + ) + + if "bind" in kwargs: + bind_key = kwargs.pop("bind") + + return self.engines[bind_key] + + def get_or_404( + self, + entity: type[_O], + ident: t.Any, + *, + description: str | None = None, + **kwargs: t.Any, + ) -> _O: + """Like :meth:`session.get() ` but aborts with a + ``404 Not Found`` error instead of returning ``None``. + + :param entity: The model class to query. + :param ident: The primary key to query. + :param description: A custom message to show on the error page. + :param kwargs: Extra arguments passed to ``session.get()``. + + .. versionchanged:: 3.1 + Pass extra keyword arguments to ``session.get()``. + + .. versionadded:: 3.0 + """ + value = self.session.get(entity, ident, **kwargs) + + if value is None: + abort(404, description=description) + + return value + + def first_or_404( + self, statement: sa.sql.Select[t.Any], *, description: str | None = None + ) -> t.Any: + """Like :meth:`Result.scalar() `, but aborts + with a ``404 Not Found`` error instead of returning ``None``. + + :param statement: The ``select`` statement to execute. + :param description: A custom message to show on the error page. + + .. versionadded:: 3.0 + """ + value = self.session.execute(statement).scalar() + + if value is None: + abort(404, description=description) + + return value + + def one_or_404( + self, statement: sa.sql.Select[t.Any], *, description: str | None = None + ) -> t.Any: + """Like :meth:`Result.scalar_one() `, + but aborts with a ``404 Not Found`` error instead of raising ``NoResultFound`` + or ``MultipleResultsFound``. + + :param statement: The ``select`` statement to execute. + :param description: A custom message to show on the error page. + + .. versionadded:: 3.0 + """ + try: + return self.session.execute(statement).scalar_one() + except (sa_exc.NoResultFound, sa_exc.MultipleResultsFound): + abort(404, description=description) + + def paginate( + self, + select: sa.sql.Select[t.Any], + *, + page: int | None = None, + per_page: int | None = None, + max_per_page: int | None = None, + error_out: bool = True, + count: bool = True, + ) -> Pagination: + """Apply an offset and limit to a select statment based on the current page and + number of items per page, returning a :class:`.Pagination` object. + + The statement should select a model class, like ``select(User)``. This applies + ``unique()`` and ``scalars()`` modifiers to the result, so compound selects will + not return the expected results. + + :param select: The ``select`` statement to paginate. + :param page: The current page, used to calculate the offset. Defaults to the + ``page`` query arg during a request, or 1 otherwise. + :param per_page: The maximum number of items on a page, used to calculate the + offset and limit. Defaults to the ``per_page`` query arg during a request, + or 20 otherwise. + :param max_per_page: The maximum allowed value for ``per_page``, to limit a + user-provided value. Use ``None`` for no limit. Defaults to 100. + :param error_out: Abort with a ``404 Not Found`` error if no items are returned + and ``page`` is not 1, or if ``page`` or ``per_page`` is less than 1, or if + either are not ints. + :param count: Calculate the total number of values by issuing an extra count + query. For very complex queries this may be inaccurate or slow, so it can be + disabled and set manually if necessary. + + .. versionchanged:: 3.0 + The ``count`` query is more efficient. + + .. versionadded:: 3.0 + """ + return SelectPagination( + select=select, + session=self.session(), + page=page, + per_page=per_page, + max_per_page=max_per_page, + error_out=error_out, + count=count, + ) + + def _call_for_binds( + self, bind_key: str | None | list[str | None], op_name: str + ) -> None: + """Call a method on each metadata. + + :meta private: + + :param bind_key: A bind key or list of keys. Defaults to all binds. + :param op_name: The name of the method to call. + + .. versionchanged:: 3.0 + Renamed from ``_execute_for_all_tables``. + """ + if bind_key == "__all__": + keys: list[str | None] = list(self.metadatas) + elif bind_key is None or isinstance(bind_key, str): + keys = [bind_key] + else: + keys = bind_key + + for key in keys: + try: + engine = self.engines[key] + except KeyError: + message = f"Bind key '{key}' is not in 'SQLALCHEMY_BINDS' config." + + if key is None: + message = f"'SQLALCHEMY_DATABASE_URI' config is not set. {message}" + + raise sa_exc.UnboundExecutionError(message) from None + + metadata = self.metadatas[key] + getattr(metadata, op_name)(bind=engine) + + def create_all(self, bind_key: str | None | list[str | None] = "__all__") -> None: + """Create tables that do not exist in the database by calling + ``metadata.create_all()`` for all or some bind keys. This does not + update existing tables, use a migration library for that. + + This requires that a Flask application context is active. + + :param bind_key: A bind key or list of keys to create the tables for. Defaults + to all binds. + + .. versionchanged:: 3.0 + Renamed the ``bind`` parameter to ``bind_key``. Removed the ``app`` + parameter. + + .. versionchanged:: 0.12 + Added the ``bind`` and ``app`` parameters. + """ + self._call_for_binds(bind_key, "create_all") + + def drop_all(self, bind_key: str | None | list[str | None] = "__all__") -> None: + """Drop tables by calling ``metadata.drop_all()`` for all or some bind keys. + + This requires that a Flask application context is active. + + :param bind_key: A bind key or list of keys to drop the tables from. Defaults to + all binds. + + .. versionchanged:: 3.0 + Renamed the ``bind`` parameter to ``bind_key``. Removed the ``app`` + parameter. + + .. versionchanged:: 0.12 + Added the ``bind`` and ``app`` parameters. + """ + self._call_for_binds(bind_key, "drop_all") + + def reflect(self, bind_key: str | None | list[str | None] = "__all__") -> None: + """Load table definitions from the database by calling ``metadata.reflect()`` + for all or some bind keys. + + This requires that a Flask application context is active. + + :param bind_key: A bind key or list of keys to reflect the tables from. Defaults + to all binds. + + .. versionchanged:: 3.0 + Renamed the ``bind`` parameter to ``bind_key``. Removed the ``app`` + parameter. + + .. versionchanged:: 0.12 + Added the ``bind`` and ``app`` parameters. + """ + self._call_for_binds(bind_key, "reflect") + + def _set_rel_query(self, kwargs: dict[str, t.Any]) -> None: + """Apply the extension's :attr:`Query` class as the default for relationships + and backrefs. + + :meta private: + """ + kwargs.setdefault("query_class", self.Query) + + if "backref" in kwargs: + backref = kwargs["backref"] + + if isinstance(backref, str): + backref = (backref, {}) + + backref[1].setdefault("query_class", self.Query) + + def relationship( + self, *args: t.Any, **kwargs: t.Any + ) -> sa_orm.RelationshipProperty[t.Any]: + """A :func:`sqlalchemy.orm.relationship` that applies this extension's + :attr:`Query` class for dynamic relationships and backrefs. + + .. versionchanged:: 3.0 + The :attr:`Query` class is set on ``backref``. + """ + self._set_rel_query(kwargs) + return sa_orm.relationship(*args, **kwargs) + + def dynamic_loader( + self, argument: t.Any, **kwargs: t.Any + ) -> sa_orm.RelationshipProperty[t.Any]: + """A :func:`sqlalchemy.orm.dynamic_loader` that applies this extension's + :attr:`Query` class for relationships and backrefs. + + .. versionchanged:: 3.0 + The :attr:`Query` class is set on ``backref``. + """ + self._set_rel_query(kwargs) + return sa_orm.dynamic_loader(argument, **kwargs) + + def _relation( + self, *args: t.Any, **kwargs: t.Any + ) -> sa_orm.RelationshipProperty[t.Any]: + """A :func:`sqlalchemy.orm.relationship` that applies this extension's + :attr:`Query` class for dynamic relationships and backrefs. + + SQLAlchemy 2.0 removes this name, use ``relationship`` instead. + + :meta private: + + .. versionchanged:: 3.0 + The :attr:`Query` class is set on ``backref``. + """ + self._set_rel_query(kwargs) + f = sa_orm.relationship + return f(*args, **kwargs) + + def __getattr__(self, name: str) -> t.Any: + if name == "relation": + return self._relation + + if name == "event": + return sa_event + + if name.startswith("_"): + raise AttributeError(name) + + for mod in (sa, sa_orm): + if hasattr(mod, name): + return getattr(mod, name) + + raise AttributeError(name) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/model.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/model.py new file mode 100644 index 0000000..c6f9e5a --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/model.py @@ -0,0 +1,330 @@ +from __future__ import annotations + +import re +import typing as t + +import sqlalchemy as sa +import sqlalchemy.orm as sa_orm + +from .query import Query + +if t.TYPE_CHECKING: + from .extension import SQLAlchemy + + +class _QueryProperty: + """A class property that creates a query object for a model. + + :meta private: + """ + + def __get__(self, obj: Model | None, cls: type[Model]) -> Query: + return cls.query_class( + cls, session=cls.__fsa__.session() # type: ignore[arg-type] + ) + + +class Model: + """The base class of the :attr:`.SQLAlchemy.Model` declarative model class. + + To define models, subclass :attr:`db.Model <.SQLAlchemy.Model>`, not this. To + customize ``db.Model``, subclass this and pass it as ``model_class`` to + :class:`.SQLAlchemy`. To customize ``db.Model`` at the metaclass level, pass an + already created declarative model class as ``model_class``. + """ + + __fsa__: t.ClassVar[SQLAlchemy] + """Internal reference to the extension object. + + :meta private: + """ + + query_class: t.ClassVar[type[Query]] = Query + """Query class used by :attr:`query`. Defaults to :attr:`.SQLAlchemy.Query`, which + defaults to :class:`.Query`. + """ + + query: t.ClassVar[Query] = _QueryProperty() # type: ignore[assignment] + """A SQLAlchemy query for a model. Equivalent to ``db.session.query(Model)``. Can be + customized per-model by overriding :attr:`query_class`. + + .. warning:: + The query interface is considered legacy in SQLAlchemy. Prefer using + ``session.execute(select())`` instead. + """ + + def __repr__(self) -> str: + state = sa.inspect(self) + assert state is not None + + if state.transient: + pk = f"(transient {id(self)})" + elif state.pending: + pk = f"(pending {id(self)})" + else: + pk = ", ".join(map(str, state.identity)) + + return f"<{type(self).__name__} {pk}>" + + +class BindMetaMixin(type): + """Metaclass mixin that sets a model's ``metadata`` based on its ``__bind_key__``. + + If the model sets ``metadata`` or ``__table__`` directly, ``__bind_key__`` is + ignored. If the ``metadata`` is the same as the parent model, it will not be set + directly on the child model. + """ + + __fsa__: SQLAlchemy + metadata: sa.MetaData + + def __init__( + cls, name: str, bases: tuple[type, ...], d: dict[str, t.Any], **kwargs: t.Any + ) -> None: + if not ("metadata" in cls.__dict__ or "__table__" in cls.__dict__): + bind_key = getattr(cls, "__bind_key__", None) + parent_metadata = getattr(cls, "metadata", None) + metadata = cls.__fsa__._make_metadata(bind_key) + + if metadata is not parent_metadata: + cls.metadata = metadata + + super().__init__(name, bases, d, **kwargs) + + +class BindMixin: + """DeclarativeBase mixin to set a model's ``metadata`` based on ``__bind_key__``. + + If no ``__bind_key__`` is specified, the model will use the default metadata + provided by ``DeclarativeBase`` or ``DeclarativeBaseNoMeta``. + If the model doesn't set ``metadata`` or ``__table__`` directly + and does set ``__bind_key__``, the model will use the metadata + for the specified bind key. + If the ``metadata`` is the same as the parent model, it will not be set + directly on the child model. + + .. versionchanged:: 3.1.0 + """ + + __fsa__: SQLAlchemy + metadata: sa.MetaData + + @classmethod + def __init_subclass__(cls: t.Type[BindMixin], **kwargs: t.Dict[str, t.Any]) -> None: + if not ("metadata" in cls.__dict__ or "__table__" in cls.__dict__) and hasattr( + cls, "__bind_key__" + ): + bind_key = getattr(cls, "__bind_key__", None) + parent_metadata = getattr(cls, "metadata", None) + metadata = cls.__fsa__._make_metadata(bind_key) + + if metadata is not parent_metadata: + cls.metadata = metadata + + super().__init_subclass__(**kwargs) + + +class NameMetaMixin(type): + """Metaclass mixin that sets a model's ``__tablename__`` by converting the + ``CamelCase`` class name to ``snake_case``. A name is set for non-abstract models + that do not otherwise define ``__tablename__``. If a model does not define a primary + key, it will not generate a name or ``__table__``, for single-table inheritance. + """ + + metadata: sa.MetaData + __tablename__: str + __table__: sa.Table + + def __init__( + cls, name: str, bases: tuple[type, ...], d: dict[str, t.Any], **kwargs: t.Any + ) -> None: + if should_set_tablename(cls): + cls.__tablename__ = camel_to_snake_case(cls.__name__) + + super().__init__(name, bases, d, **kwargs) + + # __table_cls__ has run. If no table was created, use the parent table. + if ( + "__tablename__" not in cls.__dict__ + and "__table__" in cls.__dict__ + and cls.__dict__["__table__"] is None + ): + del cls.__table__ + + def __table_cls__(cls, *args: t.Any, **kwargs: t.Any) -> sa.Table | None: + """This is called by SQLAlchemy during mapper setup. It determines the final + table object that the model will use. + + If no primary key is found, that indicates single-table inheritance, so no table + will be created and ``__tablename__`` will be unset. + """ + schema = kwargs.get("schema") + + if schema is None: + key = args[0] + else: + key = f"{schema}.{args[0]}" + + # Check if a table with this name already exists. Allows reflected tables to be + # applied to models by name. + if key in cls.metadata.tables: + return sa.Table(*args, **kwargs) + + # If a primary key is found, create a table for joined-table inheritance. + for arg in args: + if (isinstance(arg, sa.Column) and arg.primary_key) or isinstance( + arg, sa.PrimaryKeyConstraint + ): + return sa.Table(*args, **kwargs) + + # If no base classes define a table, return one that's missing a primary key + # so SQLAlchemy shows the correct error. + for base in cls.__mro__[1:-1]: + if "__table__" in base.__dict__: + break + else: + return sa.Table(*args, **kwargs) + + # Single-table inheritance, use the parent table name. __init__ will unset + # __table__ based on this. + if "__tablename__" in cls.__dict__: + del cls.__tablename__ + + return None + + +class NameMixin: + """DeclarativeBase mixin that sets a model's ``__tablename__`` by converting the + ``CamelCase`` class name to ``snake_case``. A name is set for non-abstract models + that do not otherwise define ``__tablename__``. If a model does not define a primary + key, it will not generate a name or ``__table__``, for single-table inheritance. + + .. versionchanged:: 3.1.0 + """ + + metadata: sa.MetaData + __tablename__: str + __table__: sa.Table + + @classmethod + def __init_subclass__(cls: t.Type[NameMixin], **kwargs: t.Dict[str, t.Any]) -> None: + if should_set_tablename(cls): + cls.__tablename__ = camel_to_snake_case(cls.__name__) + + super().__init_subclass__(**kwargs) + + # __table_cls__ has run. If no table was created, use the parent table. + if ( + "__tablename__" not in cls.__dict__ + and "__table__" in cls.__dict__ + and cls.__dict__["__table__"] is None + ): + del cls.__table__ + + @classmethod + def __table_cls__(cls, *args: t.Any, **kwargs: t.Any) -> sa.Table | None: + """This is called by SQLAlchemy during mapper setup. It determines the final + table object that the model will use. + + If no primary key is found, that indicates single-table inheritance, so no table + will be created and ``__tablename__`` will be unset. + """ + schema = kwargs.get("schema") + + if schema is None: + key = args[0] + else: + key = f"{schema}.{args[0]}" + + # Check if a table with this name already exists. Allows reflected tables to be + # applied to models by name. + if key in cls.metadata.tables: + return sa.Table(*args, **kwargs) + + # If a primary key is found, create a table for joined-table inheritance. + for arg in args: + if (isinstance(arg, sa.Column) and arg.primary_key) or isinstance( + arg, sa.PrimaryKeyConstraint + ): + return sa.Table(*args, **kwargs) + + # If no base classes define a table, return one that's missing a primary key + # so SQLAlchemy shows the correct error. + for base in cls.__mro__[1:-1]: + if "__table__" in base.__dict__: + break + else: + return sa.Table(*args, **kwargs) + + # Single-table inheritance, use the parent table name. __init__ will unset + # __table__ based on this. + if "__tablename__" in cls.__dict__: + del cls.__tablename__ + + return None + + +def should_set_tablename(cls: type) -> bool: + """Determine whether ``__tablename__`` should be generated for a model. + + - If no class in the MRO sets a name, one should be generated. + - If a declared attr is found, it should be used instead. + - If a name is found, it should be used if the class is a mixin, otherwise one + should be generated. + - Abstract models should not have one generated. + + Later, ``__table_cls__`` will determine if the model looks like single or + joined-table inheritance. If no primary key is found, the name will be unset. + """ + if ( + cls.__dict__.get("__abstract__", False) + or ( + not issubclass(cls, (sa_orm.DeclarativeBase, sa_orm.DeclarativeBaseNoMeta)) + and not any(isinstance(b, sa_orm.DeclarativeMeta) for b in cls.__mro__[1:]) + ) + or any( + (b is sa_orm.DeclarativeBase or b is sa_orm.DeclarativeBaseNoMeta) + for b in cls.__bases__ + ) + ): + return False + + for base in cls.__mro__: + if "__tablename__" not in base.__dict__: + continue + + if isinstance(base.__dict__["__tablename__"], sa_orm.declared_attr): + return False + + return not ( + base is cls + or base.__dict__.get("__abstract__", False) + or not ( + # SQLAlchemy 1.x + isinstance(base, sa_orm.DeclarativeMeta) + # 2.x: DeclarativeBas uses this as metaclass + or isinstance(base, sa_orm.decl_api.DeclarativeAttributeIntercept) + # 2.x: DeclarativeBaseNoMeta doesn't use a metaclass + or issubclass(base, sa_orm.DeclarativeBaseNoMeta) + ) + ) + + return True + + +def camel_to_snake_case(name: str) -> str: + """Convert a ``CamelCase`` name to ``snake_case``.""" + name = re.sub(r"((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))", r"_\1", name) + return name.lower().lstrip("_") + + +class DefaultMeta(BindMetaMixin, NameMetaMixin, sa_orm.DeclarativeMeta): + """SQLAlchemy declarative metaclass that provides ``__bind_key__`` and + ``__tablename__`` support. + """ + + +class DefaultMetaNoName(BindMetaMixin, sa_orm.DeclarativeMeta): + """SQLAlchemy declarative metaclass that provides ``__bind_key__`` and + ``__tablename__`` support. + """ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/pagination.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/pagination.py new file mode 100644 index 0000000..3d49d6e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/pagination.py @@ -0,0 +1,364 @@ +from __future__ import annotations + +import typing as t +from math import ceil + +import sqlalchemy as sa +import sqlalchemy.orm as sa_orm +from flask import abort +from flask import request + + +class Pagination: + """Apply an offset and limit to the query based on the current page and number of + items per page. + + Don't create pagination objects manually. They are created by + :meth:`.SQLAlchemy.paginate` and :meth:`.Query.paginate`. + + This is a base class, a subclass must implement :meth:`_query_items` and + :meth:`_query_count`. Those methods will use arguments passed as ``kwargs`` to + perform the queries. + + :param page: The current page, used to calculate the offset. Defaults to the + ``page`` query arg during a request, or 1 otherwise. + :param per_page: The maximum number of items on a page, used to calculate the + offset and limit. Defaults to the ``per_page`` query arg during a request, + or 20 otherwise. + :param max_per_page: The maximum allowed value for ``per_page``, to limit a + user-provided value. Use ``None`` for no limit. Defaults to 100. + :param error_out: Abort with a ``404 Not Found`` error if no items are returned + and ``page`` is not 1, or if ``page`` or ``per_page`` is less than 1, or if + either are not ints. + :param count: Calculate the total number of values by issuing an extra count + query. For very complex queries this may be inaccurate or slow, so it can be + disabled and set manually if necessary. + :param kwargs: Information about the query to paginate. Different subclasses will + require different arguments. + + .. versionchanged:: 3.0 + Iterating over a pagination object iterates over its items. + + .. versionchanged:: 3.0 + Creating instances manually is not a public API. + """ + + def __init__( + self, + page: int | None = None, + per_page: int | None = None, + max_per_page: int | None = 100, + error_out: bool = True, + count: bool = True, + **kwargs: t.Any, + ) -> None: + self._query_args = kwargs + page, per_page = self._prepare_page_args( + page=page, + per_page=per_page, + max_per_page=max_per_page, + error_out=error_out, + ) + + self.page: int = page + """The current page.""" + + self.per_page: int = per_page + """The maximum number of items on a page.""" + + self.max_per_page: int | None = max_per_page + """The maximum allowed value for ``per_page``.""" + + items = self._query_items() + + if not items and page != 1 and error_out: + abort(404) + + self.items: list[t.Any] = items + """The items on the current page. Iterating over the pagination object is + equivalent to iterating over the items. + """ + + if count: + total = self._query_count() + else: + total = None + + self.total: int | None = total + """The total number of items across all pages.""" + + @staticmethod + def _prepare_page_args( + *, + page: int | None = None, + per_page: int | None = None, + max_per_page: int | None = None, + error_out: bool = True, + ) -> tuple[int, int]: + if request: + if page is None: + try: + page = int(request.args.get("page", 1)) + except (TypeError, ValueError): + if error_out: + abort(404) + + page = 1 + + if per_page is None: + try: + per_page = int(request.args.get("per_page", 20)) + except (TypeError, ValueError): + if error_out: + abort(404) + + per_page = 20 + else: + if page is None: + page = 1 + + if per_page is None: + per_page = 20 + + if max_per_page is not None: + per_page = min(per_page, max_per_page) + + if page < 1: + if error_out: + abort(404) + else: + page = 1 + + if per_page < 1: + if error_out: + abort(404) + else: + per_page = 20 + + return page, per_page + + @property + def _query_offset(self) -> int: + """The index of the first item to query, passed to ``offset()``. + + :meta private: + + .. versionadded:: 3.0 + """ + return (self.page - 1) * self.per_page + + def _query_items(self) -> list[t.Any]: + """Execute the query to get the items on the current page. + + Uses init arguments stored in :attr:`_query_args`. + + :meta private: + + .. versionadded:: 3.0 + """ + raise NotImplementedError + + def _query_count(self) -> int: + """Execute the query to get the total number of items. + + Uses init arguments stored in :attr:`_query_args`. + + :meta private: + + .. versionadded:: 3.0 + """ + raise NotImplementedError + + @property + def first(self) -> int: + """The number of the first item on the page, starting from 1, or 0 if there are + no items. + + .. versionadded:: 3.0 + """ + if len(self.items) == 0: + return 0 + + return (self.page - 1) * self.per_page + 1 + + @property + def last(self) -> int: + """The number of the last item on the page, starting from 1, inclusive, or 0 if + there are no items. + + .. versionadded:: 3.0 + """ + first = self.first + return max(first, first + len(self.items) - 1) + + @property + def pages(self) -> int: + """The total number of pages.""" + if self.total == 0 or self.total is None: + return 0 + + return ceil(self.total / self.per_page) + + @property + def has_prev(self) -> bool: + """``True`` if this is not the first page.""" + return self.page > 1 + + @property + def prev_num(self) -> int | None: + """The previous page number, or ``None`` if this is the first page.""" + if not self.has_prev: + return None + + return self.page - 1 + + def prev(self, *, error_out: bool = False) -> Pagination: + """Query the :class:`Pagination` object for the previous page. + + :param error_out: Abort with a ``404 Not Found`` error if no items are returned + and ``page`` is not 1, or if ``page`` or ``per_page`` is less than 1, or if + either are not ints. + """ + p = type(self)( + page=self.page - 1, + per_page=self.per_page, + error_out=error_out, + count=False, + **self._query_args, + ) + p.total = self.total + return p + + @property + def has_next(self) -> bool: + """``True`` if this is not the last page.""" + return self.page < self.pages + + @property + def next_num(self) -> int | None: + """The next page number, or ``None`` if this is the last page.""" + if not self.has_next: + return None + + return self.page + 1 + + def next(self, *, error_out: bool = False) -> Pagination: + """Query the :class:`Pagination` object for the next page. + + :param error_out: Abort with a ``404 Not Found`` error if no items are returned + and ``page`` is not 1, or if ``page`` or ``per_page`` is less than 1, or if + either are not ints. + """ + p = type(self)( + page=self.page + 1, + per_page=self.per_page, + max_per_page=self.max_per_page, + error_out=error_out, + count=False, + **self._query_args, + ) + p.total = self.total + return p + + def iter_pages( + self, + *, + left_edge: int = 2, + left_current: int = 2, + right_current: int = 4, + right_edge: int = 2, + ) -> t.Iterator[int | None]: + """Yield page numbers for a pagination widget. Skipped pages between the edges + and middle are represented by a ``None``. + + For example, if there are 20 pages and the current page is 7, the following + values are yielded. + + .. code-block:: python + + 1, 2, None, 5, 6, 7, 8, 9, 10, 11, None, 19, 20 + + :param left_edge: How many pages to show from the first page. + :param left_current: How many pages to show left of the current page. + :param right_current: How many pages to show right of the current page. + :param right_edge: How many pages to show from the last page. + + .. versionchanged:: 3.0 + Improved efficiency of calculating what to yield. + + .. versionchanged:: 3.0 + ``right_current`` boundary is inclusive. + + .. versionchanged:: 3.0 + All parameters are keyword-only. + """ + pages_end = self.pages + 1 + + if pages_end == 1: + return + + left_end = min(1 + left_edge, pages_end) + yield from range(1, left_end) + + if left_end == pages_end: + return + + mid_start = max(left_end, self.page - left_current) + mid_end = min(self.page + right_current + 1, pages_end) + + if mid_start - left_end > 0: + yield None + + yield from range(mid_start, mid_end) + + if mid_end == pages_end: + return + + right_start = max(mid_end, pages_end - right_edge) + + if right_start - mid_end > 0: + yield None + + yield from range(right_start, pages_end) + + def __iter__(self) -> t.Iterator[t.Any]: + yield from self.items + + +class SelectPagination(Pagination): + """Returned by :meth:`.SQLAlchemy.paginate`. Takes ``select`` and ``session`` + arguments in addition to the :class:`Pagination` arguments. + + .. versionadded:: 3.0 + """ + + def _query_items(self) -> list[t.Any]: + select = self._query_args["select"] + select = select.limit(self.per_page).offset(self._query_offset) + session = self._query_args["session"] + return list(session.execute(select).unique().scalars()) + + def _query_count(self) -> int: + select = self._query_args["select"] + sub = select.options(sa_orm.lazyload("*")).order_by(None).subquery() + session = self._query_args["session"] + out = session.execute(sa.select(sa.func.count()).select_from(sub)).scalar() + return out # type: ignore[no-any-return] + + +class QueryPagination(Pagination): + """Returned by :meth:`.Query.paginate`. Takes a ``query`` argument in addition to + the :class:`Pagination` arguments. + + .. versionadded:: 3.0 + """ + + def _query_items(self) -> list[t.Any]: + query = self._query_args["query"] + out = query.limit(self.per_page).offset(self._query_offset).all() + return out # type: ignore[no-any-return] + + def _query_count(self) -> int: + # Query.count automatically disables eager loads + out = self._query_args["query"].order_by(None).count() + return out # type: ignore[no-any-return] diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/py.typed b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/query.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/query.py new file mode 100644 index 0000000..35f927d --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/query.py @@ -0,0 +1,105 @@ +from __future__ import annotations + +import typing as t + +import sqlalchemy.exc as sa_exc +import sqlalchemy.orm as sa_orm +from flask import abort + +from .pagination import Pagination +from .pagination import QueryPagination + + +class Query(sa_orm.Query): # type: ignore[type-arg] + """SQLAlchemy :class:`~sqlalchemy.orm.query.Query` subclass with some extra methods + useful for querying in a web application. + + This is the default query class for :attr:`.Model.query`. + + .. versionchanged:: 3.0 + Renamed to ``Query`` from ``BaseQuery``. + """ + + def get_or_404(self, ident: t.Any, description: str | None = None) -> t.Any: + """Like :meth:`~sqlalchemy.orm.Query.get` but aborts with a ``404 Not Found`` + error instead of returning ``None``. + + :param ident: The primary key to query. + :param description: A custom message to show on the error page. + """ + rv = self.get(ident) + + if rv is None: + abort(404, description=description) + + return rv + + def first_or_404(self, description: str | None = None) -> t.Any: + """Like :meth:`~sqlalchemy.orm.Query.first` but aborts with a ``404 Not Found`` + error instead of returning ``None``. + + :param description: A custom message to show on the error page. + """ + rv = self.first() + + if rv is None: + abort(404, description=description) + + return rv + + def one_or_404(self, description: str | None = None) -> t.Any: + """Like :meth:`~sqlalchemy.orm.Query.one` but aborts with a ``404 Not Found`` + error instead of raising ``NoResultFound`` or ``MultipleResultsFound``. + + :param description: A custom message to show on the error page. + + .. versionadded:: 3.0 + """ + try: + return self.one() + except (sa_exc.NoResultFound, sa_exc.MultipleResultsFound): + abort(404, description=description) + + def paginate( + self, + *, + page: int | None = None, + per_page: int | None = None, + max_per_page: int | None = None, + error_out: bool = True, + count: bool = True, + ) -> Pagination: + """Apply an offset and limit to the query based on the current page and number + of items per page, returning a :class:`.Pagination` object. + + :param page: The current page, used to calculate the offset. Defaults to the + ``page`` query arg during a request, or 1 otherwise. + :param per_page: The maximum number of items on a page, used to calculate the + offset and limit. Defaults to the ``per_page`` query arg during a request, + or 20 otherwise. + :param max_per_page: The maximum allowed value for ``per_page``, to limit a + user-provided value. Use ``None`` for no limit. Defaults to 100. + :param error_out: Abort with a ``404 Not Found`` error if no items are returned + and ``page`` is not 1, or if ``page`` or ``per_page`` is less than 1, or if + either are not ints. + :param count: Calculate the total number of values by issuing an extra count + query. For very complex queries this may be inaccurate or slow, so it can be + disabled and set manually if necessary. + + .. versionchanged:: 3.0 + All parameters are keyword-only. + + .. versionchanged:: 3.0 + The ``count`` query is more efficient. + + .. versionchanged:: 3.0 + ``max_per_page`` defaults to 100. + """ + return QueryPagination( + query=self, + page=page, + per_page=per_page, + max_per_page=max_per_page, + error_out=error_out, + count=count, + ) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/record_queries.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/record_queries.py new file mode 100644 index 0000000..e8273be --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/record_queries.py @@ -0,0 +1,117 @@ +from __future__ import annotations + +import dataclasses +import inspect +import typing as t +from time import perf_counter + +import sqlalchemy as sa +import sqlalchemy.event as sa_event +from flask import current_app +from flask import g +from flask import has_app_context + + +def get_recorded_queries() -> list[_QueryInfo]: + """Get the list of recorded query information for the current session. Queries are + recorded if the config :data:`.SQLALCHEMY_RECORD_QUERIES` is enabled. + + Each query info object has the following attributes: + + ``statement`` + The string of SQL generated by SQLAlchemy with parameter placeholders. + ``parameters`` + The parameters sent with the SQL statement. + ``start_time`` / ``end_time`` + Timing info about when the query started execution and when the results where + returned. Accuracy and value depends on the operating system. + ``duration`` + The time the query took in seconds. + ``location`` + A string description of where in your application code the query was executed. + This may not be possible to calculate, and the format is not stable. + + .. versionchanged:: 3.0 + Renamed from ``get_debug_queries``. + + .. versionchanged:: 3.0 + The info object is a dataclass instead of a tuple. + + .. versionchanged:: 3.0 + The info object attribute ``context`` is renamed to ``location``. + + .. versionchanged:: 3.0 + Not enabled automatically in debug or testing mode. + """ + return g.get("_sqlalchemy_queries", []) # type: ignore[no-any-return] + + +@dataclasses.dataclass +class _QueryInfo: + """Information about an executed query. Returned by :func:`get_recorded_queries`. + + .. versionchanged:: 3.0 + Renamed from ``_DebugQueryTuple``. + + .. versionchanged:: 3.0 + Changed to a dataclass instead of a tuple. + + .. versionchanged:: 3.0 + ``context`` is renamed to ``location``. + """ + + statement: str | None + parameters: t.Any + start_time: float + end_time: float + location: str + + @property + def duration(self) -> float: + return self.end_time - self.start_time + + +def _listen(engine: sa.engine.Engine) -> None: + sa_event.listen(engine, "before_cursor_execute", _record_start, named=True) + sa_event.listen(engine, "after_cursor_execute", _record_end, named=True) + + +def _record_start(context: sa.engine.ExecutionContext, **kwargs: t.Any) -> None: + if not has_app_context(): + return + + context._fsa_start_time = perf_counter() # type: ignore[attr-defined] + + +def _record_end(context: sa.engine.ExecutionContext, **kwargs: t.Any) -> None: + if not has_app_context(): + return + + if "_sqlalchemy_queries" not in g: + g._sqlalchemy_queries = [] + + import_top = current_app.import_name.partition(".")[0] + import_dot = f"{import_top}." + frame = inspect.currentframe() + + while frame: + name = frame.f_globals.get("__name__") + + if name and (name == import_top or name.startswith(import_dot)): + code = frame.f_code + location = f"{code.co_filename}:{frame.f_lineno} ({code.co_name})" + break + + frame = frame.f_back + else: + location = "" + + g._sqlalchemy_queries.append( + _QueryInfo( + statement=context.statement, + parameters=context.parameters, + start_time=context._fsa_start_time, # type: ignore[attr-defined] + end_time=perf_counter(), + location=location, + ) + ) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/session.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/session.py new file mode 100644 index 0000000..631fffa --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/session.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +import typing as t + +import sqlalchemy as sa +import sqlalchemy.exc as sa_exc +import sqlalchemy.orm as sa_orm +from flask.globals import app_ctx + +if t.TYPE_CHECKING: + from .extension import SQLAlchemy + + +class Session(sa_orm.Session): + """A SQLAlchemy :class:`~sqlalchemy.orm.Session` class that chooses what engine to + use based on the bind key associated with the metadata associated with the thing + being queried. + + To customize ``db.session``, subclass this and pass it as the ``class_`` key in the + ``session_options`` to :class:`.SQLAlchemy`. + + .. versionchanged:: 3.0 + Renamed from ``SignallingSession``. + """ + + def __init__(self, db: SQLAlchemy, **kwargs: t.Any) -> None: + super().__init__(**kwargs) + self._db = db + self._model_changes: dict[object, tuple[t.Any, str]] = {} + + def get_bind( + self, + mapper: t.Any | None = None, + clause: t.Any | None = None, + bind: sa.engine.Engine | sa.engine.Connection | None = None, + **kwargs: t.Any, + ) -> sa.engine.Engine | sa.engine.Connection: + """Select an engine based on the ``bind_key`` of the metadata associated with + the model or table being queried. If no bind key is set, uses the default bind. + + .. versionchanged:: 3.0.3 + Fix finding the bind for a joined inheritance model. + + .. versionchanged:: 3.0 + The implementation more closely matches the base SQLAlchemy implementation. + + .. versionchanged:: 2.1 + Support joining an external transaction. + """ + if bind is not None: + return bind + + engines = self._db.engines + + if mapper is not None: + try: + mapper = sa.inspect(mapper) + except sa_exc.NoInspectionAvailable as e: + if isinstance(mapper, type): + raise sa_orm.exc.UnmappedClassError(mapper) from e + + raise + + engine = _clause_to_engine(mapper.local_table, engines) + + if engine is not None: + return engine + + if clause is not None: + engine = _clause_to_engine(clause, engines) + + if engine is not None: + return engine + + if None in engines: + return engines[None] + + return super().get_bind(mapper=mapper, clause=clause, bind=bind, **kwargs) + + +def _clause_to_engine( + clause: sa.ClauseElement | None, + engines: t.Mapping[str | None, sa.engine.Engine], +) -> sa.engine.Engine | None: + """If the clause is a table, return the engine associated with the table's + metadata's bind key. + """ + table = None + + if clause is not None: + if isinstance(clause, sa.Table): + table = clause + elif isinstance(clause, sa.UpdateBase) and isinstance(clause.table, sa.Table): + table = clause.table + + if table is not None and "bind_key" in table.metadata.info: + key = table.metadata.info["bind_key"] + + if key not in engines: + raise sa_exc.UnboundExecutionError( + f"Bind key '{key}' is not in 'SQLALCHEMY_BINDS' config." + ) + + return engines[key] + + return None + + +def _app_ctx_id() -> int: + """Get the id of the current Flask application context for the session scope.""" + return id(app_ctx._get_current_object()) # type: ignore[attr-defined] diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/table.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/table.py new file mode 100644 index 0000000..ab08a69 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/table.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +import typing as t + +import sqlalchemy as sa +import sqlalchemy.sql.schema as sa_sql_schema + + +class _Table(sa.Table): + @t.overload + def __init__( + self, + name: str, + *args: sa_sql_schema.SchemaItem, + bind_key: str | None = None, + **kwargs: t.Any, + ) -> None: + ... + + @t.overload + def __init__( + self, + name: str, + metadata: sa.MetaData, + *args: sa_sql_schema.SchemaItem, + **kwargs: t.Any, + ) -> None: + ... + + @t.overload + def __init__( + self, name: str, *args: sa_sql_schema.SchemaItem, **kwargs: t.Any + ) -> None: + ... + + def __init__( + self, name: str, *args: sa_sql_schema.SchemaItem, **kwargs: t.Any + ) -> None: + super().__init__(name, *args, **kwargs) # type: ignore[arg-type] diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/track_modifications.py b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/track_modifications.py new file mode 100644 index 0000000..7028b65 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/flask_sqlalchemy/track_modifications.py @@ -0,0 +1,88 @@ +from __future__ import annotations + +import typing as t + +import sqlalchemy as sa +import sqlalchemy.event as sa_event +import sqlalchemy.orm as sa_orm +from flask import current_app +from flask import has_app_context +from flask.signals import Namespace # type: ignore[attr-defined] + +if t.TYPE_CHECKING: + from .session import Session + +_signals = Namespace() + +models_committed = _signals.signal("models-committed") +"""This Blinker signal is sent after the session is committed if there were changed +models in the session. + +The sender is the application that emitted the changes. The receiver is passed the +``changes`` argument with a list of tuples in the form ``(instance, operation)``. +The operations are ``"insert"``, ``"update"``, and ``"delete"``. +""" + +before_models_committed = _signals.signal("before-models-committed") +"""This signal works exactly like :data:`models_committed` but is emitted before the +commit takes place. +""" + + +def _listen(session: sa_orm.scoped_session[Session]) -> None: + sa_event.listen(session, "before_flush", _record_ops, named=True) + sa_event.listen(session, "before_commit", _record_ops, named=True) + sa_event.listen(session, "before_commit", _before_commit) + sa_event.listen(session, "after_commit", _after_commit) + sa_event.listen(session, "after_rollback", _after_rollback) + + +def _record_ops(session: Session, **kwargs: t.Any) -> None: + if not has_app_context(): + return + + if not current_app.config["SQLALCHEMY_TRACK_MODIFICATIONS"]: + return + + for targets, operation in ( + (session.new, "insert"), + (session.dirty, "update"), + (session.deleted, "delete"), + ): + for target in targets: + state = sa.inspect(target) + key = state.identity_key if state.has_identity else id(target) + session._model_changes[key] = (target, operation) + + +def _before_commit(session: Session) -> None: + if not has_app_context(): + return + + app = current_app._get_current_object() # type: ignore[attr-defined] + + if not app.config["SQLALCHEMY_TRACK_MODIFICATIONS"]: + return + + if session._model_changes: + changes = list(session._model_changes.values()) + before_models_committed.send(app, changes=changes) + + +def _after_commit(session: Session) -> None: + if not has_app_context(): + return + + app = current_app._get_current_object() # type: ignore[attr-defined] + + if not app.config["SQLALCHEMY_TRACK_MODIFICATIONS"]: + return + + if session._model_changes: + changes = list(session._model_changes.values()) + models_committed.send(app, changes=changes) + session._model_changes.clear() + + +def _after_rollback(session: Session) -> None: + session._model_changes.clear() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/AUTHORS b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/AUTHORS new file mode 100644 index 0000000..42a5c22 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/AUTHORS @@ -0,0 +1,51 @@ +Original Authors +---------------- +* Armin Rigo +* Christian Tismer + +Contributors +------------ +* Al Stone +* Alexander Schmidt +* Alexey Borzenkov +* Andreas Schwab +* Armin Ronacher +* Bin Wang +* Bob Ippolito +* ChangBo Guo +* Christoph Gohlke +* Denis Bilenko +* Dirk Mueller +* Donovan Preston +* Fantix King +* Floris Bruynooghe +* Fredrik Fornwall +* Gerd Woetzel +* Giel van Schijndel +* Gökhan Karabulut +* Gustavo Niemeyer +* Guy Rozendorn +* Hye-Shik Chang +* Jared Kuolt +* Jason Madden +* Josh Snyder +* Kyle Ambroff +* Laszlo Boszormenyi +* Mao Han +* Marc Abramowitz +* Marc Schlaich +* Marcin Bachry +* Matt Madison +* Matt Turner +* Michael Ellerman +* Michael Matz +* Ralf Schmitt +* Robie Basak +* Ronny Pfannschmidt +* Samual M. Rushing +* Tony Bowles +* Tony Breeds +* Trevor Bowen +* Tulio Magno Quites Machado Filho +* Ulrich Weigand +* Victor Stinner diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/INSTALLER b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/LICENSE b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/LICENSE new file mode 100644 index 0000000..b73a4a1 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/LICENSE @@ -0,0 +1,30 @@ +The following files are derived from Stackless Python and are subject to the +same license as Stackless Python: + + src/greenlet/slp_platformselect.h + files in src/greenlet/platform/ directory + +See LICENSE.PSF and http://www.stackless.com/ for details. + +Unless otherwise noted, the files in greenlet have been released under the +following MIT license: + +Copyright (c) Armin Rigo, Christian Tismer and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/LICENSE.PSF b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/LICENSE.PSF new file mode 100644 index 0000000..d3b509a --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/LICENSE.PSF @@ -0,0 +1,47 @@ +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011 Python Software Foundation; All Rights Reserved" are retained in Python +alone or in any derivative version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/METADATA b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/METADATA new file mode 100644 index 0000000..e87d0ab --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/METADATA @@ -0,0 +1,102 @@ +Metadata-Version: 2.1 +Name: greenlet +Version: 3.0.3 +Summary: Lightweight in-process concurrent programming +Home-page: https://greenlet.readthedocs.io/ +Author: Alexey Borzenkov +Author-email: snaury@gmail.com +Maintainer: Jason Madden +Maintainer-email: jason@seecoresoftware.com +License: MIT License +Project-URL: Bug Tracker, https://github.com/python-greenlet/greenlet/issues +Project-URL: Source Code, https://github.com/python-greenlet/greenlet/ +Project-URL: Documentation, https://greenlet.readthedocs.io/ +Keywords: greenlet coroutine concurrency threads cooperative +Platform: any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Natural Language :: English +Classifier: Programming Language :: C +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +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 :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Operating System :: OS Independent +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: LICENSE.PSF +License-File: AUTHORS +Provides-Extra: docs +Requires-Dist: Sphinx ; extra == 'docs' +Requires-Dist: furo ; extra == 'docs' +Provides-Extra: test +Requires-Dist: objgraph ; extra == 'test' +Requires-Dist: psutil ; extra == 'test' + +.. This file is included into docs/history.rst + + +Greenlets are lightweight coroutines for in-process concurrent +programming. + +The "greenlet" package is a spin-off of `Stackless`_, a version of +CPython that supports micro-threads called "tasklets". Tasklets run +pseudo-concurrently (typically in a single or a few OS-level threads) +and are synchronized with data exchanges on "channels". + +A "greenlet", on the other hand, is a still more primitive notion of +micro-thread with no implicit scheduling; coroutines, in other words. +This is useful when you want to control exactly when your code runs. +You can build custom scheduled micro-threads on top of greenlet; +however, it seems that greenlets are useful on their own as a way to +make advanced control flow structures. For example, we can recreate +generators; the difference with Python's own generators is that our +generators can call nested functions and the nested functions can +yield values too. (Additionally, you don't need a "yield" keyword. See +the example in `test_generator.py +`_). + +Greenlets are provided as a C extension module for the regular unmodified +interpreter. + +.. _`Stackless`: http://www.stackless.com + + +Who is using Greenlet? +====================== + +There are several libraries that use Greenlet as a more flexible +alternative to Python's built in coroutine support: + + - `Concurrence`_ + - `Eventlet`_ + - `Gevent`_ + +.. _Concurrence: http://opensource.hyves.org/concurrence/ +.. _Eventlet: http://eventlet.net/ +.. _Gevent: http://www.gevent.org/ + +Getting Greenlet +================ + +The easiest way to get Greenlet is to install it with pip:: + + pip install greenlet + + +Source code archives and binary distributions are available on the +python package index at https://pypi.org/project/greenlet + +The source code repository is hosted on github: +https://github.com/python-greenlet/greenlet + +Documentation is available on readthedocs.org: +https://greenlet.readthedocs.io diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/RECORD b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/RECORD new file mode 100644 index 0000000..2de8cf5 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/RECORD @@ -0,0 +1,116 @@ +../../../include/site/python3.10/greenlet/greenlet.h,sha256=sz5pYRSQqedgOt2AMgxLZdTjO-qcr_JMvgiEJR9IAJ8,4755 +greenlet-3.0.3.dist-info/AUTHORS,sha256=swW28t2knVRxRkaEQNZtO7MP9Sgnompb7B6cNgJM8Gk,849 +greenlet-3.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +greenlet-3.0.3.dist-info/LICENSE,sha256=dpgx1uXfrywggC-sz_H6-0wgJd2PYlPfpH_K1Z1NCXk,1434 +greenlet-3.0.3.dist-info/LICENSE.PSF,sha256=5f88I8EQ5JTNfXNsEP2W1GJFe6_soxCEDbZScpjH1Gs,2424 +greenlet-3.0.3.dist-info/METADATA,sha256=CHtHlitUM_AS9hKoJfYLF3Vz-UFJlqRnhbRl2-1JrjU,3779 +greenlet-3.0.3.dist-info/RECORD,, +greenlet-3.0.3.dist-info/WHEEL,sha256=wdSTXT5qc3DkoPdNKvC6DLnEMpfV8rNTDQK58jgLBrU,153 +greenlet-3.0.3.dist-info/top_level.txt,sha256=YSnRsCRoO61JGlP57o8iKL6rdLWDWuiyKD8ekpWUsDc,9 +greenlet/TBrokenGreenlet.cpp,sha256=YgKaHkQV6_dKBrgS0HKDSqZroskv0IwSZDo4bsiwz3w,1029 +greenlet/TExceptionState.cpp,sha256=Ctg2YfyEYNjOYbteRB_oIJa9lNGyC7N1F3h4XqqQdg8,1367 +greenlet/TGreenlet.cpp,sha256=1xwAzGNqO68AZ4D5lD5DHmGPBohM6nv4BYnLatgIL68,25637 +greenlet/TGreenletGlobals.cpp,sha256=qLi1icS1UDSbefTkolz9TycEi_GOUblsEznMp0HFywQ,3268 +greenlet/TMainGreenlet.cpp,sha256=FvWtGJDKb64DLy0n-ddcTF6xJDwczPMKSm9mXSsHJKg,3365 +greenlet/TPythonState.cpp,sha256=QUoIQzF0HYmAJO_nwX5gXSSlMNL1mkxlN24KJCXIrIQ,14861 +greenlet/TStackState.cpp,sha256=VclDR-qiMeJjuiJxL9_u24MJiTgdSaYvr8bWQdTEZjY,7389 +greenlet/TThreadStateDestroy.cpp,sha256=EqZ-GjksrWNC20CY_P0yXN43wVRMYEh659SmRRqBaI4,7214 +greenlet/TUserGreenlet.cpp,sha256=b_Bmh4WZdS6I1yM2AfHRtd535WovtpYMkpfu2GQpaDs,23618 +greenlet/__init__.py,sha256=Dw4tovn18bpPaWQ4SK7jDJe24uV4ao264UfaT0uufxU,1723 +greenlet/__pycache__/__init__.cpython-310.pyc,, +greenlet/_greenlet.cpython-310-x86_64-linux-gnu.so,sha256=l6Xr3KJN-eyuMyubUgL5qNbA23C_xmrz2jqRR67rD_M,1498632 +greenlet/greenlet.cpp,sha256=k9RZolayY79WgjPXwcA3Vcv48MuW7TAtogIZPaDD3gM,48815 +greenlet/greenlet.h,sha256=sz5pYRSQqedgOt2AMgxLZdTjO-qcr_JMvgiEJR9IAJ8,4755 +greenlet/greenlet_allocator.hpp,sha256=kxyWW4Qdwlrc7ufgdb5vd6Y7jhauQ699Kod0mqiO1iM,1582 +greenlet/greenlet_compiler_compat.hpp,sha256=m7wvwrZqBoCQpDMTP-Z7whdXIES7e3AuXBgvPHSsfxg,4140 +greenlet/greenlet_cpython_add_pending.hpp,sha256=apAwIhGlgYrnYn03zWL6Sxy68kltDeb1e0QupZfb3DQ,6043 +greenlet/greenlet_cpython_compat.hpp,sha256=ZpN8gewZeOtd6T-mLidA7zteQ_P4vG8T1za_KPvCijg,3621 +greenlet/greenlet_exceptions.hpp,sha256=Dt8YdaQn8AK9nBfwU9rrDoMlR2Lw5aLTQV6ZAsHmfsw,3683 +greenlet/greenlet_greenlet.hpp,sha256=Ct_EAx4OJL6FvF5g3jV1ybSxnqzLVaRdPi2EcYT1iq4,27728 +greenlet/greenlet_internal.hpp,sha256=ZXH5zemWCN8wH8zAqMUGycvz_3IulRL6Gf2hZA6CknE,2703 +greenlet/greenlet_refs.hpp,sha256=ECkHKV1CVamtzmWWGKXXMpw8lXLeIzastXM9tfqlsNI,33864 +greenlet/greenlet_slp_switch.hpp,sha256=kM1QHA2iV-gH4cFyN6lfIagHQxvJZjWOVJdIxRE3TlQ,3198 +greenlet/greenlet_thread_state.hpp,sha256=0UwJCNd86ifwM2yDd3QrNmHAECL-eNADHubwiB_XGA4,20614 +greenlet/greenlet_thread_state_dict_cleanup.hpp,sha256=tEN0rI1pZiEsdtr7Oda24gr52fGiHnYTLyM8Vme3Gns,3831 +greenlet/greenlet_thread_support.hpp,sha256=XUJ6ljWjf9OYyuOILiz8e_yHvT3fbaUiHdhiPNQUV4s,867 +greenlet/platform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +greenlet/platform/__pycache__/__init__.cpython-310.pyc,, +greenlet/platform/setup_switch_x64_masm.cmd,sha256=ZpClUJeU0ujEPSTWNSepP0W2f9XiYQKA8QKSoVou8EU,143 +greenlet/platform/switch_aarch64_gcc.h,sha256=GKC0yWNXnbK2X--X6aguRCMj2Tg7hDU1Zkl3RljDvC8,4307 +greenlet/platform/switch_alpha_unix.h,sha256=Z-SvF8JQV3oxWT8JRbL9RFu4gRFxPdJ7cviM8YayMmw,671 +greenlet/platform/switch_amd64_unix.h,sha256=EcSFCBlodEBhqhKjcJqY_5Dn_jn7pKpkJlOvp7gFXLI,2748 +greenlet/platform/switch_arm32_gcc.h,sha256=Z3KkHszdgq6uU4YN3BxvKMG2AdDnovwCCNrqGWZ1Lyo,2479 +greenlet/platform/switch_arm32_ios.h,sha256=mm5_R9aXB92hyxzFRwB71M60H6AlvHjrpTrc72Pz3l8,1892 +greenlet/platform/switch_arm64_masm.asm,sha256=4kpTtfy7rfcr8j1CpJLAK21EtZpGDAJXWRU68HEy5A8,1245 +greenlet/platform/switch_arm64_masm.obj,sha256=DmLnIB_icoEHAz1naue_pJPTZgR9ElM7-Nmztr-o9_U,746 +greenlet/platform/switch_arm64_msvc.h,sha256=RqK5MHLmXI3Q-FQ7tm32KWnbDNZKnkJdq8CR89cz640,398 +greenlet/platform/switch_csky_gcc.h,sha256=kDikyiPpewP71KoBZQO_MukDTXTXBiC7x-hF0_2DL0w,1331 +greenlet/platform/switch_loongarch64_linux.h,sha256=7M-Dhc4Q8tRbJCJhalDLwU6S9Mx8MjmN1RbTDgIvQTM,779 +greenlet/platform/switch_m68k_gcc.h,sha256=VSa6NpZhvyyvF-Q58CTIWSpEDo4FKygOyTz00whctlw,928 +greenlet/platform/switch_mips_unix.h,sha256=E0tYsqc5anDY1BhenU1l8DW-nVHC_BElzLgJw3TGtPk,1426 +greenlet/platform/switch_ppc64_aix.h,sha256=_BL0iyRr3ZA5iPlr3uk9SJ5sNRWGYLrXcZ5z-CE9anE,3860 +greenlet/platform/switch_ppc64_linux.h,sha256=0rriT5XyxPb0GqsSSn_bP9iQsnjsPbBmu0yqo5goSyQ,3815 +greenlet/platform/switch_ppc_aix.h,sha256=pHA4slEjUFP3J3SYm1TAlNPhgb2G_PAtax5cO8BEe1A,2941 +greenlet/platform/switch_ppc_linux.h,sha256=YwrlKUzxlXuiKMQqr6MFAV1bPzWnmvk6X1AqJZEpOWU,2759 +greenlet/platform/switch_ppc_macosx.h,sha256=L8sB0c00V4G2_5cQCG3zX-23DKq3le_Dcj0sUDcACos,2624 +greenlet/platform/switch_ppc_unix.h,sha256=POy4bRBcH74Chfw4viFE9bVlZ-7BaNsFC0NnXr1L2tg,2652 +greenlet/platform/switch_riscv_unix.h,sha256=jX3vC_xZXiUho8tz4J6Ai8BNQB80yLn03fxkoMztVCU,740 +greenlet/platform/switch_s390_unix.h,sha256=RRlGu957ybmq95qNNY4Qw1mcaoT3eBnW5KbVwu48KX8,2763 +greenlet/platform/switch_sparc_sun_gcc.h,sha256=xZish9GsMHBienUbUMsX1-ZZ-as7hs36sVhYIE3ew8Y,2797 +greenlet/platform/switch_x32_unix.h,sha256=nM98PKtzTWc1lcM7TRMUZJzskVdR1C69U1UqZRWX0GE,1509 +greenlet/platform/switch_x64_masm.asm,sha256=nu6n2sWyXuXfpPx40d9YmLfHXUc1sHgeTvX1kUzuvEM,1841 +greenlet/platform/switch_x64_masm.obj,sha256=GNtTNxYdo7idFUYsQv-mrXWgyT5EJ93-9q90lN6svtQ,1078 +greenlet/platform/switch_x64_msvc.h,sha256=LIeasyKo_vHzspdMzMHbosRhrBfKI4BkQOh4qcTHyJw,1805 +greenlet/platform/switch_x86_msvc.h,sha256=TtGOwinbFfnn6clxMNkCz8i6OmgB6kVRrShoF5iT9to,12838 +greenlet/platform/switch_x86_unix.h,sha256=VplW9H0FF0cZHw1DhJdIUs5q6YLS4cwb2nYwjF83R1s,3059 +greenlet/slp_platformselect.h,sha256=JEnia_2HsTwdqvnnEsDxHQqalYvFJqx_CDsqvNUQYe8,3600 +greenlet/tests/__init__.py,sha256=F282jaIavKrhsYgHJEXtIQXKHdHpe9OJOPTK7R40JzI,9022 +greenlet/tests/__pycache__/__init__.cpython-310.pyc,, +greenlet/tests/__pycache__/fail_clearing_run_switches.cpython-310.pyc,, +greenlet/tests/__pycache__/fail_cpp_exception.cpython-310.pyc,, +greenlet/tests/__pycache__/fail_initialstub_already_started.cpython-310.pyc,, +greenlet/tests/__pycache__/fail_slp_switch.cpython-310.pyc,, +greenlet/tests/__pycache__/fail_switch_three_greenlets.cpython-310.pyc,, +greenlet/tests/__pycache__/fail_switch_three_greenlets2.cpython-310.pyc,, +greenlet/tests/__pycache__/fail_switch_two_greenlets.cpython-310.pyc,, +greenlet/tests/__pycache__/leakcheck.cpython-310.pyc,, +greenlet/tests/__pycache__/test_contextvars.cpython-310.pyc,, +greenlet/tests/__pycache__/test_cpp.cpython-310.pyc,, +greenlet/tests/__pycache__/test_extension_interface.cpython-310.pyc,, +greenlet/tests/__pycache__/test_gc.cpython-310.pyc,, +greenlet/tests/__pycache__/test_generator.cpython-310.pyc,, +greenlet/tests/__pycache__/test_generator_nested.cpython-310.pyc,, +greenlet/tests/__pycache__/test_greenlet.cpython-310.pyc,, +greenlet/tests/__pycache__/test_greenlet_trash.cpython-310.pyc,, +greenlet/tests/__pycache__/test_leaks.cpython-310.pyc,, +greenlet/tests/__pycache__/test_stack_saved.cpython-310.pyc,, +greenlet/tests/__pycache__/test_throw.cpython-310.pyc,, +greenlet/tests/__pycache__/test_tracing.cpython-310.pyc,, +greenlet/tests/__pycache__/test_version.cpython-310.pyc,, +greenlet/tests/__pycache__/test_weakref.cpython-310.pyc,, +greenlet/tests/_test_extension.c,sha256=vkeGA-6oeJcGILsD7oIrT1qZop2GaTOHXiNT7mcSl-0,5773 +greenlet/tests/_test_extension.cpython-310-x86_64-linux-gnu.so,sha256=GBJExwOb15y8CP47hakiiGHRQiwKWkAj1MRFhf2O5w4,36200 +greenlet/tests/_test_extension_cpp.cpp,sha256=e0kVnaB8CCaEhE9yHtNyfqTjevsPDKKx-zgxk7PPK48,6565 +greenlet/tests/_test_extension_cpp.cpython-310-x86_64-linux-gnu.so,sha256=2aqyYbQABM3KmZE6XnWh4PJ2xBExBJOzlf0PyfyrEYg,57264 +greenlet/tests/fail_clearing_run_switches.py,sha256=o433oA_nUCtOPaMEGc8VEhZIKa71imVHXFw7TsXaP8M,1263 +greenlet/tests/fail_cpp_exception.py,sha256=o_ZbipWikok8Bjc-vjiQvcb5FHh2nVW-McGKMLcMzh0,985 +greenlet/tests/fail_initialstub_already_started.py,sha256=txENn5IyzGx2p-XR1XB7qXmC8JX_4mKDEA8kYBXUQKc,1961 +greenlet/tests/fail_slp_switch.py,sha256=rJBZcZfTWR3e2ERQtPAud6YKShiDsP84PmwOJbp4ey0,524 +greenlet/tests/fail_switch_three_greenlets.py,sha256=zSitV7rkNnaoHYVzAGGLnxz-yPtohXJJzaE8ehFDQ0M,956 +greenlet/tests/fail_switch_three_greenlets2.py,sha256=FPJensn2EJxoropl03JSTVP3kgP33k04h6aDWWozrOk,1285 +greenlet/tests/fail_switch_two_greenlets.py,sha256=1CaI8s3504VbbF1vj1uBYuy-zxBHVzHPIAd1LIc8ONg,817 +greenlet/tests/leakcheck.py,sha256=inbfM7_oVzd8jIKGxCgo4JqpFZaDAnWPkSULJ8vIE1s,11964 +greenlet/tests/test_contextvars.py,sha256=0n5pR_lbpAppc5wFfK0e1SwYLM-fsSFp72B5_ArLPGE,10348 +greenlet/tests/test_cpp.py,sha256=hpxhFAdKJTpAVZP8CBGs1ZcrKdscI9BaDZk4btkI5d4,2736 +greenlet/tests/test_extension_interface.py,sha256=eJ3cwLacdK2WbsrC-4DgeyHdwLRcG4zx7rrkRtqSzC4,3829 +greenlet/tests/test_gc.py,sha256=PCOaRpIyjNnNlDogGL3FZU_lrdXuM-pv1rxeE5TP5mc,2923 +greenlet/tests/test_generator.py,sha256=tONXiTf98VGm347o1b-810daPiwdla5cbpFg6QI1R1g,1240 +greenlet/tests/test_generator_nested.py,sha256=7v4HOYrf1XZP39dk5IUMubdZ8yc3ynwZcqj9GUJyMSA,3718 +greenlet/tests/test_greenlet.py,sha256=95qgDR-xtB0jzEFLirNx7HPUdwHikVMvDdyUoCvyjOo,45354 +greenlet/tests/test_greenlet_trash.py,sha256=P6r-3K4fmXX8foW8BVgthuqVKjicHMDvxfK7Al4x028,7508 +greenlet/tests/test_leaks.py,sha256=wskLqCAvqZ3qTZkam_wXzd-E5zelUjlXS5Ss8KshtZY,17465 +greenlet/tests/test_stack_saved.py,sha256=eyzqNY2VCGuGlxhT_In6TvZ6Okb0AXFZVyBEnK1jDwA,446 +greenlet/tests/test_throw.py,sha256=u2TQ_WvvCd6N6JdXWIxVEcXkKu5fepDlz9dktYdmtng,3712 +greenlet/tests/test_tracing.py,sha256=VlwzMU0C1noospZhuUMyB7MHw200emIvGCN_6G2p2ZU,8250 +greenlet/tests/test_version.py,sha256=O9DpAITsOFgiRcjd4odQ7ejmwx_N9Q1zQENVcbtFHIc,1339 +greenlet/tests/test_weakref.py,sha256=F8M23btEF87bIbpptLNBORosbQqNZGiYeKMqYjWrsak,883 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/WHEEL b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/WHEEL new file mode 100644 index 0000000..67013fc --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: false +Tag: cp310-cp310-manylinux_2_24_x86_64 +Tag: cp310-cp310-manylinux_2_28_x86_64 + diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/top_level.txt b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/top_level.txt new file mode 100644 index 0000000..46725be --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet-3.0.3.dist-info/top_level.txt @@ -0,0 +1 @@ +greenlet diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TBrokenGreenlet.cpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TBrokenGreenlet.cpp new file mode 100644 index 0000000..11a3bea --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TBrokenGreenlet.cpp @@ -0,0 +1,45 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ +/** + * Implementation of greenlet::UserGreenlet. + * + * Format with: + * clang-format -i --style=file src/greenlet/greenlet.c + * + * + * Fix missing braces with: + * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" +*/ + +#include "greenlet_greenlet.hpp" + +namespace greenlet { + +void* BrokenGreenlet::operator new(size_t UNUSED(count)) +{ + return allocator.allocate(1); +} + + +void BrokenGreenlet::operator delete(void* ptr) +{ + return allocator.deallocate(static_cast(ptr), + 1); +} + +greenlet::PythonAllocator greenlet::BrokenGreenlet::allocator; + +bool +BrokenGreenlet::force_slp_switch_error() const noexcept +{ + return this->_force_slp_switch_error; +} + +UserGreenlet::switchstack_result_t BrokenGreenlet::g_switchstack(void) +{ + if (this->_force_switch_error) { + return switchstack_result_t(-1); + } + return UserGreenlet::g_switchstack(); +} + +}; //namespace greenlet diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TExceptionState.cpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TExceptionState.cpp new file mode 100644 index 0000000..ee6b191 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TExceptionState.cpp @@ -0,0 +1,62 @@ +#ifndef GREENLET_EXCEPTION_STATE_CPP +#define GREENLET_EXCEPTION_STATE_CPP + +#include +#include "greenlet_greenlet.hpp" + +namespace greenlet { + + +ExceptionState::ExceptionState() +{ + this->clear(); +} + +void ExceptionState::operator<<(const PyThreadState *const tstate) noexcept +{ + this->exc_info = tstate->exc_info; + this->exc_state = tstate->exc_state; +} + +void ExceptionState::operator>>(PyThreadState *const tstate) noexcept +{ + tstate->exc_state = this->exc_state; + tstate->exc_info = + this->exc_info ? this->exc_info : &tstate->exc_state; + this->clear(); +} + +void ExceptionState::clear() noexcept +{ + this->exc_info = nullptr; + this->exc_state.exc_value = nullptr; +#if !GREENLET_PY311 + this->exc_state.exc_type = nullptr; + this->exc_state.exc_traceback = nullptr; +#endif + this->exc_state.previous_item = nullptr; +} + +int ExceptionState::tp_traverse(visitproc visit, void* arg) noexcept +{ + Py_VISIT(this->exc_state.exc_value); +#if !GREENLET_PY311 + Py_VISIT(this->exc_state.exc_type); + Py_VISIT(this->exc_state.exc_traceback); +#endif + return 0; +} + +void ExceptionState::tp_clear() noexcept +{ + Py_CLEAR(this->exc_state.exc_value); +#if !GREENLET_PY311 + Py_CLEAR(this->exc_state.exc_type); + Py_CLEAR(this->exc_state.exc_traceback); +#endif +} + + +}; // namespace greenlet + +#endif // GREENLET_EXCEPTION_STATE_CPP diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TGreenlet.cpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TGreenlet.cpp new file mode 100644 index 0000000..51f8995 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TGreenlet.cpp @@ -0,0 +1,714 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ +/** + * Implementation of greenlet::Greenlet. + * + * Format with: + * clang-format -i --style=file src/greenlet/greenlet.c + * + * + * Fix missing braces with: + * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" +*/ + +#include "greenlet_internal.hpp" +#include "greenlet_greenlet.hpp" +#include "greenlet_thread_state.hpp" + +#include "TGreenletGlobals.cpp" +#include "TThreadStateDestroy.cpp" + +namespace greenlet { + +Greenlet::Greenlet(PyGreenlet* p) +{ + p ->pimpl = this; +} + +Greenlet::~Greenlet() +{ + // XXX: Can't do this. tp_clear is a virtual function, and by the + // time we're here, we've sliced off our child classes. + //this->tp_clear(); +} + +Greenlet::Greenlet(PyGreenlet* p, const StackState& initial_stack) + : stack_state(initial_stack) +{ + // can't use a delegating constructor because of + // MSVC for Python 2.7 + p->pimpl = this; +} + +bool +Greenlet::force_slp_switch_error() const noexcept +{ + return false; +} + +void +Greenlet::release_args() +{ + this->switch_args.CLEAR(); +} + +/** + * CAUTION: This will allocate memory and may trigger garbage + * collection and arbitrary Python code. + */ +OwnedObject +Greenlet::throw_GreenletExit_during_dealloc(const ThreadState& UNUSED(current_thread_state)) +{ + // If we're killed because we lost all references in the + // middle of a switch, that's ok. Don't reset the args/kwargs, + // we still want to pass them to the parent. + PyErr_SetString(mod_globs->PyExc_GreenletExit, + "Killing the greenlet because all references have vanished."); + // To get here it had to have run before + return this->g_switch(); +} + +inline void +Greenlet::slp_restore_state() noexcept +{ +#ifdef SLP_BEFORE_RESTORE_STATE + SLP_BEFORE_RESTORE_STATE(); +#endif + this->stack_state.copy_heap_to_stack( + this->thread_state()->borrow_current()->stack_state); +} + + +inline int +Greenlet::slp_save_state(char *const stackref) noexcept +{ + // XXX: This used to happen in the middle, before saving, but + // after finding the next owner. Does that matter? This is + // only defined for Sparc/GCC where it flushes register + // windows to the stack (I think) +#ifdef SLP_BEFORE_SAVE_STATE + SLP_BEFORE_SAVE_STATE(); +#endif + return this->stack_state.copy_stack_to_heap(stackref, + this->thread_state()->borrow_current()->stack_state); +} + +/** + * CAUTION: This will allocate memory and may trigger garbage + * collection and arbitrary Python code. + */ +OwnedObject +Greenlet::on_switchstack_or_initialstub_failure( + Greenlet* target, + const Greenlet::switchstack_result_t& err, + const bool target_was_me, + const bool was_initial_stub) +{ + // If we get here, either g_initialstub() + // failed, or g_switchstack() failed. Either one of those + // cases SHOULD leave us in the original greenlet with a valid stack. + if (!PyErr_Occurred()) { + PyErr_SetString( + PyExc_SystemError, + was_initial_stub + ? "Failed to switch stacks into a greenlet for the first time." + : "Failed to switch stacks into a running greenlet."); + } + this->release_args(); + + if (target && !target_was_me) { + target->murder_in_place(); + } + + assert(!err.the_new_current_greenlet); + assert(!err.origin_greenlet); + return OwnedObject(); + +} + +OwnedGreenlet +Greenlet::g_switchstack_success() noexcept +{ + PyThreadState* tstate = PyThreadState_GET(); + // restore the saved state + this->python_state >> tstate; + this->exception_state >> tstate; + + // The thread state hasn't been changed yet. + ThreadState* thread_state = this->thread_state(); + OwnedGreenlet result(thread_state->get_current()); + thread_state->set_current(this->self()); + //assert(thread_state->borrow_current().borrow() == this->_self); + return result; +} + +Greenlet::switchstack_result_t +Greenlet::g_switchstack(void) +{ + // if any of these assertions fail, it's likely because we + // switched away and tried to switch back to us. Early stages of + // switching are not reentrant because we re-use ``this->args()``. + // Switching away would happen if we trigger a garbage collection + // (by just using some Python APIs that happen to allocate Python + // objects) and some garbage had weakref callbacks or __del__ that + // switches (people don't write code like that by hand, but with + // gevent it's possible without realizing it) + assert(this->args() || PyErr_Occurred()); + { /* save state */ + if (this->thread_state()->is_current(this->self())) { + // Hmm, nothing to do. + // TODO: Does this bypass trace events that are + // important? + return switchstack_result_t(0, + this, this->thread_state()->borrow_current()); + } + BorrowedGreenlet current = this->thread_state()->borrow_current(); + PyThreadState* tstate = PyThreadState_GET(); + + current->python_state << tstate; + current->exception_state << tstate; + this->python_state.will_switch_from(tstate); + switching_thread_state = this; + current->expose_frames(); + } + assert(this->args() || PyErr_Occurred()); + // If this is the first switch into a greenlet, this will + // return twice, once with 1 in the new greenlet, once with 0 + // in the origin. + int err; + if (this->force_slp_switch_error()) { + err = -1; + } + else { + err = slp_switch(); + } + + if (err < 0) { /* error */ + // Tested by + // test_greenlet.TestBrokenGreenlets.test_failed_to_slp_switch_into_running + // + // It's not clear if it's worth trying to clean up and + // continue here. Failing to switch stacks is a big deal which + // may not be recoverable (who knows what state the stack is in). + // Also, we've stolen references in preparation for calling + // ``g_switchstack_success()`` and we don't have a clean + // mechanism for backing that all out. + Py_FatalError("greenlet: Failed low-level slp_switch(). The stack is probably corrupt."); + } + + // No stack-based variables are valid anymore. + + // But the global is volatile so we can reload it without the + // compiler caching it from earlier. + Greenlet* greenlet_that_switched_in = switching_thread_state; // aka this + switching_thread_state = nullptr; + // except that no stack variables are valid, we would: + // assert(this == greenlet_that_switched_in); + + // switchstack success is where we restore the exception state, + // etc. It returns the origin greenlet because its convenient. + + OwnedGreenlet origin = greenlet_that_switched_in->g_switchstack_success(); + assert(greenlet_that_switched_in->args() || PyErr_Occurred()); + return switchstack_result_t(err, greenlet_that_switched_in, origin); +} + + +inline void +Greenlet::check_switch_allowed() const +{ + // TODO: Make this take a parameter of the current greenlet, + // or current main greenlet, to make the check for + // cross-thread switching cheaper. Surely somewhere up the + // call stack we've already accessed the thread local variable. + + // We expect to always have a main greenlet now; accessing the thread state + // created it. However, if we get here and cleanup has already + // begun because we're a greenlet that was running in a + // (now dead) thread, these invariants will not hold true. In + // fact, accessing `this->thread_state` may not even be possible. + + // If the thread this greenlet was running in is dead, + // we'll still have a reference to a main greenlet, but the + // thread state pointer we have is bogus. + // TODO: Give the objects an API to determine if they belong + // to a dead thread. + + const BorrowedMainGreenlet main_greenlet = this->find_main_greenlet_in_lineage(); + + if (!main_greenlet) { + throw PyErrOccurred(mod_globs->PyExc_GreenletError, + "cannot switch to a garbage collected greenlet"); + } + + if (!main_greenlet->thread_state()) { + throw PyErrOccurred(mod_globs->PyExc_GreenletError, + "cannot switch to a different thread (which happens to have exited)"); + } + + // The main greenlet we found was from the .parent lineage. + // That may or may not have any relationship to the main + // greenlet of the running thread. We can't actually access + // our this->thread_state members to try to check that, + // because it could be in the process of getting destroyed, + // but setting the main_greenlet->thread_state member to NULL + // may not be visible yet. So we need to check against the + // current thread state (once the cheaper checks are out of + // the way) + const BorrowedMainGreenlet current_main_greenlet = GET_THREAD_STATE().state().borrow_main_greenlet(); + if ( + // lineage main greenlet is not this thread's greenlet + current_main_greenlet != main_greenlet + || ( + // atteched to some thread + this->main_greenlet() + // XXX: Same condition as above. Was this supposed to be + // this->main_greenlet()? + && current_main_greenlet != main_greenlet) + // switching into a known dead thread (XXX: which, if we get here, + // is bad, because we just accessed the thread state, which is + // gone!) + || (!current_main_greenlet->thread_state())) { + // CAUTION: This may trigger memory allocations, gc, and + // arbitrary Python code. + throw PyErrOccurred(mod_globs->PyExc_GreenletError, + "cannot switch to a different thread"); + } +} + +const OwnedObject +Greenlet::context() const +{ + using greenlet::PythonStateContext; + OwnedObject result; + + if (this->is_currently_running_in_some_thread()) { + /* Currently running greenlet: context is stored in the thread state, + not the greenlet object. */ + if (GET_THREAD_STATE().state().is_current(this->self())) { + result = PythonStateContext::context(PyThreadState_GET()); + } + else { + throw ValueError( + "cannot get context of a " + "greenlet that is running in a different thread"); + } + } + else { + /* Greenlet is not running: just return context. */ + result = this->python_state.context(); + } + if (!result) { + result = OwnedObject::None(); + } + return result; +} + + +void +Greenlet::context(BorrowedObject given) +{ + using greenlet::PythonStateContext; + if (!given) { + throw AttributeError("can't delete context attribute"); + } + if (given.is_None()) { + /* "Empty context" is stored as NULL, not None. */ + given = nullptr; + } + + //checks type, incrs refcnt + greenlet::refs::OwnedContext context(given); + PyThreadState* tstate = PyThreadState_GET(); + + if (this->is_currently_running_in_some_thread()) { + if (!GET_THREAD_STATE().state().is_current(this->self())) { + throw ValueError("cannot set context of a greenlet" + " that is running in a different thread"); + } + + /* Currently running greenlet: context is stored in the thread state, + not the greenlet object. */ + OwnedObject octx = OwnedObject::consuming(PythonStateContext::context(tstate)); + PythonStateContext::context(tstate, context.relinquish_ownership()); + } + else { + /* Greenlet is not running: just set context. Note that the + greenlet may be dead.*/ + this->python_state.context() = context; + } +} + +/** + * CAUTION: May invoke arbitrary Python code. + * + * Figure out what the result of ``greenlet.switch(arg, kwargs)`` + * should be and transfers ownership of it to the left-hand-side. + * + * If switch() was just passed an arg tuple, then we'll just return that. + * If only keyword arguments were passed, then we'll pass the keyword + * argument dict. Otherwise, we'll create a tuple of (args, kwargs) and + * return both. + * + * CAUTION: This may allocate a new tuple object, which may + * cause the Python garbage collector to run, which in turn may + * run arbitrary Python code that switches. + */ +OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept +{ + // Because this may invoke arbitrary Python code, which could + // result in switching back to us, we need to get the + // arguments locally on the stack. + assert(rhs); + OwnedObject args = rhs.args(); + OwnedObject kwargs = rhs.kwargs(); + rhs.CLEAR(); + // We shouldn't be called twice for the same switch. + assert(args || kwargs); + assert(!rhs); + + if (!kwargs) { + lhs = args; + } + else if (!PyDict_Size(kwargs.borrow())) { + lhs = args; + } + else if (!PySequence_Length(args.borrow())) { + lhs = kwargs; + } + else { + // PyTuple_Pack allocates memory, may GC, may run arbitrary + // Python code. + lhs = OwnedObject::consuming(PyTuple_Pack(2, args.borrow(), kwargs.borrow())); + } + return lhs; +} + +static OwnedObject +g_handle_exit(const OwnedObject& greenlet_result) +{ + if (!greenlet_result && mod_globs->PyExc_GreenletExit.PyExceptionMatches()) { + /* catch and ignore GreenletExit */ + PyErrFetchParam val; + PyErr_Fetch(PyErrFetchParam(), val, PyErrFetchParam()); + if (!val) { + return OwnedObject::None(); + } + return OwnedObject(val); + } + + if (greenlet_result) { + // package the result into a 1-tuple + // PyTuple_Pack increments the reference of its arguments, + // so we always need to decref the greenlet result; + // the owner will do that. + return OwnedObject::consuming(PyTuple_Pack(1, greenlet_result.borrow())); + } + + return OwnedObject(); +} + + + +/** + * May run arbitrary Python code. + */ +OwnedObject +Greenlet::g_switch_finish(const switchstack_result_t& err) +{ + assert(err.the_new_current_greenlet == this); + + ThreadState& state = *this->thread_state(); + // Because calling the trace function could do arbitrary things, + // including switching away from this greenlet and then maybe + // switching back, we need to capture the arguments now so that + // they don't change. + OwnedObject result; + if (this->args()) { + result <<= this->args(); + } + else { + assert(PyErr_Occurred()); + } + assert(!this->args()); + try { + // Our only caller handles the bad error case + assert(err.status >= 0); + assert(state.borrow_current() == this->self()); + if (OwnedObject tracefunc = state.get_tracefunc()) { + assert(result || PyErr_Occurred()); + g_calltrace(tracefunc, + result ? mod_globs->event_switch : mod_globs->event_throw, + err.origin_greenlet, + this->self()); + } + // The above could have invoked arbitrary Python code, but + // it couldn't switch back to this object and *also* + // throw an exception, so the args won't have changed. + + if (PyErr_Occurred()) { + // We get here if we fell of the end of the run() function + // raising an exception. The switch itself was + // successful, but the function raised. + // valgrind reports that memory allocated here can still + // be reached after a test run. + throw PyErrOccurred::from_current(); + } + return result; + } + catch (const PyErrOccurred&) { + /* Turn switch errors into switch throws */ + /* Turn trace errors into switch throws */ + this->release_args(); + throw; + } +} + +void +Greenlet::g_calltrace(const OwnedObject& tracefunc, + const greenlet::refs::ImmortalEventName& event, + const BorrowedGreenlet& origin, + const BorrowedGreenlet& target) +{ + PyErrPieces saved_exc; + try { + TracingGuard tracing_guard; + // TODO: We have saved the active exception (if any) that's + // about to be raised. In the 'throw' case, we could provide + // the exception to the tracefunction, which seems very helpful. + tracing_guard.CallTraceFunction(tracefunc, event, origin, target); + } + catch (const PyErrOccurred&) { + // In case of exceptions trace function is removed, + // and any existing exception is replaced with the tracing + // exception. + GET_THREAD_STATE().state().set_tracefunc(Py_None); + throw; + } + + saved_exc.PyErrRestore(); + assert( + (event == mod_globs->event_throw && PyErr_Occurred()) + || (event == mod_globs->event_switch && !PyErr_Occurred()) + ); +} + +void +Greenlet::murder_in_place() +{ + if (this->active()) { + assert(!this->is_currently_running_in_some_thread()); + this->deactivate_and_free(); + } +} + +inline void +Greenlet::deactivate_and_free() +{ + if (!this->active()) { + return; + } + // Throw away any saved stack. + this->stack_state = StackState(); + assert(!this->stack_state.active()); + // Throw away any Python references. + // We're holding a borrowed reference to the last + // frame we executed. Since we borrowed it, the + // normal traversal, clear, and dealloc functions + // ignore it, meaning it leaks. (The thread state + // object can't find it to clear it when that's + // deallocated either, because by definition if we + // got an object on this list, it wasn't + // running and the thread state doesn't have + // this frame.) + // So here, we *do* clear it. + this->python_state.tp_clear(true); +} + +bool +Greenlet::belongs_to_thread(const ThreadState* thread_state) const +{ + if (!this->thread_state() // not running anywhere, or thread + // exited + || !thread_state) { // same, or there is no thread state. + return false; + } + return true; +} + + +void +Greenlet::deallocing_greenlet_in_thread(const ThreadState* current_thread_state) +{ + /* Cannot raise an exception to kill the greenlet if + it is not running in the same thread! */ + if (this->belongs_to_thread(current_thread_state)) { + assert(current_thread_state); + // To get here it had to have run before + /* Send the greenlet a GreenletExit exception. */ + + // We don't care about the return value, only whether an + // exception happened. + this->throw_GreenletExit_during_dealloc(*current_thread_state); + return; + } + + // Not the same thread! Temporarily save the greenlet + // into its thread's deleteme list, *if* it exists. + // If that thread has already exited, and processed its pending + // cleanup, we'll never be able to clean everything up: we won't + // be able to raise an exception. + // That's mostly OK! Since we can't add it to a list, our refcount + // won't increase, and we'll go ahead with the DECREFs later. + ThreadState *const thread_state = this->thread_state(); + if (thread_state) { + thread_state->delete_when_thread_running(this->self()); + } + else { + // The thread is dead, we can't raise an exception. + // We need to make it look non-active, though, so that dealloc + // finishes killing it. + this->deactivate_and_free(); + } + return; +} + + +int +Greenlet::tp_traverse(visitproc visit, void* arg) +{ + + int result; + if ((result = this->exception_state.tp_traverse(visit, arg)) != 0) { + return result; + } + //XXX: This is ugly. But so is handling everything having to do + //with the top frame. + bool visit_top_frame = this->was_running_in_dead_thread(); + // When true, the thread is dead. Our implicit weak reference to the + // frame is now all that's left; we consider ourselves to + // strongly own it now. + if ((result = this->python_state.tp_traverse(visit, arg, visit_top_frame)) != 0) { + return result; + } + return 0; +} + +int +Greenlet::tp_clear() +{ + bool own_top_frame = this->was_running_in_dead_thread(); + this->exception_state.tp_clear(); + this->python_state.tp_clear(own_top_frame); + return 0; +} + +bool Greenlet::is_currently_running_in_some_thread() const +{ + return this->stack_state.active() && !this->python_state.top_frame(); +} + +#if GREENLET_PY312 +void GREENLET_NOINLINE(Greenlet::expose_frames)() +{ + if (!this->python_state.top_frame()) { + return; + } + + _PyInterpreterFrame* last_complete_iframe = nullptr; + _PyInterpreterFrame* iframe = this->python_state.top_frame()->f_frame; + while (iframe) { + // We must make a copy before looking at the iframe contents, + // since iframe might point to a portion of the greenlet's C stack + // that was spilled when switching greenlets. + _PyInterpreterFrame iframe_copy; + this->stack_state.copy_from_stack(&iframe_copy, iframe, sizeof(*iframe)); + if (!_PyFrame_IsIncomplete(&iframe_copy)) { + // If the iframe were OWNED_BY_CSTACK then it would always be + // incomplete. Since it's not incomplete, it's not on the C stack + // and we can access it through the original `iframe` pointer + // directly. This is important since GetFrameObject might + // lazily _create_ the frame object and we don't want the + // interpreter to lose track of it. + assert(iframe_copy.owner != FRAME_OWNED_BY_CSTACK); + + // We really want to just write: + // PyFrameObject* frame = _PyFrame_GetFrameObject(iframe); + // but _PyFrame_GetFrameObject calls _PyFrame_MakeAndSetFrameObject + // which is not a visible symbol in libpython. The easiest + // way to get a public function to call it is using + // PyFrame_GetBack, which is defined as follows: + // assert(frame != NULL); + // assert(!_PyFrame_IsIncomplete(frame->f_frame)); + // PyFrameObject *back = frame->f_back; + // if (back == NULL) { + // _PyInterpreterFrame *prev = frame->f_frame->previous; + // prev = _PyFrame_GetFirstComplete(prev); + // if (prev) { + // back = _PyFrame_GetFrameObject(prev); + // } + // } + // return (PyFrameObject*)Py_XNewRef(back); + if (!iframe->frame_obj) { + PyFrameObject dummy_frame; + _PyInterpreterFrame dummy_iframe; + dummy_frame.f_back = nullptr; + dummy_frame.f_frame = &dummy_iframe; + // force the iframe to be considered complete without + // needing to check its code object: + dummy_iframe.owner = FRAME_OWNED_BY_GENERATOR; + dummy_iframe.previous = iframe; + assert(!_PyFrame_IsIncomplete(&dummy_iframe)); + // Drop the returned reference immediately; the iframe + // continues to hold a strong reference + Py_XDECREF(PyFrame_GetBack(&dummy_frame)); + assert(iframe->frame_obj); + } + + // This is a complete frame, so make the last one of those we saw + // point at it, bypassing any incomplete frames (which may have + // been on the C stack) in between the two. We're overwriting + // last_complete_iframe->previous and need that to be reversible, + // so we store the original previous ptr in the frame object + // (which we must have created on a previous iteration through + // this loop). The frame object has a bunch of storage that is + // only used when its iframe is OWNED_BY_FRAME_OBJECT, which only + // occurs when the frame object outlives the frame's execution, + // which can't have happened yet because the frame is currently + // executing as far as the interpreter is concerned. So, we can + // reuse it for our own purposes. + assert(iframe->owner == FRAME_OWNED_BY_THREAD + || iframe->owner == FRAME_OWNED_BY_GENERATOR); + if (last_complete_iframe) { + assert(last_complete_iframe->frame_obj); + memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0], + &last_complete_iframe->previous, sizeof(void *)); + last_complete_iframe->previous = iframe; + } + last_complete_iframe = iframe; + } + // Frames that are OWNED_BY_FRAME_OBJECT are linked via the + // frame's f_back while all others are linked via the iframe's + // previous ptr. Since all the frames we traverse are running + // as far as the interpreter is concerned, we don't have to + // worry about the OWNED_BY_FRAME_OBJECT case. + iframe = iframe_copy.previous; + } + + // Give the outermost complete iframe a null previous pointer to + // account for any potential incomplete/C-stack iframes between it + // and the actual top-of-stack + if (last_complete_iframe) { + assert(last_complete_iframe->frame_obj); + memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0], + &last_complete_iframe->previous, sizeof(void *)); + last_complete_iframe->previous = nullptr; + } +} +#else +void Greenlet::expose_frames() +{ + +} +#endif + +}; // namespace greenlet diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TGreenletGlobals.cpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TGreenletGlobals.cpp new file mode 100644 index 0000000..c71c963 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TGreenletGlobals.cpp @@ -0,0 +1,94 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ +/** + * Implementation of GreenletGlobals. + * + * Format with: + * clang-format -i --style=file src/greenlet/greenlet.c + * + * + * Fix missing braces with: + * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" +*/ +#ifndef T_GREENLET_GLOBALS +#define T_GREENLET_GLOBALS + +#include "greenlet_refs.hpp" +#include "greenlet_exceptions.hpp" +#include "greenlet_thread_support.hpp" +#include "greenlet_thread_state.hpp" + +namespace greenlet { + +// This encapsulates what were previously module global "constants" +// established at init time. +// This is a step towards Python3 style module state that allows +// reloading. +// +// In an earlier iteration of this code, we used placement new to be +// able to allocate this object statically still, so that references +// to its members don't incur an extra pointer indirection. +// But under some scenarios, that could result in crashes at +// shutdown because apparently the destructor was getting run twice? +class GreenletGlobals +{ + +public: + const greenlet::refs::ImmortalEventName event_switch; + const greenlet::refs::ImmortalEventName event_throw; + const greenlet::refs::ImmortalException PyExc_GreenletError; + const greenlet::refs::ImmortalException PyExc_GreenletExit; + const greenlet::refs::ImmortalObject empty_tuple; + const greenlet::refs::ImmortalObject empty_dict; + const greenlet::refs::ImmortalString str_run; + Mutex* const thread_states_to_destroy_lock; + greenlet::cleanup_queue_t thread_states_to_destroy; + + GreenletGlobals() : + event_switch("switch"), + event_throw("throw"), + PyExc_GreenletError("greenlet.error"), + PyExc_GreenletExit("greenlet.GreenletExit", PyExc_BaseException), + empty_tuple(Require(PyTuple_New(0))), + empty_dict(Require(PyDict_New())), + str_run("run"), + thread_states_to_destroy_lock(new Mutex()) + {} + + ~GreenletGlobals() + { + // This object is (currently) effectively immortal, and not + // just because of those placement new tricks; if we try to + // deallocate the static object we allocated, and overwrote, + // we would be doing so at C++ teardown time, which is after + // the final Python GIL is released, and we can't use the API + // then. + // (The members will still be destructed, but they also don't + // do any deallocation.) + } + + void queue_to_destroy(ThreadState* ts) const + { + // we're currently accessed through a static const object, + // implicitly marking our members as const, so code can't just + // call push_back (or pop_back) without casting away the + // const. + // + // Do that for callers. + greenlet::cleanup_queue_t& q = const_cast(this->thread_states_to_destroy); + q.push_back(ts); + } + + ThreadState* take_next_to_destroy() const + { + greenlet::cleanup_queue_t& q = const_cast(this->thread_states_to_destroy); + ThreadState* result = q.back(); + q.pop_back(); + return result; + } +}; + +}; // namespace greenlet + +static const greenlet::GreenletGlobals* mod_globs; + +#endif // T_GREENLET_GLOBALS diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TMainGreenlet.cpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TMainGreenlet.cpp new file mode 100644 index 0000000..c33aadb --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TMainGreenlet.cpp @@ -0,0 +1,155 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ +/** + * Implementation of greenlet::MainGreenlet. + * + * Format with: + * clang-format -i --style=file src/greenlet/greenlet.c + * + * + * Fix missing braces with: + * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" +*/ + +#include "greenlet_greenlet.hpp" +#include "greenlet_thread_state.hpp" + + +// Protected by the GIL. Incremented when we create a main greenlet, +// in a new thread, decremented when it is destroyed. +static Py_ssize_t G_TOTAL_MAIN_GREENLETS; + +namespace greenlet { +greenlet::PythonAllocator MainGreenlet::allocator; + +void* MainGreenlet::operator new(size_t UNUSED(count)) +{ + return allocator.allocate(1); +} + + +void MainGreenlet::operator delete(void* ptr) +{ + return allocator.deallocate(static_cast(ptr), + 1); +} + + +MainGreenlet::MainGreenlet(PyGreenlet* p, ThreadState* state) + : Greenlet(p, StackState::make_main()), + _self(p), + _thread_state(state) +{ + G_TOTAL_MAIN_GREENLETS++; +} + +MainGreenlet::~MainGreenlet() +{ + G_TOTAL_MAIN_GREENLETS--; + this->tp_clear(); +} + +ThreadState* +MainGreenlet::thread_state() const noexcept +{ + return this->_thread_state; +} + +void +MainGreenlet::thread_state(ThreadState* t) noexcept +{ + assert(!t); + this->_thread_state = t; +} + +BorrowedGreenlet +MainGreenlet::self() const noexcept +{ + return BorrowedGreenlet(this->_self.borrow()); +} + + +const BorrowedMainGreenlet +MainGreenlet::main_greenlet() const +{ + return this->_self; +} + +BorrowedMainGreenlet +MainGreenlet::find_main_greenlet_in_lineage() const +{ + return BorrowedMainGreenlet(this->_self); +} + +bool +MainGreenlet::was_running_in_dead_thread() const noexcept +{ + return !this->_thread_state; +} + +OwnedObject +MainGreenlet::g_switch() +{ + try { + this->check_switch_allowed(); + } + catch (const PyErrOccurred&) { + this->release_args(); + throw; + } + + switchstack_result_t err = this->g_switchstack(); + if (err.status < 0) { + // XXX: This code path is untested, but it is shared + // with the UserGreenlet path that is tested. + return this->on_switchstack_or_initialstub_failure( + this, + err, + true, // target was me + false // was initial stub + ); + } + + return err.the_new_current_greenlet->g_switch_finish(err); +} + +int +MainGreenlet::tp_traverse(visitproc visit, void* arg) +{ + if (this->_thread_state) { + // we've already traversed main, (self), don't do it again. + int result = this->_thread_state->tp_traverse(visit, arg, false); + if (result) { + return result; + } + } + return Greenlet::tp_traverse(visit, arg); +} + +const OwnedObject& +MainGreenlet::run() const +{ + throw AttributeError("Main greenlets do not have a run attribute."); +} + +void +MainGreenlet::run(const BorrowedObject UNUSED(nrun)) +{ + throw AttributeError("Main greenlets do not have a run attribute."); +} + +void +MainGreenlet::parent(const BorrowedObject raw_new_parent) +{ + if (!raw_new_parent) { + throw AttributeError("can't delete attribute"); + } + throw AttributeError("cannot set the parent of a main greenlet"); +} + +const OwnedGreenlet +MainGreenlet::parent() const +{ + return OwnedGreenlet(); // null becomes None +} + +}; // namespace greenlet diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TPythonState.cpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TPythonState.cpp new file mode 100644 index 0000000..465d417 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TPythonState.cpp @@ -0,0 +1,375 @@ +#ifndef GREENLET_PYTHON_STATE_CPP +#define GREENLET_PYTHON_STATE_CPP + +#include +#include "greenlet_greenlet.hpp" + +namespace greenlet { + +PythonState::PythonState() + : _top_frame() +#if GREENLET_USE_CFRAME + ,cframe(nullptr) + ,use_tracing(0) +#endif +#if GREENLET_PY312 + ,py_recursion_depth(0) + ,c_recursion_depth(0) +#else + ,recursion_depth(0) +#endif + ,trash_delete_nesting(0) +#if GREENLET_PY311 + ,current_frame(nullptr) + ,datastack_chunk(nullptr) + ,datastack_top(nullptr) + ,datastack_limit(nullptr) +#endif +{ +#if GREENLET_USE_CFRAME + /* + The PyThreadState->cframe pointer usually points to memory on + the stack, alloceted in a call into PyEval_EvalFrameDefault. + + Initially, before any evaluation begins, it points to the + initial PyThreadState object's ``root_cframe`` object, which is + statically allocated for the lifetime of the thread. + + A greenlet can last for longer than a call to + PyEval_EvalFrameDefault, so we can't set its ``cframe`` pointer + to be the current ``PyThreadState->cframe``; nor could we use + one from the greenlet parent for the same reason. Yet a further + no: we can't allocate one scoped to the greenlet and then + destroy it when the greenlet is deallocated, because inside the + interpreter the _PyCFrame objects form a linked list, and that too + can result in accessing memory beyond its dynamic lifetime (if + the greenlet doesn't actually finish before it dies, its entry + could still be in the list). + + Using the ``root_cframe`` is problematic, though, because its + members are never modified by the interpreter and are set to 0, + meaning that its ``use_tracing`` flag is never updated. We don't + want to modify that value in the ``root_cframe`` ourself: it + *shouldn't* matter much because we should probably never get + back to the point where that's the only cframe on the stack; + even if it did matter, the major consequence of an incorrect + value for ``use_tracing`` is that if its true the interpreter + does some extra work --- however, it's just good code hygiene. + + Our solution: before a greenlet runs, after its initial + creation, it uses the ``root_cframe`` just to have something to + put there. However, once the greenlet is actually switched to + for the first time, ``g_initialstub`` (which doesn't actually + "return" while the greenlet is running) stores a new _PyCFrame on + its local stack, and copies the appropriate values from the + currently running _PyCFrame; this is then made the _PyCFrame for the + newly-minted greenlet. ``g_initialstub`` then proceeds to call + ``glet.run()``, which results in ``PyEval_...`` adding the + _PyCFrame to the list. Switches continue as normal. Finally, when + the greenlet finishes, the call to ``glet.run()`` returns and + the _PyCFrame is taken out of the linked list and the stack value + is now unused and free to expire. + + XXX: I think we can do better. If we're deallocing in the same + thread, can't we traverse the list and unlink our frame? + Can we just keep a reference to the thread state in case we + dealloc in another thread? (Is that even possible if we're still + running and haven't returned from g_initialstub?) + */ + this->cframe = &PyThreadState_GET()->root_cframe; +#endif +} + + +inline void PythonState::may_switch_away() noexcept +{ +#if GREENLET_PY311 + // PyThreadState_GetFrame is probably going to have to allocate a + // new frame object. That may trigger garbage collection. Because + // we call this during the early phases of a switch (it doesn't + // matter to which greenlet, as this has a global effect), if a GC + // triggers a switch away, two things can happen, both bad: + // - We might not get switched back to, halting forward progress. + // this is pathological, but possible. + // - We might get switched back to with a different set of + // arguments or a throw instead of a switch. That would corrupt + // our state (specifically, PyErr_Occurred() and this->args() + // would no longer agree). + // + // Thus, when we call this API, we need to have GC disabled. + // This method serves as a bottleneck we call when maybe beginning + // a switch. In this way, it is always safe -- no risk of GC -- to + // use ``_GetFrame()`` whenever we need to, just as it was in + // <=3.10 (because subsequent calls will be cached and not + // allocate memory). + + GCDisabledGuard no_gc; + Py_XDECREF(PyThreadState_GetFrame(PyThreadState_GET())); +#endif +} + +void PythonState::operator<<(const PyThreadState *const tstate) noexcept +{ + this->_context.steal(tstate->context); +#if GREENLET_USE_CFRAME + /* + IMPORTANT: ``cframe`` is a pointer into the STACK. Thus, because + the call to ``slp_switch()`` changes the contents of the stack, + you cannot read from ``ts_current->cframe`` after that call and + necessarily get the same values you get from reading it here. + Anything you need to restore from now to then must be saved in a + global/threadlocal variable (because we can't use stack + variables here either). For things that need to persist across + the switch, use `will_switch_from`. + */ + this->cframe = tstate->cframe; + #if !GREENLET_PY312 + this->use_tracing = tstate->cframe->use_tracing; + #endif +#endif // GREENLET_USE_CFRAME +#if GREENLET_PY311 + #if GREENLET_PY312 + this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; + this->c_recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; + #else // not 312 + this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; + #endif // GREENLET_PY312 + this->current_frame = tstate->cframe->current_frame; + this->datastack_chunk = tstate->datastack_chunk; + this->datastack_top = tstate->datastack_top; + this->datastack_limit = tstate->datastack_limit; + + PyFrameObject *frame = PyThreadState_GetFrame((PyThreadState *)tstate); + Py_XDECREF(frame); // PyThreadState_GetFrame gives us a new + // reference. + this->_top_frame.steal(frame); + #if GREENLET_PY312 + this->trash_delete_nesting = tstate->trash.delete_nesting; + #else // not 312 + this->trash_delete_nesting = tstate->trash_delete_nesting; + #endif // GREENLET_PY312 +#else // Not 311 + this->recursion_depth = tstate->recursion_depth; + this->_top_frame.steal(tstate->frame); + this->trash_delete_nesting = tstate->trash_delete_nesting; +#endif // GREENLET_PY311 +} + +#if GREENLET_PY312 +void GREENLET_NOINLINE(PythonState::unexpose_frames)() +{ + if (!this->top_frame()) { + return; + } + + // See GreenletState::expose_frames() and the comment on frames_were_exposed + // for more information about this logic. + _PyInterpreterFrame *iframe = this->_top_frame->f_frame; + while (iframe != nullptr) { + _PyInterpreterFrame *prev_exposed = iframe->previous; + assert(iframe->frame_obj); + memcpy(&iframe->previous, &iframe->frame_obj->_f_frame_data[0], + sizeof(void *)); + iframe = prev_exposed; + } +} +#else +void PythonState::unexpose_frames() +{} +#endif + +void PythonState::operator>>(PyThreadState *const tstate) noexcept +{ + tstate->context = this->_context.relinquish_ownership(); + /* Incrementing this value invalidates the contextvars cache, + which would otherwise remain valid across switches */ + tstate->context_ver++; +#if GREENLET_USE_CFRAME + tstate->cframe = this->cframe; + /* + If we were tracing, we need to keep tracing. + There should never be the possibility of hitting the + root_cframe here. See note above about why we can't + just copy this from ``origin->cframe->use_tracing``. + */ + #if !GREENLET_PY312 + tstate->cframe->use_tracing = this->use_tracing; + #endif +#endif // GREENLET_USE_CFRAME +#if GREENLET_PY311 + #if GREENLET_PY312 + tstate->py_recursion_remaining = tstate->py_recursion_limit - this->py_recursion_depth; + tstate->c_recursion_remaining = C_RECURSION_LIMIT - this->c_recursion_depth; + this->unexpose_frames(); + #else // \/ 3.11 + tstate->recursion_remaining = tstate->recursion_limit - this->recursion_depth; + #endif // GREENLET_PY312 + tstate->cframe->current_frame = this->current_frame; + tstate->datastack_chunk = this->datastack_chunk; + tstate->datastack_top = this->datastack_top; + tstate->datastack_limit = this->datastack_limit; + this->_top_frame.relinquish_ownership(); + #if GREENLET_PY312 + tstate->trash.delete_nesting = this->trash_delete_nesting; + #else // not 3.12 + tstate->trash_delete_nesting = this->trash_delete_nesting; + #endif // GREENLET_PY312 +#else // not 3.11 + tstate->frame = this->_top_frame.relinquish_ownership(); + tstate->recursion_depth = this->recursion_depth; + tstate->trash_delete_nesting = this->trash_delete_nesting; +#endif // GREENLET_PY311 +} + +inline void PythonState::will_switch_from(PyThreadState *const origin_tstate) noexcept +{ +#if GREENLET_USE_CFRAME && !GREENLET_PY312 + // The weird thing is, we don't actually save this for an + // effect on the current greenlet, it's saved for an + // effect on the target greenlet. That is, we want + // continuity of this setting across the greenlet switch. + this->use_tracing = origin_tstate->cframe->use_tracing; +#endif +} + +void PythonState::set_initial_state(const PyThreadState* const tstate) noexcept +{ + this->_top_frame = nullptr; +#if GREENLET_PY312 + this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; + // XXX: TODO: Comment from a reviewer: + // Should this be ``C_RECURSION_LIMIT - tstate->c_recursion_remaining``? + // But to me it looks more like that might not be the right + // initialization either? + this->c_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; +#elif GREENLET_PY311 + this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; +#else + this->recursion_depth = tstate->recursion_depth; +#endif +} +// TODO: Better state management about when we own the top frame. +int PythonState::tp_traverse(visitproc visit, void* arg, bool own_top_frame) noexcept +{ + Py_VISIT(this->_context.borrow()); + if (own_top_frame) { + Py_VISIT(this->_top_frame.borrow()); + } + return 0; +} + +void PythonState::tp_clear(bool own_top_frame) noexcept +{ + PythonStateContext::tp_clear(); + // If we get here owning a frame, + // we got dealloc'd without being finished. We may or may not be + // in the same thread. + if (own_top_frame) { + this->_top_frame.CLEAR(); + } +} + +#if GREENLET_USE_CFRAME +void PythonState::set_new_cframe(_PyCFrame& frame) noexcept +{ + frame = *PyThreadState_GET()->cframe; + /* Make the target greenlet refer to the stack value. */ + this->cframe = &frame; + /* + And restore the link to the previous frame so this one gets + unliked appropriately. + */ + this->cframe->previous = &PyThreadState_GET()->root_cframe; +} +#endif + +const PythonState::OwnedFrame& PythonState::top_frame() const noexcept +{ + return this->_top_frame; +} + +void PythonState::did_finish(PyThreadState* tstate) noexcept +{ +#if GREENLET_PY311 + // See https://github.com/gevent/gevent/issues/1924 and + // https://github.com/python-greenlet/greenlet/issues/328. In + // short, Python 3.11 allocates memory for frames as a sort of + // linked list that's kept as part of PyThreadState in the + // ``datastack_chunk`` member and friends. These are saved and + // restored as part of switching greenlets. + // + // When we initially switch to a greenlet, we set those to NULL. + // That causes the frame management code to treat this like a + // brand new thread and start a fresh list of chunks, beginning + // with a new "root" chunk. As we make calls in this greenlet, + // those chunks get added, and as calls return, they get popped. + // But the frame code (pystate.c) is careful to make sure that the + // root chunk never gets popped. + // + // Thus, when a greenlet exits for the last time, there will be at + // least a single root chunk that we must be responsible for + // deallocating. + // + // The complex part is that these chunks are allocated and freed + // using ``_PyObject_VirtualAlloc``/``Free``. Those aren't public + // functions, and they aren't exported for linking. It so happens + // that we know they are just thin wrappers around the Arena + // allocator, so we can use that directly to deallocate in a + // compatible way. + // + // CAUTION: Check this implementation detail on every major version. + // + // It might be nice to be able to do this in our destructor, but + // can we be sure that no one else is using that memory? Plus, as + // described below, our pointers may not even be valid anymore. As + // a special case, there is one time that we know we can do this, + // and that's from the destructor of the associated UserGreenlet + // (NOT main greenlet) + PyObjectArenaAllocator alloc; + _PyStackChunk* chunk = nullptr; + if (tstate) { + // We really did finish, we can never be switched to again. + chunk = tstate->datastack_chunk; + // Unfortunately, we can't do much sanity checking. Our + // this->datastack_chunk pointer is out of date (evaluation may + // have popped down through it already) so we can't verify that + // we deallocate it. I don't think we can even check datastack_top + // for the same reason. + + PyObject_GetArenaAllocator(&alloc); + tstate->datastack_chunk = nullptr; + tstate->datastack_limit = nullptr; + tstate->datastack_top = nullptr; + + } + else if (this->datastack_chunk) { + // The UserGreenlet (NOT the main greenlet!) is being deallocated. If we're + // still holding a stack chunk, it's garbage because we know + // we can never switch back to let cPython clean it up. + // Because the last time we got switched away from, and we + // haven't run since then, we know our chain is valid and can + // be dealloced. + chunk = this->datastack_chunk; + PyObject_GetArenaAllocator(&alloc); + } + + if (alloc.free && chunk) { + // In case the arena mechanism has been torn down already. + while (chunk) { + _PyStackChunk *prev = chunk->previous; + chunk->previous = nullptr; + alloc.free(alloc.ctx, chunk, chunk->size); + chunk = prev; + } + } + + this->datastack_chunk = nullptr; + this->datastack_limit = nullptr; + this->datastack_top = nullptr; +#endif +} + + +}; // namespace greenlet + +#endif // GREENLET_PYTHON_STATE_CPP diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TStackState.cpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TStackState.cpp new file mode 100644 index 0000000..9aab596 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TStackState.cpp @@ -0,0 +1,265 @@ +#ifndef GREENLET_STACK_STATE_CPP +#define GREENLET_STACK_STATE_CPP + +#include "greenlet_greenlet.hpp" + +namespace greenlet { + +#ifdef GREENLET_USE_STDIO +#include +using std::cerr; +using std::endl; + +std::ostream& operator<<(std::ostream& os, const StackState& s) +{ + os << "StackState(stack_start=" << (void*)s._stack_start + << ", stack_stop=" << (void*)s.stack_stop + << ", stack_copy=" << (void*)s.stack_copy + << ", stack_saved=" << s._stack_saved + << ", stack_prev=" << s.stack_prev + << ", addr=" << &s + << ")"; + return os; +} +#endif + +StackState::StackState(void* mark, StackState& current) + : _stack_start(nullptr), + stack_stop((char*)mark), + stack_copy(nullptr), + _stack_saved(0), + /* Skip a dying greenlet */ + stack_prev(current._stack_start + ? ¤t + : current.stack_prev) +{ +} + +StackState::StackState() + : _stack_start(nullptr), + stack_stop(nullptr), + stack_copy(nullptr), + _stack_saved(0), + stack_prev(nullptr) +{ +} + +StackState::StackState(const StackState& other) +// can't use a delegating constructor because of +// MSVC for Python 2.7 + : _stack_start(nullptr), + stack_stop(nullptr), + stack_copy(nullptr), + _stack_saved(0), + stack_prev(nullptr) +{ + this->operator=(other); +} + +StackState& StackState::operator=(const StackState& other) +{ + if (&other == this) { + return *this; + } + if (other._stack_saved) { + throw std::runtime_error("Refusing to steal memory."); + } + + //If we have memory allocated, dispose of it + this->free_stack_copy(); + + this->_stack_start = other._stack_start; + this->stack_stop = other.stack_stop; + this->stack_copy = other.stack_copy; + this->_stack_saved = other._stack_saved; + this->stack_prev = other.stack_prev; + return *this; +} + +inline void StackState::free_stack_copy() noexcept +{ + PyMem_Free(this->stack_copy); + this->stack_copy = nullptr; + this->_stack_saved = 0; +} + +inline void StackState::copy_heap_to_stack(const StackState& current) noexcept +{ + + /* Restore the heap copy back into the C stack */ + if (this->_stack_saved != 0) { + memcpy(this->_stack_start, this->stack_copy, this->_stack_saved); + this->free_stack_copy(); + } + StackState* owner = const_cast(¤t); + if (!owner->_stack_start) { + owner = owner->stack_prev; /* greenlet is dying, skip it */ + } + while (owner && owner->stack_stop <= this->stack_stop) { + // cerr << "\tOwner: " << owner << endl; + owner = owner->stack_prev; /* find greenlet with more stack */ + } + this->stack_prev = owner; + // cerr << "\tFinished with: " << *this << endl; +} + +inline int StackState::copy_stack_to_heap_up_to(const char* const stop) noexcept +{ + /* Save more of g's stack into the heap -- at least up to 'stop' + g->stack_stop |________| + | | + | __ stop . . . . . + | | ==> . . + |________| _______ + | | | | + | | | | + g->stack_start | | |_______| g->stack_copy + */ + intptr_t sz1 = this->_stack_saved; + intptr_t sz2 = stop - this->_stack_start; + assert(this->_stack_start); + if (sz2 > sz1) { + char* c = (char*)PyMem_Realloc(this->stack_copy, sz2); + if (!c) { + PyErr_NoMemory(); + return -1; + } + memcpy(c + sz1, this->_stack_start + sz1, sz2 - sz1); + this->stack_copy = c; + this->_stack_saved = sz2; + } + return 0; +} + +inline int StackState::copy_stack_to_heap(char* const stackref, + const StackState& current) noexcept +{ + /* must free all the C stack up to target_stop */ + const char* const target_stop = this->stack_stop; + + StackState* owner = const_cast(¤t); + assert(owner->_stack_saved == 0); // everything is present on the stack + if (!owner->_stack_start) { + owner = owner->stack_prev; /* not saved if dying */ + } + else { + owner->_stack_start = stackref; + } + + while (owner->stack_stop < target_stop) { + /* ts_current is entierely within the area to free */ + if (owner->copy_stack_to_heap_up_to(owner->stack_stop)) { + return -1; /* XXX */ + } + owner = owner->stack_prev; + } + if (owner != this) { + if (owner->copy_stack_to_heap_up_to(target_stop)) { + return -1; /* XXX */ + } + } + return 0; +} + +inline bool StackState::started() const noexcept +{ + return this->stack_stop != nullptr; +} + +inline bool StackState::main() const noexcept +{ + return this->stack_stop == (char*)-1; +} + +inline bool StackState::active() const noexcept +{ + return this->_stack_start != nullptr; +} + +inline void StackState::set_active() noexcept +{ + assert(this->_stack_start == nullptr); + this->_stack_start = (char*)1; +} + +inline void StackState::set_inactive() noexcept +{ + this->_stack_start = nullptr; + // XXX: What if we still have memory out there? + // That case is actually triggered by + // test_issue251_issue252_explicit_reference_not_collectable (greenlet.tests.test_leaks.TestLeaks) + // and + // test_issue251_issue252_need_to_collect_in_background + // (greenlet.tests.test_leaks.TestLeaks) + // + // Those objects never get deallocated, so the destructor never + // runs. + // It *seems* safe to clean up the memory here? + if (this->_stack_saved) { + this->free_stack_copy(); + } +} + +inline intptr_t StackState::stack_saved() const noexcept +{ + return this->_stack_saved; +} + +inline char* StackState::stack_start() const noexcept +{ + return this->_stack_start; +} + + +inline StackState StackState::make_main() noexcept +{ + StackState s; + s._stack_start = (char*)1; + s.stack_stop = (char*)-1; + return s; +} + +StackState::~StackState() +{ + if (this->_stack_saved != 0) { + this->free_stack_copy(); + } +} + +void StackState::copy_from_stack(void* vdest, const void* vsrc, size_t n) const +{ + char* dest = static_cast(vdest); + const char* src = static_cast(vsrc); + if (src + n <= this->_stack_start + || src >= this->_stack_start + this->_stack_saved + || this->_stack_saved == 0) { + // Nothing we're copying was spilled from the stack + memcpy(dest, src, n); + return; + } + + if (src < this->_stack_start) { + // Copy the part before the saved stack. + // We know src + n > _stack_start due to the test above. + const size_t nbefore = this->_stack_start - src; + memcpy(dest, src, nbefore); + dest += nbefore; + src += nbefore; + n -= nbefore; + } + // We know src >= _stack_start after the before-copy, and + // src < _stack_start + _stack_saved due to the first if condition + size_t nspilled = std::min(n, this->_stack_start + this->_stack_saved - src); + memcpy(dest, this->stack_copy + (src - this->_stack_start), nspilled); + dest += nspilled; + src += nspilled; + n -= nspilled; + if (n > 0) { + // Copy the part after the saved stack + memcpy(dest, src, n); + } +} + +}; // namespace greenlet + +#endif // GREENLET_STACK_STATE_CPP diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TThreadStateDestroy.cpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TThreadStateDestroy.cpp new file mode 100644 index 0000000..a149a1a --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TThreadStateDestroy.cpp @@ -0,0 +1,195 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ +/** + * Implementation of the ThreadState destructors. + * + * Format with: + * clang-format -i --style=file src/greenlet/greenlet.c + * + * + * Fix missing braces with: + * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" +*/ +#ifndef T_THREADSTATE_DESTROY +#define T_THREADSTATE_DESTROY + +#include "greenlet_greenlet.hpp" +#include "greenlet_thread_state.hpp" +#include "greenlet_thread_support.hpp" +#include "greenlet_cpython_add_pending.hpp" +#include "TGreenletGlobals.cpp" + +namespace greenlet { + +struct ThreadState_DestroyWithGIL +{ + ThreadState_DestroyWithGIL(ThreadState* state) + { + if (state && state->has_main_greenlet()) { + DestroyWithGIL(state); + } + } + + static int + DestroyWithGIL(ThreadState* state) + { + // Holding the GIL. + // Passed a non-shared pointer to the actual thread state. + // state -> main greenlet + assert(state->has_main_greenlet()); + PyGreenlet* main(state->borrow_main_greenlet()); + // When we need to do cross-thread operations, we check this. + // A NULL value means the thread died some time ago. + // We do this here, rather than in a Python dealloc function + // for the greenlet, in case there's still a reference out + // there. + static_cast(main->pimpl)->thread_state(nullptr); + + delete state; // Deleting this runs the destructor, DECREFs the main greenlet. + return 0; + } +}; + + + +struct ThreadState_DestroyNoGIL +{ + // ensure this is actually defined. + static_assert(GREENLET_BROKEN_PY_ADD_PENDING == 1 || GREENLET_BROKEN_PY_ADD_PENDING == 0, + "GREENLET_BROKEN_PY_ADD_PENDING not defined correctly."); + +#if GREENLET_BROKEN_PY_ADD_PENDING + static int _push_pending_call(struct _pending_calls *pending, + int (*func)(void *), void *arg) + { + int i = pending->last; + int j = (i + 1) % NPENDINGCALLS; + if (j == pending->first) { + return -1; /* Queue full */ + } + pending->calls[i].func = func; + pending->calls[i].arg = arg; + pending->last = j; + return 0; + } + + static int AddPendingCall(int (*func)(void *), void *arg) + { + _PyRuntimeState *runtime = &_PyRuntime; + if (!runtime) { + // obviously impossible + return 0; + } + struct _pending_calls *pending = &runtime->ceval.pending; + if (!pending->lock) { + return 0; + } + int result = 0; + PyThread_acquire_lock(pending->lock, WAIT_LOCK); + if (!pending->finishing) { + result = _push_pending_call(pending, func, arg); + } + PyThread_release_lock(pending->lock); + SIGNAL_PENDING_CALLS(&runtime->ceval); + return result; + } +#else + // Python < 3.8 or >= 3.9 + static int AddPendingCall(int (*func)(void*), void* arg) + { + return Py_AddPendingCall(func, arg); + } +#endif + + ThreadState_DestroyNoGIL(ThreadState* state) + { + // We are *NOT* holding the GIL. Our thread is in the middle + // of its death throes and the Python thread state is already + // gone so we can't use most Python APIs. One that is safe is + // ``Py_AddPendingCall``, unless the interpreter itself has + // been torn down. There is a limited number of calls that can + // be queued: 32 (NPENDINGCALLS) in CPython 3.10, so we + // coalesce these calls using our own queue. + if (state && state->has_main_greenlet()) { + // mark the thread as dead ASAP. + // this is racy! If we try to throw or switch to a + // greenlet from this thread from some other thread before + // we clear the state pointer, it won't realize the state + // is dead which can crash the process. + PyGreenlet* p = state->borrow_main_greenlet(); + assert(p->pimpl->thread_state() == state || p->pimpl->thread_state() == nullptr); + static_cast(p->pimpl)->thread_state(nullptr); + } + + // NOTE: Because we're not holding the GIL here, some other + // Python thread could run and call ``os.fork()``, which would + // be bad if that happenend while we are holding the cleanup + // lock (it wouldn't function in the child process). + // Make a best effort to try to keep the duration we hold the + // lock short. + // TODO: On platforms that support it, use ``pthread_atfork`` to + // drop this lock. + LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock); + + if (state && state->has_main_greenlet()) { + // Because we don't have the GIL, this is a race condition. + if (!PyInterpreterState_Head()) { + // We have to leak the thread state, if the + // interpreter has shut down when we're getting + // deallocated, we can't run the cleanup code that + // deleting it would imply. + return; + } + + mod_globs->queue_to_destroy(state); + if (mod_globs->thread_states_to_destroy.size() == 1) { + // We added the first item to the queue. We need to schedule + // the cleanup. + int result = ThreadState_DestroyNoGIL::AddPendingCall( + ThreadState_DestroyNoGIL::DestroyQueueWithGIL, + NULL); + if (result < 0) { + // Hmm, what can we do here? + fprintf(stderr, + "greenlet: WARNING: failed in call to Py_AddPendingCall; " + "expect a memory leak.\n"); + } + } + } + } + + static int + DestroyQueueWithGIL(void* UNUSED(arg)) + { + // We're holding the GIL here, so no Python code should be able to + // run to call ``os.fork()``. + while (1) { + ThreadState* to_destroy; + { + LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock); + if (mod_globs->thread_states_to_destroy.empty()) { + break; + } + to_destroy = mod_globs->take_next_to_destroy(); + } + // Drop the lock while we do the actual deletion. + ThreadState_DestroyWithGIL::DestroyWithGIL(to_destroy); + } + return 0; + } + +}; + +}; // namespace greenlet + +// The intent when GET_THREAD_STATE() is needed multiple times in a +// function is to take a reference to its return value in a local +// variable, to avoid the thread-local indirection. On some platforms +// (macOS), accessing a thread-local involves a function call (plus an +// initial function call in each function that uses a thread local); +// in contrast, static volatile variables are at some pre-computed +// offset. +typedef greenlet::ThreadStateCreator ThreadStateCreator; +static thread_local ThreadStateCreator g_thread_state_global; +#define GET_THREAD_STATE() g_thread_state_global + +#endif //T_THREADSTATE_DESTROY diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TUserGreenlet.cpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TUserGreenlet.cpp new file mode 100644 index 0000000..495a794 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/TUserGreenlet.cpp @@ -0,0 +1,667 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ +/** + * Implementation of greenlet::UserGreenlet. + * + * Format with: + * clang-format -i --style=file src/greenlet/greenlet.c + * + * + * Fix missing braces with: + * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" +*/ + +#include "greenlet_internal.hpp" +#include "greenlet_greenlet.hpp" +#include "greenlet_thread_state.hpp" +#include "TThreadStateDestroy.cpp" + + +namespace greenlet { +using greenlet::refs::BorrowedMainGreenlet; +greenlet::PythonAllocator UserGreenlet::allocator; + +void* UserGreenlet::operator new(size_t UNUSED(count)) +{ + return allocator.allocate(1); +} + + +void UserGreenlet::operator delete(void* ptr) +{ + return allocator.deallocate(static_cast(ptr), + 1); +} + + +UserGreenlet::UserGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent) + : Greenlet(p), _parent(the_parent) +{ + this->_self = p; +} + +UserGreenlet::~UserGreenlet() +{ + // Python 3.11: If we don't clear out the raw frame datastack + // when deleting an unfinished greenlet, + // TestLeaks.test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_main fails. + this->python_state.did_finish(nullptr); + this->tp_clear(); +} + +BorrowedGreenlet +UserGreenlet::self() const noexcept +{ + return this->_self; +} + + + +const BorrowedMainGreenlet +UserGreenlet::main_greenlet() const +{ + return this->_main_greenlet; +} + + +BorrowedMainGreenlet +UserGreenlet::find_main_greenlet_in_lineage() const +{ + if (this->started()) { + assert(this->_main_greenlet); + return BorrowedMainGreenlet(this->_main_greenlet); + } + + if (!this->_parent) { + /* garbage collected greenlet in chain */ + // XXX: WHAT? + return BorrowedMainGreenlet(nullptr); + } + + return this->_parent->find_main_greenlet_in_lineage(); +} + + +/** + * CAUTION: This will allocate memory and may trigger garbage + * collection and arbitrary Python code. + */ +OwnedObject +UserGreenlet::throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state) +{ + /* The dying greenlet cannot be a parent of ts_current + because the 'parent' field chain would hold a + reference */ + UserGreenlet::ParentIsCurrentGuard with_current_parent(this, current_thread_state); + + // We don't care about the return value, only whether an + // exception happened. Whether or not an exception happens, + // we need to restore the parent in case the greenlet gets + // resurrected. + return Greenlet::throw_GreenletExit_during_dealloc(current_thread_state); +} + +ThreadState* +UserGreenlet::thread_state() const noexcept +{ + // TODO: maybe make this throw, if the thread state isn't there? + // if (!this->main_greenlet) { + // throw std::runtime_error("No thread state"); // TODO: Better exception + // } + if (!this->_main_greenlet) { + return nullptr; + } + return this->_main_greenlet->thread_state(); +} + + +bool +UserGreenlet::was_running_in_dead_thread() const noexcept +{ + return this->_main_greenlet && !this->thread_state(); +} + +OwnedObject +UserGreenlet::g_switch() +{ + assert(this->args() || PyErr_Occurred()); + + try { + this->check_switch_allowed(); + } + catch (const PyErrOccurred&) { + this->release_args(); + throw; + } + + // Switching greenlets used to attempt to clean out ones that need + // deleted *if* we detected a thread switch. Should it still do + // that? + // An issue is that if we delete a greenlet from another thread, + // it gets queued to this thread, and ``kill_greenlet()`` switches + // back into the greenlet + + /* find the real target by ignoring dead greenlets, + and if necessary starting a greenlet. */ + switchstack_result_t err; + Greenlet* target = this; + // TODO: probably cleaner to handle the case where we do + // switch to ourself separately from the other cases. + // This can probably even further be simplified if we keep + // track of the switching_state we're going for and just call + // into g_switch() if it's not ourself. The main problem with that + // is that we would be using more stack space. + bool target_was_me = true; + bool was_initial_stub = false; + while (target) { + if (target->active()) { + if (!target_was_me) { + target->args() <<= this->args(); + assert(!this->args()); + } + err = target->g_switchstack(); + break; + } + if (!target->started()) { + // We never encounter a main greenlet that's not started. + assert(!target->main()); + UserGreenlet* real_target = static_cast(target); + assert(real_target); + void* dummymarker; + was_initial_stub = true; + if (!target_was_me) { + target->args() <<= this->args(); + assert(!this->args()); + } + try { + // This can only throw back to us while we're + // still in this greenlet. Once the new greenlet + // is bootstrapped, it has its own exception state. + err = real_target->g_initialstub(&dummymarker); + } + catch (const PyErrOccurred&) { + this->release_args(); + throw; + } + catch (const GreenletStartedWhileInPython&) { + // The greenlet was started sometime before this + // greenlet actually switched to it, i.e., + // "concurrent" calls to switch() or throw(). + // We need to retry the switch. + // Note that the current greenlet has been reset + // to this one (or we wouldn't be running!) + continue; + } + break; + } + + target = target->parent(); + target_was_me = false; + } + // The ``this`` pointer and all other stack or register based + // variables are invalid now, at least where things succeed + // above. + // But this one, probably not so much? It's not clear if it's + // safe to throw an exception at this point. + + if (err.status < 0) { + // If we get here, either g_initialstub() + // failed, or g_switchstack() failed. Either one of those + // cases SHOULD leave us in the original greenlet with a valid + // stack. + return this->on_switchstack_or_initialstub_failure(target, err, target_was_me, was_initial_stub); + } + + // err.the_new_current_greenlet would be the same as ``target``, + // if target wasn't probably corrupt. + return err.the_new_current_greenlet->g_switch_finish(err); +} + + + +Greenlet::switchstack_result_t +UserGreenlet::g_initialstub(void* mark) +{ + OwnedObject run; + + // We need to grab a reference to the current switch arguments + // in case we're entered concurrently during the call to + // GetAttr() and have to try again. + // We'll restore them when we return in that case. + // Scope them tightly to avoid ref leaks. + { + SwitchingArgs args(this->args()); + + /* save exception in case getattr clears it */ + PyErrPieces saved; + + /* + self.run is the object to call in the new greenlet. + This could run arbitrary python code and switch greenlets! + */ + run = this->_self.PyRequireAttr(mod_globs->str_run); + /* restore saved exception */ + saved.PyErrRestore(); + + + /* recheck that it's safe to switch in case greenlet reparented anywhere above */ + this->check_switch_allowed(); + + /* by the time we got here another start could happen elsewhere, + * that means it should now be a regular switch. + * This can happen if the Python code is a subclass that implements + * __getattribute__ or __getattr__, or makes ``run`` a descriptor; + * all of those can run arbitrary code that switches back into + * this greenlet. + */ + if (this->stack_state.started()) { + // the successful switch cleared these out, we need to + // restore our version. They will be copied on up to the + // next target. + assert(!this->args()); + this->args() <<= args; + throw GreenletStartedWhileInPython(); + } + } + + // Sweet, if we got here, we have the go-ahead and will switch + // greenlets. + // Nothing we do from here on out should allow for a thread or + // greenlet switch: No arbitrary calls to Python, including + // decref'ing + +#if GREENLET_USE_CFRAME + /* OK, we need it, we're about to switch greenlets, save the state. */ + /* + See green_new(). This is a stack-allocated variable used + while *self* is in PyObject_Call(). + We want to defer copying the state info until we're sure + we need it and are in a stable place to do so. + */ + _PyCFrame trace_info; + + this->python_state.set_new_cframe(trace_info); +#endif + /* start the greenlet */ + ThreadState& thread_state = GET_THREAD_STATE().state(); + this->stack_state = StackState(mark, + thread_state.borrow_current()->stack_state); + this->python_state.set_initial_state(PyThreadState_GET()); + this->exception_state.clear(); + this->_main_greenlet = thread_state.get_main_greenlet(); + + /* perform the initial switch */ + switchstack_result_t err = this->g_switchstack(); + /* returns twice! + The 1st time with ``err == 1``: we are in the new greenlet. + This one owns a greenlet that used to be current. + The 2nd time with ``err <= 0``: back in the caller's + greenlet; this happens if the child finishes or switches + explicitly to us. Either way, the ``err`` variable is + created twice at the same memory location, but possibly + having different ``origin`` values. Note that it's not + constructed for the second time until the switch actually happens. + */ + if (err.status == 1) { + // In the new greenlet. + + // This never returns! Calling inner_bootstrap steals + // the contents of our run object within this stack frame, so + // it is not valid to do anything with it. + try { + this->inner_bootstrap(err.origin_greenlet.relinquish_ownership(), + run.relinquish_ownership()); + } + // Getting a C++ exception here isn't good. It's probably a + // bug in the underlying greenlet, meaning it's probably a + // C++ extension. We're going to abort anyway, but try to + // display some nice information *if* possible. Some obscure + // platforms don't properly support this (old 32-bit Arm, see see + // https://github.com/python-greenlet/greenlet/issues/385); that's not + // great, but should usually be OK because, as mentioned above, we're + // terminating anyway. + // + // The catching is tested by + // ``test_cpp.CPPTests.test_unhandled_exception_in_greenlet_aborts``. + // + // PyErrOccurred can theoretically be thrown by + // inner_bootstrap() -> g_switch_finish(), but that should + // never make it back to here. It is a std::exception and + // would be caught if it is. + catch (const std::exception& e) { + std::string base = "greenlet: Unhandled C++ exception: "; + base += e.what(); + Py_FatalError(base.c_str()); + } + catch (...) { + // Some compilers/runtimes use exceptions internally. + // It appears that GCC on Linux with libstdc++ throws an + // exception internally at process shutdown time to unwind + // stacks and clean up resources. Depending on exactly + // where we are when the process exits, that could result + // in an unknown exception getting here. If we + // Py_FatalError() or abort() here, we interfere with + // orderly process shutdown. Throwing the exception on up + // is the right thing to do. + // + // gevent's ``examples/dns_mass_resolve.py`` demonstrates this. +#ifndef NDEBUG + fprintf(stderr, + "greenlet: inner_bootstrap threw unknown exception; " + "is the process terminating?\n"); +#endif + throw; + } + Py_FatalError("greenlet: inner_bootstrap returned with no exception.\n"); + } + + + // In contrast, notice that we're keeping the origin greenlet + // around as an owned reference; we need it to call the trace + // function for the switch back into the parent. It was only + // captured at the time the switch actually happened, though, + // so we haven't been keeping an extra reference around this + // whole time. + + /* back in the parent */ + if (err.status < 0) { + /* start failed badly, restore greenlet state */ + this->stack_state = StackState(); + this->_main_greenlet.CLEAR(); + // CAUTION: This may run arbitrary Python code. + run.CLEAR(); // inner_bootstrap didn't run, we own the reference. + } + + // In the success case, the spawned code (inner_bootstrap) will + // take care of decrefing this, so we relinquish ownership so as + // to not double-decref. + + run.relinquish_ownership(); + + return err; +} + + +void +UserGreenlet::inner_bootstrap(PyGreenlet* origin_greenlet, PyObject* run) +{ + // The arguments here would be another great place for move. + // As it is, we take them as a reference so that when we clear + // them we clear what's on the stack above us. Do that NOW, and + // without using a C++ RAII object, + // so there's no way that exiting the parent frame can clear it, + // or we clear it unexpectedly. This arises in the context of the + // interpreter shutting down. See https://github.com/python-greenlet/greenlet/issues/325 + //PyObject* run = _run.relinquish_ownership(); + + /* in the new greenlet */ + assert(this->thread_state()->borrow_current() == this->_self); + // C++ exceptions cannot propagate to the parent greenlet from + // here. (TODO: Do we need a catch(...) clause, perhaps on the + // function itself? ALl we could do is terminate the program.) + // NOTE: On 32-bit Windows, the call chain is extremely + // important here in ways that are subtle, having to do with + // the depth of the SEH list. The call to restore it MUST NOT + // add a new SEH handler to the list, or we'll restore it to + // the wrong thing. + this->thread_state()->restore_exception_state(); + /* stack variables from above are no good and also will not unwind! */ + // EXCEPT: That can't be true, we access run, among others, here. + + this->stack_state.set_active(); /* running */ + + // We're about to possibly run Python code again, which + // could switch back/away to/from us, so we need to grab the + // arguments locally. + SwitchingArgs args; + args <<= this->args(); + assert(!this->args()); + + // XXX: We could clear this much earlier, right? + // Or would that introduce the possibility of running Python + // code when we don't want to? + // CAUTION: This may run arbitrary Python code. + this->_run_callable.CLEAR(); + + + // The first switch we need to manually call the trace + // function here instead of in g_switch_finish, because we + // never return there. + if (OwnedObject tracefunc = this->thread_state()->get_tracefunc()) { + OwnedGreenlet trace_origin; + trace_origin = origin_greenlet; + try { + g_calltrace(tracefunc, + args ? mod_globs->event_switch : mod_globs->event_throw, + trace_origin, + this->_self); + } + catch (const PyErrOccurred&) { + /* Turn trace errors into switch throws */ + args.CLEAR(); + } + } + + // We no longer need the origin, it was only here for + // tracing. + // We may never actually exit this stack frame so we need + // to explicitly clear it. + // This could run Python code and switch. + Py_CLEAR(origin_greenlet); + + OwnedObject result; + if (!args) { + /* pending exception */ + result = NULL; + } + else { + /* call g.run(*args, **kwargs) */ + // This could result in further switches + try { + //result = run.PyCall(args.args(), args.kwargs()); + // CAUTION: Just invoking this, before the function even + // runs, may cause memory allocations, which may trigger + // GC, which may run arbitrary Python code. + result = OwnedObject::consuming(PyObject_Call(run, args.args().borrow(), args.kwargs().borrow())); + } + catch (...) { + // Unhandled C++ exception! + + // If we declare ourselves as noexcept, if we don't catch + // this here, most platforms will just abort() the + // process. But on 64-bit Windows with older versions of + // the C runtime, this can actually corrupt memory and + // just return. We see this when compiling with the + // Windows 7.0 SDK targeting Windows Server 2008, but not + // when using the Appveyor Visual Studio 2019 image. So + // this currently only affects Python 2.7 on Windows 64. + // That is, the tests pass and the runtime aborts + // everywhere else. + // + // However, if we catch it and try to continue with a + // Python error, then all Windows 64 bit platforms corrupt + // memory. So all we can do is manually abort, hopefully + // with a good error message. (Note that the above was + // tested WITHOUT the `/EHr` switch being used at compile + // time, so MSVC may have "optimized" out important + // checking. Using that switch, we may be in a better + // place in terms of memory corruption.) But sometimes it + // can't be caught here at all, which is confusing but not + // terribly surprising; so again, the G_NOEXCEPT_WIN32 + // plus "/EHr". + // + // Hopefully the basic C stdlib is still functional enough + // for us to at least print an error. + // + // It gets more complicated than that, though, on some + // platforms, specifically at least Linux/gcc/libstdc++. They use + // an exception to unwind the stack when a background + // thread exits. (See comments about noexcept.) So this + // may not actually represent anything untoward. On those + // platforms we allow throws of this to propagate, or + // attempt to anyway. +# if defined(WIN32) || defined(_WIN32) + Py_FatalError( + "greenlet: Unhandled C++ exception from a greenlet run function. " + "Because memory is likely corrupted, terminating process."); + std::abort(); +#else + throw; +#endif + } + } + // These lines may run arbitrary code + args.CLEAR(); + Py_CLEAR(run); + + if (!result + && mod_globs->PyExc_GreenletExit.PyExceptionMatches() + && (this->args())) { + // This can happen, for example, if our only reference + // goes away after we switch back to the parent. + // See test_dealloc_switch_args_not_lost + PyErrPieces clear_error; + result <<= this->args(); + result = single_result(result); + } + this->release_args(); + this->python_state.did_finish(PyThreadState_GET()); + + result = g_handle_exit(result); + assert(this->thread_state()->borrow_current() == this->_self); + + /* jump back to parent */ + this->stack_state.set_inactive(); /* dead */ + + + // TODO: Can we decref some things here? Release our main greenlet + // and maybe parent? + for (Greenlet* parent = this->_parent; + parent; + parent = parent->parent()) { + // We need to somewhere consume a reference to + // the result; in most cases we'll never have control + // back in this stack frame again. Calling + // green_switch actually adds another reference! + // This would probably be clearer with a specific API + // to hand results to the parent. + parent->args() <<= result; + assert(!result); + // The parent greenlet now owns the result; in the + // typical case we'll never get back here to assign to + // result and thus release the reference. + try { + result = parent->g_switch(); + } + catch (const PyErrOccurred&) { + // Ignore, keep passing the error on up. + } + + /* Return here means switch to parent failed, + * in which case we throw *current* exception + * to the next parent in chain. + */ + assert(!result); + } + /* We ran out of parents, cannot continue */ + PyErr_WriteUnraisable(this->self().borrow_o()); + Py_FatalError("greenlet: ran out of parent greenlets while propagating exception; " + "cannot continue"); + std::abort(); +} + +void +UserGreenlet::run(const BorrowedObject nrun) +{ + if (this->started()) { + throw AttributeError( + "run cannot be set " + "after the start of the greenlet"); + } + this->_run_callable = nrun; +} + +const OwnedGreenlet +UserGreenlet::parent() const +{ + return this->_parent; +} + +void +UserGreenlet::parent(const BorrowedObject raw_new_parent) +{ + if (!raw_new_parent) { + throw AttributeError("can't delete attribute"); + } + + BorrowedMainGreenlet main_greenlet_of_new_parent; + BorrowedGreenlet new_parent(raw_new_parent.borrow()); // could + // throw + // TypeError! + for (BorrowedGreenlet p = new_parent; p; p = p->parent()) { + if (p == this->_self) { + throw ValueError("cyclic parent chain"); + } + main_greenlet_of_new_parent = p->main_greenlet(); + } + + if (!main_greenlet_of_new_parent) { + throw ValueError("parent must not be garbage collected"); + } + + if (this->started() + && this->_main_greenlet != main_greenlet_of_new_parent) { + throw ValueError("parent cannot be on a different thread"); + } + + this->_parent = new_parent; +} + +void +UserGreenlet::murder_in_place() +{ + this->_main_greenlet.CLEAR(); + Greenlet::murder_in_place(); +} + +bool +UserGreenlet::belongs_to_thread(const ThreadState* thread_state) const +{ + return Greenlet::belongs_to_thread(thread_state) && this->_main_greenlet == thread_state->borrow_main_greenlet(); +} + + +int +UserGreenlet::tp_traverse(visitproc visit, void* arg) +{ + Py_VISIT(this->_parent.borrow_o()); + Py_VISIT(this->_main_greenlet.borrow_o()); + Py_VISIT(this->_run_callable.borrow_o()); + + return Greenlet::tp_traverse(visit, arg); +} + +int +UserGreenlet::tp_clear() +{ + Greenlet::tp_clear(); + this->_parent.CLEAR(); + this->_main_greenlet.CLEAR(); + this->_run_callable.CLEAR(); + return 0; +} + +UserGreenlet::ParentIsCurrentGuard::ParentIsCurrentGuard(UserGreenlet* p, + const ThreadState& thread_state) + : oldparent(p->_parent), + greenlet(p) +{ + p->_parent = thread_state.get_current(); +} + +UserGreenlet::ParentIsCurrentGuard::~ParentIsCurrentGuard() +{ + this->greenlet->_parent = oldparent; + oldparent.CLEAR(); +} + +}; //namespace greenlet diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/__init__.py new file mode 100644 index 0000000..298a19d --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/__init__.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" +The root of the greenlet package. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +__all__ = [ + '__version__', + '_C_API', + + 'GreenletExit', + 'error', + + 'getcurrent', + 'greenlet', + + 'gettrace', + 'settrace', +] + +# pylint:disable=no-name-in-module + +### +# Metadata +### +__version__ = '3.0.3' +from ._greenlet import _C_API # pylint:disable=no-name-in-module + +### +# Exceptions +### +from ._greenlet import GreenletExit +from ._greenlet import error + +### +# greenlets +### +from ._greenlet import getcurrent +from ._greenlet import greenlet + +### +# tracing +### +try: + from ._greenlet import gettrace + from ._greenlet import settrace +except ImportError: + # Tracing wasn't supported. + # XXX: The option to disable it was removed in 1.0, + # so this branch should be dead code. + pass + +### +# Constants +# These constants aren't documented and aren't recommended. +# In 1.0, USE_GC and USE_TRACING are always true, and USE_CONTEXT_VARS +# is the same as ``sys.version_info[:2] >= 3.7`` +### +from ._greenlet import GREENLET_USE_CONTEXT_VARS # pylint:disable=unused-import +from ._greenlet import GREENLET_USE_GC # pylint:disable=unused-import +from ._greenlet import GREENLET_USE_TRACING # pylint:disable=unused-import + +# Controlling the use of the gc module. Provisional API for this greenlet +# implementation in 2.0. +from ._greenlet import CLOCKS_PER_SEC # pylint:disable=unused-import +from ._greenlet import enable_optional_cleanup # pylint:disable=unused-import +from ._greenlet import get_clocks_used_doing_optional_cleanup # pylint:disable=unused-import + +# Other APIS in the _greenlet module are for test support. diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/_greenlet.cpython-310-x86_64-linux-gnu.so b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/_greenlet.cpython-310-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..4ab595ef8a3b454cd7bedc766fa15a83a3f157a8 GIT binary patch literal 1498632 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≶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

RorFPx_E@Ix43ETYq#K7YDRLuXJPk3f&E)Uk7ac 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�pyoC@fb;kSFTA)tGdYV5 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

KMfsjG^<8z%1r;DFoKHksY3>NU(@=C32FXp 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 z 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 N?*H>%2k&d;{{VqZ?*IS* literal 0 HcmV?d00001 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet.cpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet.cpp new file mode 100644 index 0000000..5a9818e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet.cpp @@ -0,0 +1,1494 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ +/* Format with: + * clang-format -i --style=file src/greenlet/greenlet.c + * + * + * Fix missing braces with: + * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" +*/ +#include +#include +#include +#include + + +#define PY_SSIZE_T_CLEAN +#include +#include "structmember.h" // PyMemberDef + +#include "greenlet_internal.hpp" +// Code after this point can assume access to things declared in stdint.h, +// including the fixed-width types. This goes for the platform-specific switch functions +// as well. +#include "greenlet_refs.hpp" +#include "greenlet_slp_switch.hpp" +#include "greenlet_thread_state.hpp" +#include "greenlet_thread_support.hpp" +#include "greenlet_greenlet.hpp" + +#include "TGreenletGlobals.cpp" +#include "TThreadStateDestroy.cpp" +#include "TGreenlet.cpp" +#include "TMainGreenlet.cpp" +#include "TUserGreenlet.cpp" +#include "TBrokenGreenlet.cpp" +#include "TExceptionState.cpp" +#include "TPythonState.cpp" +#include "TStackState.cpp" + + +using greenlet::LockGuard; +using greenlet::LockInitError; +using greenlet::PyErrOccurred; +using greenlet::Require; + +using greenlet::g_handle_exit; +using greenlet::single_result; + +using greenlet::Greenlet; +using greenlet::UserGreenlet; +using greenlet::MainGreenlet; +using greenlet::BrokenGreenlet; +using greenlet::ThreadState; +using greenlet::PythonState; + + + +// ******* Implementation of things from included files +template +greenlet::refs::_BorrowedGreenlet& greenlet::refs::_BorrowedGreenlet::operator=(const greenlet::refs::BorrowedObject& other) +{ + this->_set_raw_pointer(static_cast(other)); + return *this; +} + +template +inline greenlet::refs::_BorrowedGreenlet::operator Greenlet*() const noexcept +{ + if (!this->p) { + return nullptr; + } + return reinterpret_cast(this->p)->pimpl; +} + +template +greenlet::refs::_BorrowedGreenlet::_BorrowedGreenlet(const BorrowedObject& p) + : BorrowedReference(nullptr) +{ + + this->_set_raw_pointer(p.borrow()); +} + +template +inline greenlet::refs::_OwnedGreenlet::operator Greenlet*() const noexcept +{ + if (!this->p) { + return nullptr; + } + return reinterpret_cast(this->p)->pimpl; +} + + + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wmissing-field-initializers" +# pragma clang diagnostic ignored "-Wwritable-strings" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +// warning: ISO C++ forbids converting a string constant to ‘char*’ +// (The python APIs aren't const correct and accept writable char*) +# pragma GCC diagnostic ignored "-Wwrite-strings" +#endif + + +/*********************************************************** + +A PyGreenlet is a range of C stack addresses that must be +saved and restored in such a way that the full range of the +stack contains valid data when we switch to it. + +Stack layout for a greenlet: + + | ^^^ | + | older data | + | | + stack_stop . |_______________| + . | | + . | greenlet data | + . | in stack | + . * |_______________| . . _____________ stack_copy + stack_saved + . | | | | + . | data | |greenlet data| + . | unrelated | | saved | + . | to | | in heap | + stack_start . | this | . . |_____________| stack_copy + | greenlet | + | | + | newer data | + | vvv | + + +Note that a greenlet's stack data is typically partly at its correct +place in the stack, and partly saved away in the heap, but always in +the above configuration: two blocks, the more recent one in the heap +and the older one still in the stack (either block may be empty). + +Greenlets are chained: each points to the previous greenlet, which is +the one that owns the data currently in the C stack above my +stack_stop. The currently running greenlet is the first element of +this chain. The main (initial) greenlet is the last one. Greenlets +whose stack is entirely in the heap can be skipped from the chain. + +The chain is not related to execution order, but only to the order +in which bits of C stack happen to belong to greenlets at a particular +point in time. + +The main greenlet doesn't have a stack_stop: it is responsible for the +complete rest of the C stack, and we don't know where it begins. We +use (char*) -1, the largest possible address. + +States: + stack_stop == NULL && stack_start == NULL: did not start yet + stack_stop != NULL && stack_start == NULL: already finished + stack_stop != NULL && stack_start != NULL: active + +The running greenlet's stack_start is undefined but not NULL. + + ***********************************************************/ + +static PyGreenlet* +green_create_main(ThreadState* state) +{ + PyGreenlet* gmain; + + /* create the main greenlet for this thread */ + gmain = (PyGreenlet*)PyType_GenericAlloc(&PyGreenlet_Type, 0); + if (gmain == NULL) { + Py_FatalError("green_create_main failed to alloc"); + return NULL; + } + new MainGreenlet(gmain, state); + + assert(Py_REFCNT(gmain) == 1); + return gmain; +} + + + +/***********************************************************/ + +/* Some functions must not be inlined: + * slp_restore_state, when inlined into slp_switch might cause + it to restore stack over its own local variables + * slp_save_state, when inlined would add its own local + variables to the saved stack, wasting space + * slp_switch, cannot be inlined for obvious reasons + * g_initialstub, when inlined would receive a pointer into its + own stack frame, leading to incomplete stack save/restore + +g_initialstub is a member function and declared virtual so that the +compiler always calls it through a vtable. + +slp_save_state and slp_restore_state are also member functions. They +are called from trampoline functions that themselves are declared as +not eligible for inlining. +*/ + +extern "C" { +static int GREENLET_NOINLINE(slp_save_state_trampoline)(char* stackref) +{ + return switching_thread_state->slp_save_state(stackref); +} +static void GREENLET_NOINLINE(slp_restore_state_trampoline)() +{ + switching_thread_state->slp_restore_state(); +} +} + + +/***********************************************************/ + +static PyGreenlet* +green_new(PyTypeObject* type, PyObject* UNUSED(args), PyObject* UNUSED(kwds)) +{ + PyGreenlet* o = + (PyGreenlet*)PyBaseObject_Type.tp_new(type, mod_globs->empty_tuple, mod_globs->empty_dict); + if (o) { + new UserGreenlet(o, GET_THREAD_STATE().state().borrow_current()); + assert(Py_REFCNT(o) == 1); + } + return o; +} + +static PyGreenlet* +green_unswitchable_new(PyTypeObject* type, PyObject* UNUSED(args), PyObject* UNUSED(kwds)) +{ + PyGreenlet* o = + (PyGreenlet*)PyBaseObject_Type.tp_new(type, mod_globs->empty_tuple, mod_globs->empty_dict); + if (o) { + new BrokenGreenlet(o, GET_THREAD_STATE().state().borrow_current()); + assert(Py_REFCNT(o) == 1); + } + return o; +} + +static int +green_setrun(BorrowedGreenlet self, BorrowedObject nrun, void* c); +static int +green_setparent(BorrowedGreenlet self, BorrowedObject nparent, void* c); + +static int +green_init(BorrowedGreenlet self, BorrowedObject args, BorrowedObject kwargs) +{ + PyArgParseParam run; + PyArgParseParam nparent; + static const char* const kwlist[] = { + "run", + "parent", + NULL + }; + + // recall: The O specifier does NOT increase the reference count. + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, "|OO:green", (char**)kwlist, &run, &nparent)) { + return -1; + } + + if (run) { + if (green_setrun(self, run, NULL)) { + return -1; + } + } + if (nparent && !nparent.is_None()) { + return green_setparent(self, nparent, NULL); + } + return 0; +} + + + +static int +green_traverse(PyGreenlet* self, visitproc visit, void* arg) +{ + // We must only visit referenced objects, i.e. only objects + // Py_INCREF'ed by this greenlet (directly or indirectly): + // + // - stack_prev is not visited: holds previous stack pointer, but it's not + // referenced + // - frames are not visited as we don't strongly reference them; + // alive greenlets are not garbage collected + // anyway. This can be a problem, however, if this greenlet is + // never allowed to finish, and is referenced from the frame: we + // have an uncollectible cycle in that case. Note that the + // frame object itself is also frequently not even tracked by the GC + // starting with Python 3.7 (frames are allocated by the + // interpreter untracked, and only become tracked when their + // evaluation is finished if they have a refcount > 1). All of + // this is to say that we should probably strongly reference + // the frame object. Doing so, while always allowing GC on a + // greenlet, solves several leaks for us. + + Py_VISIT(self->dict); + if (!self->pimpl) { + // Hmm. I have seen this at interpreter shutdown time, + // I think. That's very odd because this doesn't go away until + // we're ``green_dealloc()``, at which point we shouldn't be + // traversed anymore. + return 0; + } + + return self->pimpl->tp_traverse(visit, arg); +} + +static int +green_is_gc(BorrowedGreenlet self) +{ + int result = 0; + /* Main greenlet can be garbage collected since it can only + become unreachable if the underlying thread exited. + Active greenlets --- including those that are suspended --- + cannot be garbage collected, however. + */ + if (self->main() || !self->active()) { + result = 1; + } + // The main greenlet pointer will eventually go away after the thread dies. + if (self->was_running_in_dead_thread()) { + // Our thread is dead! We can never run again. Might as well + // GC us. Note that if a tuple containing only us and other + // immutable objects had been scanned before this, when we + // would have returned 0, the tuple will take itself out of GC + // tracking and never be investigated again. So that could + // result in both us and the tuple leaking due to an + // unreachable/uncollectible reference. The same goes for + // dictionaries. + // + // It's not a great idea to be changing our GC state on the + // fly. + result = 1; + } + return result; +} + + +static int +green_clear(PyGreenlet* self) +{ + /* Greenlet is only cleared if it is about to be collected. + Since active greenlets are not garbage collectable, we can + be sure that, even if they are deallocated during clear, + nothing they reference is in unreachable or finalizers, + so even if it switches we are relatively safe. */ + // XXX: Are we responsible for clearing weakrefs here? + Py_CLEAR(self->dict); + return self->pimpl->tp_clear(); +} + +/** + * Returns 0 on failure (the object was resurrected) or 1 on success. + **/ +static int +_green_dealloc_kill_started_non_main_greenlet(BorrowedGreenlet self) +{ + /* Hacks hacks hacks copied from instance_dealloc() */ + /* Temporarily resurrect the greenlet. */ + assert(self.REFCNT() == 0); + Py_SET_REFCNT(self.borrow(), 1); + /* Save the current exception, if any. */ + PyErrPieces saved_err; + try { + // BY THE TIME WE GET HERE, the state may actually be going + // away + // if we're shutting down the interpreter and freeing thread + // entries, + // this could result in freeing greenlets that were leaked. So + // we can't try to read the state. + self->deallocing_greenlet_in_thread( + self->thread_state() + ? static_cast(GET_THREAD_STATE()) + : nullptr); + } + catch (const PyErrOccurred&) { + PyErr_WriteUnraisable(self.borrow_o()); + /* XXX what else should we do? */ + } + /* Check for no resurrection must be done while we keep + * our internal reference, otherwise PyFile_WriteObject + * causes recursion if using Py_INCREF/Py_DECREF + */ + if (self.REFCNT() == 1 && self->active()) { + /* Not resurrected, but still not dead! + XXX what else should we do? we complain. */ + PyObject* f = PySys_GetObject("stderr"); + Py_INCREF(self.borrow_o()); /* leak! */ + if (f != NULL) { + PyFile_WriteString("GreenletExit did not kill ", f); + PyFile_WriteObject(self.borrow_o(), f, 0); + PyFile_WriteString("\n", f); + } + } + /* Restore the saved exception. */ + saved_err.PyErrRestore(); + /* Undo the temporary resurrection; can't use DECREF here, + * it would cause a recursive call. + */ + assert(self.REFCNT() > 0); + + Py_ssize_t refcnt = self.REFCNT() - 1; + Py_SET_REFCNT(self.borrow_o(), refcnt); + if (refcnt != 0) { + /* Resurrected! */ + _Py_NewReference(self.borrow_o()); + Py_SET_REFCNT(self.borrow_o(), refcnt); + /* Better to use tp_finalizer slot (PEP 442) + * and call ``PyObject_CallFinalizerFromDealloc``, + * but that's only supported in Python 3.4+; see + * Modules/_io/iobase.c for an example. + * + * The following approach is copied from iobase.c in CPython 2.7. + * (along with much of this function in general). Here's their + * comment: + * + * When called from a heap type's dealloc, the type will be + * decref'ed on return (see e.g. subtype_dealloc in typeobject.c). */ + if (PyType_HasFeature(self.TYPE(), Py_TPFLAGS_HEAPTYPE)) { + Py_INCREF(self.TYPE()); + } + + PyObject_GC_Track((PyObject*)self); + + _Py_DEC_REFTOTAL; +#ifdef COUNT_ALLOCS + --Py_TYPE(self)->tp_frees; + --Py_TYPE(self)->tp_allocs; +#endif /* COUNT_ALLOCS */ + return 0; + } + return 1; +} + + +static void +green_dealloc(PyGreenlet* self) +{ + PyObject_GC_UnTrack(self); + BorrowedGreenlet me(self); + if (me->active() + && me->started() + && !me->main()) { + if (!_green_dealloc_kill_started_non_main_greenlet(me)) { + return; + } + } + + if (self->weakreflist != NULL) { + PyObject_ClearWeakRefs((PyObject*)self); + } + Py_CLEAR(self->dict); + + if (self->pimpl) { + // In case deleting this, which frees some memory, + // somehow winds up calling back into us. That's usually a + //bug in our code. + Greenlet* p = self->pimpl; + self->pimpl = nullptr; + delete p; + } + // and finally we're done. self is now invalid. + Py_TYPE(self)->tp_free((PyObject*)self); +} + + + +static OwnedObject +throw_greenlet(BorrowedGreenlet self, PyErrPieces& err_pieces) +{ + PyObject* result = nullptr; + err_pieces.PyErrRestore(); + assert(PyErr_Occurred()); + if (self->started() && !self->active()) { + /* dead greenlet: turn GreenletExit into a regular return */ + result = g_handle_exit(OwnedObject()).relinquish_ownership(); + } + self->args() <<= result; + + return single_result(self->g_switch()); +} + + + +PyDoc_STRVAR( + green_switch_doc, + "switch(*args, **kwargs)\n" + "\n" + "Switch execution to this greenlet.\n" + "\n" + "If this greenlet has never been run, then this greenlet\n" + "will be switched to using the body of ``self.run(*args, **kwargs)``.\n" + "\n" + "If the greenlet is active (has been run, but was switch()'ed\n" + "out before leaving its run function), then this greenlet will\n" + "be resumed and the return value to its switch call will be\n" + "None if no arguments are given, the given argument if one\n" + "argument is given, or the args tuple and keyword args dict if\n" + "multiple arguments are given.\n" + "\n" + "If the greenlet is dead, or is the current greenlet then this\n" + "function will simply return the arguments using the same rules as\n" + "above.\n"); + +static PyObject* +green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs) +{ + using greenlet::SwitchingArgs; + SwitchingArgs switch_args(OwnedObject::owning(args), OwnedObject::owning(kwargs)); + self->pimpl->may_switch_away(); + self->pimpl->args() <<= switch_args; + + // If we're switching out of a greenlet, and that switch is the + // last thing the greenlet does, the greenlet ought to be able to + // go ahead and die at that point. Currently, someone else must + // manually switch back to the greenlet so that we "fall off the + // end" and can perform cleanup. You'd think we'd be able to + // figure out that this is happening using the frame's ``f_lasti`` + // member, which is supposed to be an index into + // ``frame->f_code->co_code``, the bytecode string. However, in + // recent interpreters, ``f_lasti`` tends not to be updated thanks + // to things like the PREDICT() macros in ceval.c. So it doesn't + // really work to do that in many cases. For example, the Python + // code: + // def run(): + // greenlet.getcurrent().parent.switch() + // produces bytecode of len 16, with the actual call to switch() + // being at index 10 (in Python 3.10). However, the reported + // ``f_lasti`` we actually see is...5! (Which happens to be the + // second byte of the CALL_METHOD op for ``getcurrent()``). + + try { + //OwnedObject result = single_result(self->pimpl->g_switch()); + OwnedObject result(single_result(self->pimpl->g_switch())); +#ifndef NDEBUG + // Note that the current greenlet isn't necessarily self. If self + // finished, we went to one of its parents. + assert(!self->pimpl->args()); + + const BorrowedGreenlet& current = GET_THREAD_STATE().state().borrow_current(); + // It's possible it's never been switched to. + assert(!current->args()); +#endif + PyObject* p = result.relinquish_ownership(); + + if (!p && !PyErr_Occurred()) { + // This shouldn't be happening anymore, so the asserts + // are there for debug builds. Non-debug builds + // crash "gracefully" in this case, although there is an + // argument to be made for killing the process in all + // cases --- for this to be the case, our switches + // probably nested in an incorrect way, so the state is + // suspicious. Nothing should be corrupt though, just + // confused at the Python level. Letting this propagate is + // probably good enough. + assert(p || PyErr_Occurred()); + throw PyErrOccurred( + mod_globs->PyExc_GreenletError, + "Greenlet.switch() returned NULL without an exception set." + ); + } + return p; + } + catch(const PyErrOccurred&) { + return nullptr; + } +} + +PyDoc_STRVAR( + green_throw_doc, + "Switches execution to this greenlet, but immediately raises the\n" + "given exception in this greenlet. If no argument is provided, the " + "exception\n" + "defaults to `greenlet.GreenletExit`. The normal exception\n" + "propagation rules apply, as described for `switch`. Note that calling " + "this\n" + "method is almost equivalent to the following::\n" + "\n" + " def raiser():\n" + " raise typ, val, tb\n" + " g_raiser = greenlet(raiser, parent=g)\n" + " g_raiser.switch()\n" + "\n" + "except that this trick does not work for the\n" + "`greenlet.GreenletExit` exception, which would not propagate\n" + "from ``g_raiser`` to ``g``.\n"); + +static PyObject* +green_throw(PyGreenlet* self, PyObject* args) +{ + PyArgParseParam typ(mod_globs->PyExc_GreenletExit); + PyArgParseParam val; + PyArgParseParam tb; + + if (!PyArg_ParseTuple(args, "|OOO:throw", &typ, &val, &tb)) { + return nullptr; + } + + assert(typ.borrow() || val.borrow()); + + self->pimpl->may_switch_away(); + try { + // Both normalizing the error and the actual throw_greenlet + // could throw PyErrOccurred. + PyErrPieces err_pieces(typ.borrow(), val.borrow(), tb.borrow()); + + return throw_greenlet(self, err_pieces).relinquish_ownership(); + } + catch (const PyErrOccurred&) { + return nullptr; + } +} + +static int +green_bool(PyGreenlet* self) +{ + return self->pimpl->active(); +} + +/** + * CAUTION: Allocates memory, may run GC and arbitrary Python code. + */ +static PyObject* +green_getdict(PyGreenlet* self, void* UNUSED(context)) +{ + if (self->dict == NULL) { + self->dict = PyDict_New(); + if (self->dict == NULL) { + return NULL; + } + } + Py_INCREF(self->dict); + return self->dict; +} + +static int +green_setdict(PyGreenlet* self, PyObject* val, void* UNUSED(context)) +{ + PyObject* tmp; + + if (val == NULL) { + PyErr_SetString(PyExc_TypeError, "__dict__ may not be deleted"); + return -1; + } + if (!PyDict_Check(val)) { + PyErr_SetString(PyExc_TypeError, "__dict__ must be a dictionary"); + return -1; + } + tmp = self->dict; + Py_INCREF(val); + self->dict = val; + Py_XDECREF(tmp); + return 0; +} + +static bool +_green_not_dead(BorrowedGreenlet self) +{ + // XXX: Where else should we do this? + // Probably on entry to most Python-facing functions? + if (self->was_running_in_dead_thread()) { + self->deactivate_and_free(); + return false; + } + return self->active() || !self->started(); +} + + +static PyObject* +green_getdead(BorrowedGreenlet self, void* UNUSED(context)) +{ + if (_green_not_dead(self)) { + Py_RETURN_FALSE; + } + else { + Py_RETURN_TRUE; + } +} + +static PyObject* +green_get_stack_saved(PyGreenlet* self, void* UNUSED(context)) +{ + return PyLong_FromSsize_t(self->pimpl->stack_saved()); +} + + +static PyObject* +green_getrun(BorrowedGreenlet self, void* UNUSED(context)) +{ + try { + OwnedObject result(self->run()); + return result.relinquish_ownership(); + } + catch(const PyErrOccurred&) { + return nullptr; + } +} + + + + + +static int +green_setrun(BorrowedGreenlet self, BorrowedObject nrun, void* UNUSED(context)) +{ + try { + self->run(nrun); + return 0; + } + catch(const PyErrOccurred&) { + return -1; + } +} + +static PyObject* +green_getparent(BorrowedGreenlet self, void* UNUSED(context)) +{ + return self->parent().acquire_or_None(); +} + + + +static int +green_setparent(BorrowedGreenlet self, BorrowedObject nparent, void* UNUSED(context)) +{ + try { + self->parent(nparent); + } + catch(const PyErrOccurred&) { + return -1; + } + return 0; +} + + +static PyObject* +green_getcontext(const PyGreenlet* self, void* UNUSED(context)) +{ + const Greenlet *const g = self->pimpl; + try { + OwnedObject result(g->context()); + return result.relinquish_ownership(); + } + catch(const PyErrOccurred&) { + return nullptr; + } +} + +static int +green_setcontext(BorrowedGreenlet self, PyObject* nctx, void* UNUSED(context)) +{ + try { + self->context(nctx); + return 0; + } + catch(const PyErrOccurred&) { + return -1; + } +} + + +static PyObject* +green_getframe(BorrowedGreenlet self, void* UNUSED(context)) +{ + const PythonState::OwnedFrame& top_frame = self->top_frame(); + return top_frame.acquire_or_None(); +} + + +static PyObject* +green_getstate(PyGreenlet* self) +{ + PyErr_Format(PyExc_TypeError, + "cannot serialize '%s' object", + Py_TYPE(self)->tp_name); + return nullptr; +} + +static PyObject* +green_repr(BorrowedGreenlet self) +{ + /* + Return a string like + + + The handling of greenlets across threads is not super good. + We mostly use the internal definitions of these terms, but they + generally should make sense to users as well. + */ + PyObject* result; + int never_started = !self->started() && !self->active(); + + const char* const tp_name = Py_TYPE(self)->tp_name; + + if (_green_not_dead(self)) { + /* XXX: The otid= is almost useless because you can't correlate it to + any thread identifier exposed to Python. We could use + PyThreadState_GET()->thread_id, but we'd need to save that in the + greenlet, or save the whole PyThreadState object itself. + + As it stands, its only useful for identifying greenlets from the same thread. + */ + const char* state_in_thread; + if (self->was_running_in_dead_thread()) { + // The thread it was running in is dead! + // This can happen, especially at interpreter shut down. + // It complicates debugging output because it may be + // impossible to access the current thread state at that + // time. Thus, don't access the current thread state. + state_in_thread = " (thread exited)"; + } + else { + state_in_thread = GET_THREAD_STATE().state().is_current(self) + ? " current" + : (self->started() ? " suspended" : ""); + } + result = PyUnicode_FromFormat( + "<%s object at %p (otid=%p)%s%s%s%s>", + tp_name, + self.borrow_o(), + self->thread_state(), + state_in_thread, + self->active() ? " active" : "", + never_started ? " pending" : " started", + self->main() ? " main" : "" + ); + } + else { + result = PyUnicode_FromFormat( + "<%s object at %p (otid=%p) %sdead>", + tp_name, + self.borrow_o(), + self->thread_state(), + self->was_running_in_dead_thread() + ? "(thread exited) " + : "" + ); + } + + return result; +} + +/***************************************************************************** + * C interface + * + * These are exported using the CObject API + */ +extern "C" { +static PyGreenlet* +PyGreenlet_GetCurrent(void) +{ + return GET_THREAD_STATE().state().get_current().relinquish_ownership(); +} + +static int +PyGreenlet_SetParent(PyGreenlet* g, PyGreenlet* nparent) +{ + return green_setparent((PyGreenlet*)g, (PyObject*)nparent, NULL); +} + +static PyGreenlet* +PyGreenlet_New(PyObject* run, PyGreenlet* parent) +{ + using greenlet::refs::NewDictReference; + // In the past, we didn't use green_new and green_init, but that + // was a maintenance issue because we duplicated code. This way is + // much safer, but slightly slower. If that's a problem, we could + // refactor green_init to separate argument parsing from initialization. + OwnedGreenlet g = OwnedGreenlet::consuming(green_new(&PyGreenlet_Type, nullptr, nullptr)); + if (!g) { + return NULL; + } + + try { + NewDictReference kwargs; + if (run) { + kwargs.SetItem(mod_globs->str_run, run); + } + if (parent) { + kwargs.SetItem("parent", (PyObject*)parent); + } + + Require(green_init(g, mod_globs->empty_tuple, kwargs)); + } + catch (const PyErrOccurred&) { + return nullptr; + } + + return g.relinquish_ownership(); +} + +static PyObject* +PyGreenlet_Switch(PyGreenlet* self, PyObject* args, PyObject* kwargs) +{ + if (!PyGreenlet_Check(self)) { + PyErr_BadArgument(); + return NULL; + } + + if (args == NULL) { + args = mod_globs->empty_tuple; + } + + if (kwargs == NULL || !PyDict_Check(kwargs)) { + kwargs = NULL; + } + + return green_switch(self, args, kwargs); +} + +static PyObject* +PyGreenlet_Throw(PyGreenlet* self, PyObject* typ, PyObject* val, PyObject* tb) +{ + if (!PyGreenlet_Check(self)) { + PyErr_BadArgument(); + return nullptr; + } + try { + PyErrPieces err_pieces(typ, val, tb); + return throw_greenlet(self, err_pieces).relinquish_ownership(); + } + catch (const PyErrOccurred&) { + return nullptr; + } +} + +static int +Extern_PyGreenlet_MAIN(PyGreenlet* self) +{ + if (!PyGreenlet_Check(self)) { + PyErr_BadArgument(); + return -1; + } + return self->pimpl->main(); +} + +static int +Extern_PyGreenlet_ACTIVE(PyGreenlet* self) +{ + if (!PyGreenlet_Check(self)) { + PyErr_BadArgument(); + return -1; + } + return self->pimpl->active(); +} + +static int +Extern_PyGreenlet_STARTED(PyGreenlet* self) +{ + if (!PyGreenlet_Check(self)) { + PyErr_BadArgument(); + return -1; + } + return self->pimpl->started(); +} + +static PyGreenlet* +Extern_PyGreenlet_GET_PARENT(PyGreenlet* self) +{ + if (!PyGreenlet_Check(self)) { + PyErr_BadArgument(); + return NULL; + } + // This can return NULL even if there is no exception + return self->pimpl->parent().acquire(); +} +} // extern C. + +/** End C API ****************************************************************/ + +static PyMethodDef green_methods[] = { + {"switch", + reinterpret_cast(green_switch), + METH_VARARGS | METH_KEYWORDS, + green_switch_doc}, + {"throw", (PyCFunction)green_throw, METH_VARARGS, green_throw_doc}, + {"__getstate__", (PyCFunction)green_getstate, METH_NOARGS, NULL}, + {NULL, NULL} /* sentinel */ +}; + +static PyGetSetDef green_getsets[] = { + /* name, getter, setter, doc, context pointer */ + {"__dict__", (getter)green_getdict, (setter)green_setdict, /*XXX*/ NULL}, + {"run", (getter)green_getrun, (setter)green_setrun, /*XXX*/ NULL}, + {"parent", (getter)green_getparent, (setter)green_setparent, /*XXX*/ NULL}, + {"gr_frame", (getter)green_getframe, NULL, /*XXX*/ NULL}, + {"gr_context", + (getter)green_getcontext, + (setter)green_setcontext, + /*XXX*/ NULL}, + {"dead", (getter)green_getdead, NULL, /*XXX*/ NULL}, + {"_stack_saved", (getter)green_get_stack_saved, NULL, /*XXX*/ NULL}, + {NULL} +}; + +static PyMemberDef green_members[] = { + {NULL} +}; + +static PyNumberMethods green_as_number = { + NULL, /* nb_add */ + NULL, /* nb_subtract */ + NULL, /* nb_multiply */ + NULL, /* nb_remainder */ + NULL, /* nb_divmod */ + NULL, /* nb_power */ + NULL, /* nb_negative */ + NULL, /* nb_positive */ + NULL, /* nb_absolute */ + (inquiry)green_bool, /* nb_bool */ +}; + + +PyTypeObject PyGreenlet_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "greenlet.greenlet", /* tp_name */ + sizeof(PyGreenlet), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)green_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)green_repr, /* tp_repr */ + &green_as_number, /* tp_as _number*/ + 0, /* tp_as _sequence*/ + 0, /* tp_as _mapping*/ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer*/ + G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "greenlet(run=None, parent=None) -> greenlet\n\n" + "Creates a new greenlet object (without running it).\n\n" + " - *run* -- The callable to invoke.\n" + " - *parent* -- The parent greenlet. The default is the current " + "greenlet.", /* tp_doc */ + (traverseproc)green_traverse, /* tp_traverse */ + (inquiry)green_clear, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(PyGreenlet, weakreflist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + green_methods, /* tp_methods */ + green_members, /* tp_members */ + green_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof(PyGreenlet, dict), /* tp_dictoffset */ + (initproc)green_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + (newfunc)green_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ + (inquiry)green_is_gc, /* tp_is_gc */ +}; + + + +static PyObject* +green_unswitchable_getforce(PyGreenlet* self, void* UNUSED(context)) +{ + BrokenGreenlet* broken = dynamic_cast(self->pimpl); + return PyBool_FromLong(broken->_force_switch_error); +} + +static int +green_unswitchable_setforce(PyGreenlet* self, BorrowedObject nforce, void* UNUSED(context)) +{ + if (!nforce) { + PyErr_SetString( + PyExc_AttributeError, + "Cannot delete force_switch_error" + ); + return -1; + } + BrokenGreenlet* broken = dynamic_cast(self->pimpl); + int is_true = PyObject_IsTrue(nforce); + if (is_true == -1) { + return -1; + } + broken->_force_switch_error = is_true; + return 0; +} + +static PyObject* +green_unswitchable_getforceslp(PyGreenlet* self, void* UNUSED(context)) +{ + BrokenGreenlet* broken = dynamic_cast(self->pimpl); + return PyBool_FromLong(broken->_force_slp_switch_error); +} + +static int +green_unswitchable_setforceslp(PyGreenlet* self, BorrowedObject nforce, void* UNUSED(context)) +{ + if (!nforce) { + PyErr_SetString( + PyExc_AttributeError, + "Cannot delete force_slp_switch_error" + ); + return -1; + } + BrokenGreenlet* broken = dynamic_cast(self->pimpl); + int is_true = PyObject_IsTrue(nforce); + if (is_true == -1) { + return -1; + } + broken->_force_slp_switch_error = is_true; + return 0; +} + +static PyGetSetDef green_unswitchable_getsets[] = { + /* name, getter, setter, doc, context pointer */ + {"force_switch_error", + (getter)green_unswitchable_getforce, + (setter)green_unswitchable_setforce, + /*XXX*/ NULL}, + {"force_slp_switch_error", + (getter)green_unswitchable_getforceslp, + (setter)green_unswitchable_setforceslp, + /*XXX*/ NULL}, + + {NULL} +}; + +PyTypeObject PyGreenletUnswitchable_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "greenlet._greenlet.UnswitchableGreenlet", + 0, /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)green_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as _number*/ + 0, /* tp_as _sequence*/ + 0, /* tp_as _mapping*/ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer*/ + G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Undocumented internal class", /* tp_doc */ + (traverseproc)green_traverse, /* tp_traverse */ + (inquiry)green_clear, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + green_unswitchable_getsets, /* tp_getset */ + &PyGreenlet_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)green_init, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + (newfunc)green_unswitchable_new, /* tp_new */ + PyObject_GC_Del, /* tp_free */ + (inquiry)green_is_gc, /* tp_is_gc */ +}; + + +PyDoc_STRVAR(mod_getcurrent_doc, + "getcurrent() -> greenlet\n" + "\n" + "Returns the current greenlet (i.e. the one which called this " + "function).\n"); + +static PyObject* +mod_getcurrent(PyObject* UNUSED(module)) +{ + return GET_THREAD_STATE().state().get_current().relinquish_ownership_o(); +} + +PyDoc_STRVAR(mod_settrace_doc, + "settrace(callback) -> object\n" + "\n" + "Sets a new tracing function and returns the previous one.\n"); +static PyObject* +mod_settrace(PyObject* UNUSED(module), PyObject* args) +{ + PyArgParseParam tracefunc; + if (!PyArg_ParseTuple(args, "O", &tracefunc)) { + return NULL; + } + ThreadState& state = GET_THREAD_STATE(); + OwnedObject previous = state.get_tracefunc(); + if (!previous) { + previous = Py_None; + } + + state.set_tracefunc(tracefunc); + + return previous.relinquish_ownership(); +} + +PyDoc_STRVAR(mod_gettrace_doc, + "gettrace() -> object\n" + "\n" + "Returns the currently set tracing function, or None.\n"); + +static PyObject* +mod_gettrace(PyObject* UNUSED(module)) +{ + OwnedObject tracefunc = GET_THREAD_STATE().state().get_tracefunc(); + if (!tracefunc) { + tracefunc = Py_None; + } + return tracefunc.relinquish_ownership(); +} + +PyDoc_STRVAR(mod_set_thread_local_doc, + "set_thread_local(key, value) -> None\n" + "\n" + "Set a value in the current thread-local dictionary. Debbuging only.\n"); + +static PyObject* +mod_set_thread_local(PyObject* UNUSED(module), PyObject* args) +{ + PyArgParseParam key; + PyArgParseParam value; + PyObject* result = NULL; + + if (PyArg_UnpackTuple(args, "set_thread_local", 2, 2, &key, &value)) { + if(PyDict_SetItem( + PyThreadState_GetDict(), // borrow + key, + value) == 0 ) { + // success + Py_INCREF(Py_None); + result = Py_None; + } + } + return result; +} + +PyDoc_STRVAR(mod_get_pending_cleanup_count_doc, + "get_pending_cleanup_count() -> Integer\n" + "\n" + "Get the number of greenlet cleanup operations pending. Testing only.\n"); + + +static PyObject* +mod_get_pending_cleanup_count(PyObject* UNUSED(module)) +{ + LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock); + return PyLong_FromSize_t(mod_globs->thread_states_to_destroy.size()); +} + +PyDoc_STRVAR(mod_get_total_main_greenlets_doc, + "get_total_main_greenlets() -> Integer\n" + "\n" + "Quickly return the number of main greenlets that exist. Testing only.\n"); + +static PyObject* +mod_get_total_main_greenlets(PyObject* UNUSED(module)) +{ + return PyLong_FromSize_t(G_TOTAL_MAIN_GREENLETS); +} + +PyDoc_STRVAR(mod_get_clocks_used_doing_optional_cleanup_doc, + "get_clocks_used_doing_optional_cleanup() -> Integer\n" + "\n" + "Get the number of clock ticks the program has used doing optional " + "greenlet cleanup.\n" + "Beginning in greenlet 2.0, greenlet tries to find and dispose of greenlets\n" + "that leaked after a thread exited. This requires invoking Python's garbage collector,\n" + "which may have a performance cost proportional to the number of live objects.\n" + "This function returns the amount of processor time\n" + "greenlet has used to do this. In programs that run with very large amounts of live\n" + "objects, this metric can be used to decide whether the cost of doing this cleanup\n" + "is worth the memory leak being corrected. If not, you can disable the cleanup\n" + "using ``enable_optional_cleanup(False)``.\n" + "The units are arbitrary and can only be compared to themselves (similarly to ``time.clock()``);\n" + "for example, to see how it scales with your heap. You can attempt to convert them into seconds\n" + "by dividing by the value of CLOCKS_PER_SEC." + "If cleanup has been disabled, returns None." + "\n" + "This is an implementation specific, provisional API. It may be changed or removed\n" + "in the future.\n" + ".. versionadded:: 2.0" + ); +static PyObject* +mod_get_clocks_used_doing_optional_cleanup(PyObject* UNUSED(module)) +{ + std::clock_t& clocks = ThreadState::clocks_used_doing_gc(); + + if (clocks == std::clock_t(-1)) { + Py_RETURN_NONE; + } + // This might not actually work on some implementations; clock_t + // is an opaque type. + return PyLong_FromSsize_t(clocks); +} + +PyDoc_STRVAR(mod_enable_optional_cleanup_doc, + "mod_enable_optional_cleanup(bool) -> None\n" + "\n" + "Enable or disable optional cleanup operations.\n" + "See ``get_clocks_used_doing_optional_cleanup()`` for details.\n" + ); +static PyObject* +mod_enable_optional_cleanup(PyObject* UNUSED(module), PyObject* flag) +{ + int is_true = PyObject_IsTrue(flag); + if (is_true == -1) { + return nullptr; + } + + std::clock_t& clocks = ThreadState::clocks_used_doing_gc(); + if (is_true) { + // If we already have a value, we don't want to lose it. + if (clocks == std::clock_t(-1)) { + clocks = 0; + } + } + else { + clocks = std::clock_t(-1); + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(mod_get_tstate_trash_delete_nesting_doc, + "get_tstate_trash_delete_nesting() -> Integer\n" + "\n" + "Return the 'trash can' nesting level. Testing only.\n"); +static PyObject* +mod_get_tstate_trash_delete_nesting(PyObject* UNUSED(module)) +{ + PyThreadState* tstate = PyThreadState_GET(); + +#if GREENLET_PY312 + return PyLong_FromLong(tstate->trash.delete_nesting); +#else + return PyLong_FromLong(tstate->trash_delete_nesting); +#endif +} + +static PyMethodDef GreenMethods[] = { + {"getcurrent", + (PyCFunction)mod_getcurrent, + METH_NOARGS, + mod_getcurrent_doc}, + {"settrace", (PyCFunction)mod_settrace, METH_VARARGS, mod_settrace_doc}, + {"gettrace", (PyCFunction)mod_gettrace, METH_NOARGS, mod_gettrace_doc}, + {"set_thread_local", (PyCFunction)mod_set_thread_local, METH_VARARGS, mod_set_thread_local_doc}, + {"get_pending_cleanup_count", (PyCFunction)mod_get_pending_cleanup_count, METH_NOARGS, mod_get_pending_cleanup_count_doc}, + {"get_total_main_greenlets", (PyCFunction)mod_get_total_main_greenlets, METH_NOARGS, mod_get_total_main_greenlets_doc}, + {"get_clocks_used_doing_optional_cleanup", (PyCFunction)mod_get_clocks_used_doing_optional_cleanup, METH_NOARGS, mod_get_clocks_used_doing_optional_cleanup_doc}, + {"enable_optional_cleanup", (PyCFunction)mod_enable_optional_cleanup, METH_O, mod_enable_optional_cleanup_doc}, + {"get_tstate_trash_delete_nesting", (PyCFunction)mod_get_tstate_trash_delete_nesting, METH_NOARGS, mod_get_tstate_trash_delete_nesting_doc}, + {NULL, NULL} /* Sentinel */ +}; + +static const char* const copy_on_greentype[] = { + "getcurrent", + "error", + "GreenletExit", + "settrace", + "gettrace", + NULL +}; + +static struct PyModuleDef greenlet_module_def = { + PyModuleDef_HEAD_INIT, + "greenlet._greenlet", + NULL, + -1, + GreenMethods, +}; + + + +static PyObject* +greenlet_internal_mod_init() noexcept +{ + static void* _PyGreenlet_API[PyGreenlet_API_pointers]; + + try { + CreatedModule m(greenlet_module_def); + + Require(PyType_Ready(&PyGreenlet_Type)); + Require(PyType_Ready(&PyGreenletUnswitchable_Type)); + + mod_globs = new greenlet::GreenletGlobals; + ThreadState::init(); + + m.PyAddObject("greenlet", PyGreenlet_Type); + m.PyAddObject("UnswitchableGreenlet", PyGreenletUnswitchable_Type); + m.PyAddObject("error", mod_globs->PyExc_GreenletError); + m.PyAddObject("GreenletExit", mod_globs->PyExc_GreenletExit); + + m.PyAddObject("GREENLET_USE_GC", 1); + m.PyAddObject("GREENLET_USE_TRACING", 1); + m.PyAddObject("GREENLET_USE_CONTEXT_VARS", 1L); + m.PyAddObject("GREENLET_USE_STANDARD_THREADING", 1L); + + OwnedObject clocks_per_sec = OwnedObject::consuming(PyLong_FromSsize_t(CLOCKS_PER_SEC)); + m.PyAddObject("CLOCKS_PER_SEC", clocks_per_sec); + + /* also publish module-level data as attributes of the greentype. */ + // XXX: This is weird, and enables a strange pattern of + // confusing the class greenlet with the module greenlet; with + // the exception of (possibly) ``getcurrent()``, this + // shouldn't be encouraged so don't add new items here. + for (const char* const* p = copy_on_greentype; *p; p++) { + OwnedObject o = m.PyRequireAttr(*p); + PyDict_SetItemString(PyGreenlet_Type.tp_dict, *p, o.borrow()); + } + + /* + * Expose C API + */ + + /* types */ + _PyGreenlet_API[PyGreenlet_Type_NUM] = (void*)&PyGreenlet_Type; + + /* exceptions */ + _PyGreenlet_API[PyExc_GreenletError_NUM] = (void*)mod_globs->PyExc_GreenletError; + _PyGreenlet_API[PyExc_GreenletExit_NUM] = (void*)mod_globs->PyExc_GreenletExit; + + /* methods */ + _PyGreenlet_API[PyGreenlet_New_NUM] = (void*)PyGreenlet_New; + _PyGreenlet_API[PyGreenlet_GetCurrent_NUM] = (void*)PyGreenlet_GetCurrent; + _PyGreenlet_API[PyGreenlet_Throw_NUM] = (void*)PyGreenlet_Throw; + _PyGreenlet_API[PyGreenlet_Switch_NUM] = (void*)PyGreenlet_Switch; + _PyGreenlet_API[PyGreenlet_SetParent_NUM] = (void*)PyGreenlet_SetParent; + + /* Previously macros, but now need to be functions externally. */ + _PyGreenlet_API[PyGreenlet_MAIN_NUM] = (void*)Extern_PyGreenlet_MAIN; + _PyGreenlet_API[PyGreenlet_STARTED_NUM] = (void*)Extern_PyGreenlet_STARTED; + _PyGreenlet_API[PyGreenlet_ACTIVE_NUM] = (void*)Extern_PyGreenlet_ACTIVE; + _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM] = (void*)Extern_PyGreenlet_GET_PARENT; + + /* XXX: Note that our module name is ``greenlet._greenlet``, but for + backwards compatibility with existing C code, we need the _C_API to + be directly in greenlet. + */ + const NewReference c_api_object(Require( + PyCapsule_New( + (void*)_PyGreenlet_API, + "greenlet._C_API", + NULL))); + m.PyAddObject("_C_API", c_api_object); + assert(c_api_object.REFCNT() == 2); + + // cerr << "Sizes:" + // << "\n\tGreenlet : " << sizeof(Greenlet) + // << "\n\tUserGreenlet : " << sizeof(UserGreenlet) + // << "\n\tMainGreenlet : " << sizeof(MainGreenlet) + // << "\n\tExceptionState : " << sizeof(greenlet::ExceptionState) + // << "\n\tPythonState : " << sizeof(greenlet::PythonState) + // << "\n\tStackState : " << sizeof(greenlet::StackState) + // << "\n\tSwitchingArgs : " << sizeof(greenlet::SwitchingArgs) + // << "\n\tOwnedObject : " << sizeof(greenlet::refs::OwnedObject) + // << "\n\tBorrowedObject : " << sizeof(greenlet::refs::BorrowedObject) + // << "\n\tPyGreenlet : " << sizeof(PyGreenlet) + // << endl; + + return m.borrow(); // But really it's the main reference. + } + catch (const LockInitError& e) { + PyErr_SetString(PyExc_MemoryError, e.what()); + return NULL; + } + catch (const PyErrOccurred&) { + return NULL; + } + +} + +extern "C" { + +PyMODINIT_FUNC +PyInit__greenlet(void) +{ + return greenlet_internal_mod_init(); +} + +}; // extern C + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet.h new file mode 100644 index 0000000..d02a16e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet.h @@ -0,0 +1,164 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ + +/* Greenlet object interface */ + +#ifndef Py_GREENLETOBJECT_H +#define Py_GREENLETOBJECT_H + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is deprecated and undocumented. It does not change. */ +#define GREENLET_VERSION "1.0.0" + +#ifndef GREENLET_MODULE +#define implementation_ptr_t void* +#endif + +typedef struct _greenlet { + PyObject_HEAD + PyObject* weakreflist; + PyObject* dict; + implementation_ptr_t pimpl; +} PyGreenlet; + +#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type)) + + +/* C API functions */ + +/* Total number of symbols that are exported */ +#define PyGreenlet_API_pointers 12 + +#define PyGreenlet_Type_NUM 0 +#define PyExc_GreenletError_NUM 1 +#define PyExc_GreenletExit_NUM 2 + +#define PyGreenlet_New_NUM 3 +#define PyGreenlet_GetCurrent_NUM 4 +#define PyGreenlet_Throw_NUM 5 +#define PyGreenlet_Switch_NUM 6 +#define PyGreenlet_SetParent_NUM 7 + +#define PyGreenlet_MAIN_NUM 8 +#define PyGreenlet_STARTED_NUM 9 +#define PyGreenlet_ACTIVE_NUM 10 +#define PyGreenlet_GET_PARENT_NUM 11 + +#ifndef GREENLET_MODULE +/* This section is used by modules that uses the greenlet C API */ +static void** _PyGreenlet_API = NULL; + +# define PyGreenlet_Type \ + (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM]) + +# define PyExc_GreenletError \ + ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM]) + +# define PyExc_GreenletExit \ + ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM]) + +/* + * PyGreenlet_New(PyObject *args) + * + * greenlet.greenlet(run, parent=None) + */ +# define PyGreenlet_New \ + (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \ + _PyGreenlet_API[PyGreenlet_New_NUM]) + +/* + * PyGreenlet_GetCurrent(void) + * + * greenlet.getcurrent() + */ +# define PyGreenlet_GetCurrent \ + (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) + +/* + * PyGreenlet_Throw( + * PyGreenlet *greenlet, + * PyObject *typ, + * PyObject *val, + * PyObject *tb) + * + * g.throw(...) + */ +# define PyGreenlet_Throw \ + (*(PyObject * (*)(PyGreenlet * self, \ + PyObject * typ, \ + PyObject * val, \ + PyObject * tb)) \ + _PyGreenlet_API[PyGreenlet_Throw_NUM]) + +/* + * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) + * + * g.switch(*args, **kwargs) + */ +# define PyGreenlet_Switch \ + (*(PyObject * \ + (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \ + _PyGreenlet_API[PyGreenlet_Switch_NUM]) + +/* + * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) + * + * g.parent = new_parent + */ +# define PyGreenlet_SetParent \ + (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \ + _PyGreenlet_API[PyGreenlet_SetParent_NUM]) + +/* + * PyGreenlet_GetParent(PyObject* greenlet) + * + * return greenlet.parent; + * + * This could return NULL even if there is no exception active. + * If it does not return NULL, you are responsible for decrementing the + * reference count. + */ +# define PyGreenlet_GetParent \ + (*(PyGreenlet* (*)(PyGreenlet*)) \ + _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM]) + +/* + * deprecated, undocumented alias. + */ +# define PyGreenlet_GET_PARENT PyGreenlet_GetParent + +# define PyGreenlet_MAIN \ + (*(int (*)(PyGreenlet*)) \ + _PyGreenlet_API[PyGreenlet_MAIN_NUM]) + +# define PyGreenlet_STARTED \ + (*(int (*)(PyGreenlet*)) \ + _PyGreenlet_API[PyGreenlet_STARTED_NUM]) + +# define PyGreenlet_ACTIVE \ + (*(int (*)(PyGreenlet*)) \ + _PyGreenlet_API[PyGreenlet_ACTIVE_NUM]) + + + + +/* Macro that imports greenlet and initializes C API */ +/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we + keep the older definition to be sure older code that might have a copy of + the header still works. */ +# define PyGreenlet_Import() \ + { \ + _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ + } + +#endif /* GREENLET_MODULE */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GREENLETOBJECT_H */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_allocator.hpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_allocator.hpp new file mode 100644 index 0000000..b452f54 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_allocator.hpp @@ -0,0 +1,63 @@ +#ifndef GREENLET_ALLOCATOR_HPP +#define GREENLET_ALLOCATOR_HPP + +#define PY_SSIZE_T_CLEAN +#include +#include +#include "greenlet_compiler_compat.hpp" + + +namespace greenlet +{ + // This allocator is stateless; all instances are identical. + // It can *ONLY* be used when we're sure we're holding the GIL + // (Python's allocators require the GIL). + template + struct PythonAllocator : public std::allocator { + + PythonAllocator(const PythonAllocator& UNUSED(other)) + : std::allocator() + { + } + + PythonAllocator(const std::allocator other) + : std::allocator(other) + {} + + template + PythonAllocator(const std::allocator& other) + : std::allocator(other) + { + } + + PythonAllocator() : std::allocator() {} + + T* allocate(size_t number_objects, const void* UNUSED(hint)=0) + { + void* p; + if (number_objects == 1) + p = PyObject_Malloc(sizeof(T)); + else + p = PyMem_Malloc(sizeof(T) * number_objects); + return static_cast(p); + } + + void deallocate(T* t, size_t n) + { + void* p = t; + if (n == 1) { + PyObject_Free(p); + } + else + PyMem_Free(p); + } + // This member is deprecated in C++17 and removed in C++20 + template< class U > + struct rebind { + typedef PythonAllocator other; + }; + + }; +} + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_compiler_compat.hpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_compiler_compat.hpp new file mode 100644 index 0000000..ee5bbdd --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_compiler_compat.hpp @@ -0,0 +1,95 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ +#ifndef GREENLET_COMPILER_COMPAT_HPP +#define GREENLET_COMPILER_COMPAT_HPP + +/** + * Definitions to aid with compatibility with different compilers. + * + * .. caution:: Use extreme care with noexcept. + * Some compilers and runtimes, specifically gcc/libgcc/libstdc++ on + * Linux, implement stack unwinding by throwing an uncatchable + * exception, one that specifically does not appear to be an active + * exception to the rest of the runtime. If this happens while we're in a noexcept function, + * we have violated our dynamic exception contract, and so the runtime + * will call std::terminate(), which kills the process with the + * unhelpful message "terminate called without an active exception". + * + * This has happened in this scenario: A background thread is running + * a greenlet that has made a native call and released the GIL. + * Meanwhile, the main thread finishes and starts shutting down the + * interpreter. When the background thread is scheduled again and + * attempts to obtain the GIL, it notices that the interpreter is + * exiting and calls ``pthread_exit()``. This in turn starts to unwind + * the stack by throwing that exception. But we had the ``PyCall`` + * functions annotated as noexcept, so the runtime terminated us. + * + * #2 0x00007fab26fec2b7 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6 + * #3 0x00007fab26febb3c in __gxx_personality_v0 () from /lib/x86_64-linux-gnu/libstdc++.so.6 + * #4 0x00007fab26f34de6 in ?? () from /lib/x86_64-linux-gnu/libgcc_s.so.1 + * #6 0x00007fab276a34c6 in __GI___pthread_unwind at ./nptl/unwind.c:130 + * #7 0x00007fab2769bd3a in __do_cancel () at ../sysdeps/nptl/pthreadP.h:280 + * #8 __GI___pthread_exit (value=value@entry=0x0) at ./nptl/pthread_exit.c:36 + * #9 0x000000000052e567 in PyThread_exit_thread () at ../Python/thread_pthread.h:370 + * #10 0x00000000004d60b5 in take_gil at ../Python/ceval_gil.h:224 + * #11 0x00000000004d65f9 in PyEval_RestoreThread at ../Python/ceval.c:467 + * #12 0x000000000060cce3 in setipaddr at ../Modules/socketmodule.c:1203 + * #13 0x00000000006101cd in socket_gethostbyname + */ + +#include + +# if defined(__clang__) +# define G_FP_TMPL_STATIC static +# else +// GCC has no problem allowing static function pointers, but emits +// tons of warnings about "whose type uses the anonymous namespace [-Wsubobject-linkage]" +# define G_FP_TMPL_STATIC +# endif + +# define G_NO_COPIES_OF_CLS(Cls) private: \ + Cls(const Cls& other) = delete; \ + Cls& operator=(const Cls& other) = delete + +# define G_NO_ASSIGNMENT_OF_CLS(Cls) private: \ + Cls& operator=(const Cls& other) = delete + +# define G_NO_COPY_CONSTRUCTOR_OF_CLS(Cls) private: \ + Cls(const Cls& other) = delete; + + +// CAUTION: MSVC is stupidly picky: +// +// "The compiler ignores, without warning, any __declspec keywords +// placed after * or & and in front of the variable identifier in a +// declaration." +// (https://docs.microsoft.com/en-us/cpp/cpp/declspec?view=msvc-160) +// +// So pointer return types must be handled differently (because of the +// trailing *), or you get inscrutable compiler warnings like "error +// C2059: syntax error: ''" +// +// In C++ 11, there is a standard syntax for attributes, and +// GCC defines an attribute to use with this: [[gnu:noinline]]. +// In the future, this is expected to become standard. + +#if defined(__GNUC__) || defined(__clang__) +/* We used to check for GCC 4+ or 3.4+, but those compilers are + laughably out of date. Just assume they support it. */ +# define GREENLET_NOINLINE(name) __attribute__((noinline)) name +# define GREENLET_NOINLINE_P(rtype, name) rtype __attribute__((noinline)) name +# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) +#elif defined(_MSC_VER) +/* We used to check for && (_MSC_VER >= 1300) but that's also out of date. */ +# define GREENLET_NOINLINE(name) __declspec(noinline) name +# define GREENLET_NOINLINE_P(rtype, name) __declspec(noinline) rtype name +# define UNUSED(x) UNUSED_ ## x +#endif + +#if defined(_MSC_VER) +# define G_NOEXCEPT_WIN32 noexcept +#else +# define G_NOEXCEPT_WIN32 +#endif + + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_cpython_add_pending.hpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_cpython_add_pending.hpp new file mode 100644 index 0000000..0d28efd --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_cpython_add_pending.hpp @@ -0,0 +1,172 @@ +#ifndef GREENLET_CPYTHON_ADD_PENDING_HPP +#define GREENLET_CPYTHON_ADD_PENDING_HPP + +#if (PY_VERSION_HEX >= 0x30800A0 && PY_VERSION_HEX < 0x3090000) && !(defined(_WIN32) || defined(WIN32)) +// XXX: From Python 3.8a3 [1] up until Python 3.9a6 [2][3], +// ``Py_AddPendingCall`` would try to produce a Python exception if +// the interpreter was in the beginning of shutting down when this +// function is called. However, ``Py_AddPendingCall`` doesn't require +// the GIL, and we are absolutely not holding it when we make that +// call. That means that trying to create the Python exception is +// using the C API in an undefined state; here the C API detects this +// and aborts the process with an error ("Fatal Python error: Python +// memory allocator called without holding the GIL": Add -> +// PyErr_SetString -> PyUnicode_New -> PyObject_Malloc). This arises +// (obviously) in multi-threaded programs and happens if one thread is +// exiting and cleaning up its thread-local data while the other +// thread is trying to shut down the interpreter. A crash on shutdown +// is still a crash and could result in data loss (e.g., daemon +// threads are still running, pending signal handlers may be present, +// buffers may not be flushed, there may be __del__ that need run, +// etc), so we have to work around it. +// +// Of course, we can (and do) check for whether the interpreter is +// shutting down before calling ``Py_AddPendingCall``, but that's a +// race condition since we don't hold the GIL, and so we may not +// actually get the right answer. Plus, ``Py_FinalizeEx`` actually +// calls ``_Py_FinishPendingCalls`` (which sets the pending->finishing +// flag, which is used to gate creating the exceptioen) *before* +// publishing any other data that would let us detect the shutdown +// (such as runtime->finalizing). So that point is moot. +// +// Our solution for those versions is to inline the same code, without +// the problematic bit that sets the exception. Unfortunately, all of +// the structure definitions are private/opaque, *and* we can't +// actually count on being able to include their definitions from +// ``internal/pycore_*``, because on some platforms those header files +// are incomplete (i.e., on macOS with macports 3.8, the includes are +// fine, but on Ubuntu jammy with 3.8 from ppa:deadsnakes or GitHub +// Actions 3.8 (I think it's Ubuntu 18.04), they con't be used; at +// least, I couldn't get them to work). So we need to define the +// structures and _PyRuntime data member ourself. Yet more +// unfortunately, _PyRuntime won't link on Windows, so we can only do +// this on other platforms. +// +// [1] https://github.com/python/cpython/commit/842a2f07f2f08a935ef470bfdaeef40f87490cfc +// [2] https://github.com/python/cpython/commit/cfc3c2f8b34d3864717ab584c5b6c260014ba55a +// [3] https://github.com/python/cpython/issues/81308 +# define GREENLET_BROKEN_PY_ADD_PENDING 1 + +// When defining these structures, the important thing is to get +// binary compatibility, i.e., structure layout. For that, we only +// need to define fields up to the ones we use; after that they're +// irrelevant UNLESS the structure is included in another structure +// *before* the structure we're interested in --- in that case, it +// must be complete. Ellipsis indicate elided trailing members. +// Pointer types are changed to void* to keep from having to define +// more structures. + +// From "internal/pycore_atomic.h" + +// There are several different definitions of this, including the +// plain ``int`` version, a ``volatile int`` and an ``_Atomic int`` +// I don't think any of those change the size/layout. +typedef struct _Py_atomic_int { + volatile int _value; +} _Py_atomic_int; + +// This needs too much infrastructure, so we just do a regular store. +#define _Py_atomic_store_relaxed(ATOMIC_VAL, NEW_VAL) \ + (ATOMIC_VAL)->_value = NEW_VAL + + + +// From "internal/pycore_pymem.h" +#define NUM_GENERATIONS 3 + + +struct gc_generation { + PyGC_Head head; // We already have this defined. + int threshold; + int count; +}; +struct gc_generation_stats { + Py_ssize_t collections; + Py_ssize_t collected; + Py_ssize_t uncollectable; +}; + +struct _gc_runtime_state { + void *trash_delete_later; + int trash_delete_nesting; + int enabled; + int debug; + struct gc_generation generations[NUM_GENERATIONS]; + void *generation0; + struct gc_generation permanent_generation; + struct gc_generation_stats generation_stats[NUM_GENERATIONS]; + int collecting; + void *garbage; + void *callbacks; + Py_ssize_t long_lived_total; + Py_ssize_t long_lived_pending; +}; + +// From "internal/pycore_pystate.h" +struct _pending_calls { + int finishing; + PyThread_type_lock lock; + _Py_atomic_int calls_to_do; + int async_exc; +#define NPENDINGCALLS 32 + struct { + int (*func)(void *); + void *arg; + } calls[NPENDINGCALLS]; + int first; + int last; +}; + +struct _ceval_runtime_state { + int recursion_limit; + int tracing_possible; + _Py_atomic_int eval_breaker; + _Py_atomic_int gil_drop_request; + struct _pending_calls pending; + // ... +}; + +typedef struct pyruntimestate { + int preinitializing; + int preinitialized; + int core_initialized; + int initialized; + void *finalizing; + + struct pyinterpreters { + PyThread_type_lock mutex; + void *head; + void *main; + int64_t next_id; + } interpreters; + // XXX Remove this field once we have a tp_* slot. + struct _xidregistry { + PyThread_type_lock mutex; + void *head; + } xidregistry; + + unsigned long main_thread; + +#define NEXITFUNCS 32 + void (*exitfuncs[NEXITFUNCS])(void); + int nexitfuncs; + + struct _gc_runtime_state gc; + struct _ceval_runtime_state ceval; + // ... +} _PyRuntimeState; + +#define SIGNAL_PENDING_CALLS(ceval) \ + do { \ + _Py_atomic_store_relaxed(&(ceval)->pending.calls_to_do, 1); \ + _Py_atomic_store_relaxed(&(ceval)->eval_breaker, 1); \ + } while (0) + +extern _PyRuntimeState _PyRuntime; + +#else +# define GREENLET_BROKEN_PY_ADD_PENDING 0 +#endif + + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_cpython_compat.hpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_cpython_compat.hpp new file mode 100644 index 0000000..cdc1617 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_cpython_compat.hpp @@ -0,0 +1,127 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ +#ifndef GREENLET_CPYTHON_COMPAT_H +#define GREENLET_CPYTHON_COMPAT_H + +/** + * Helpers for compatibility with multiple versions of CPython. + */ + +#define PY_SSIZE_T_CLEAN +#include "Python.h" + + +#if PY_VERSION_HEX >= 0x30A00B1 +# define GREENLET_PY310 1 +/* +Python 3.10 beta 1 changed tstate->use_tracing to a nested cframe member. +See https://github.com/python/cpython/pull/25276 +We have to save and restore this as well. +*/ +# define GREENLET_USE_CFRAME 1 +#else +# define GREENLET_USE_CFRAME 0 +# define GREENLET_PY310 0 +#endif + + + +#if PY_VERSION_HEX >= 0x30B00A4 +/* +Greenlet won't compile on anything older than Python 3.11 alpha 4 (see +https://bugs.python.org/issue46090). Summary of breaking internal changes: +- Python 3.11 alpha 1 changed how frame objects are represented internally. + - https://github.com/python/cpython/pull/30122 +- Python 3.11 alpha 3 changed how recursion limits are stored. + - https://github.com/python/cpython/pull/29524 +- Python 3.11 alpha 4 changed how exception state is stored. It also includes a + change to help greenlet save and restore the interpreter frame "data stack". + - https://github.com/python/cpython/pull/30122 + - https://github.com/python/cpython/pull/30234 +*/ +# define GREENLET_PY311 1 +#else +# define GREENLET_PY311 0 +#endif + + +#if PY_VERSION_HEX >= 0x30C0000 +# define GREENLET_PY312 1 +#else +# define GREENLET_PY312 0 +#endif + +#ifndef Py_SET_REFCNT +/* Py_REFCNT and Py_SIZE macros are converted to functions +https://bugs.python.org/issue39573 */ +# define Py_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt) +#endif + +#ifndef _Py_DEC_REFTOTAL +/* _Py_DEC_REFTOTAL macro has been removed from Python 3.9 by: + https://github.com/python/cpython/commit/49932fec62c616ec88da52642339d83ae719e924 + + The symbol we use to replace it was removed by at least 3.12. +*/ +# ifdef Py_REF_DEBUG +# if GREENLET_PY312 +# define _Py_DEC_REFTOTAL +# else +# define _Py_DEC_REFTOTAL _Py_RefTotal-- +# endif +# else +# define _Py_DEC_REFTOTAL +# endif +#endif +// Define these flags like Cython does if we're on an old version. +#ifndef Py_TPFLAGS_CHECKTYPES + #define Py_TPFLAGS_CHECKTYPES 0 +#endif +#ifndef Py_TPFLAGS_HAVE_INDEX + #define Py_TPFLAGS_HAVE_INDEX 0 +#endif +#ifndef Py_TPFLAGS_HAVE_NEWBUFFER + #define Py_TPFLAGS_HAVE_NEWBUFFER 0 +#endif + +#ifndef Py_TPFLAGS_HAVE_VERSION_TAG + #define Py_TPFLAGS_HAVE_VERSION_TAG 0 +#endif + +#define G_TPFLAGS_DEFAULT Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_VERSION_TAG | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_HAVE_NEWBUFFER | Py_TPFLAGS_HAVE_GC + + +#if PY_VERSION_HEX < 0x03090000 +// The official version only became available in 3.9 +# define PyObject_GC_IsTracked(o) _PyObject_GC_IS_TRACKED(o) +#endif + + +// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2 +#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +static inline void PyThreadState_EnterTracing(PyThreadState *tstate) +{ + tstate->tracing++; +#if PY_VERSION_HEX >= 0x030A00A1 + tstate->cframe->use_tracing = 0; +#else + tstate->use_tracing = 0; +#endif +} +#endif + +// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2 +#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) +static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) +{ + tstate->tracing--; + int use_tracing = (tstate->c_tracefunc != NULL + || tstate->c_profilefunc != NULL); +#if PY_VERSION_HEX >= 0x030A00A1 + tstate->cframe->use_tracing = use_tracing; +#else + tstate->use_tracing = use_tracing; +#endif +} +#endif + +#endif /* GREENLET_CPYTHON_COMPAT_H */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_exceptions.hpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_exceptions.hpp new file mode 100644 index 0000000..3807018 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_exceptions.hpp @@ -0,0 +1,150 @@ +#ifndef GREENLET_EXCEPTIONS_HPP +#define GREENLET_EXCEPTIONS_HPP + +#define PY_SSIZE_T_CLEAN +#include +#include +#include + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-function" +#endif + +namespace greenlet { + + class PyErrOccurred : public std::runtime_error + { + public: + + // CAUTION: In debug builds, may run arbitrary Python code. + static const PyErrOccurred + from_current() + { + assert(PyErr_Occurred()); +#ifndef NDEBUG + // This is not exception safe, and + // not necessarily safe in general (what if it switches?) + // But we only do this in debug mode, where we are in + // tight control of what exceptions are getting raised and + // can prevent those issues. + + // You can't call PyObject_Str with a pending exception. + PyObject* typ; + PyObject* val; + PyObject* tb; + + PyErr_Fetch(&typ, &val, &tb); + PyObject* typs = PyObject_Str(typ); + PyObject* vals = PyObject_Str(val ? val : typ); + const char* typ_msg = PyUnicode_AsUTF8(typs); + const char* val_msg = PyUnicode_AsUTF8(vals); + PyErr_Restore(typ, val, tb); + + std::string msg(typ_msg); + msg += ": "; + msg += val_msg; + PyErrOccurred ex(msg); + Py_XDECREF(typs); + Py_XDECREF(vals); + + return ex; +#else + return PyErrOccurred(); +#endif + } + + PyErrOccurred() : std::runtime_error("") + { + assert(PyErr_Occurred()); + } + + PyErrOccurred(const std::string& msg) : std::runtime_error(msg) + { + assert(PyErr_Occurred()); + } + + PyErrOccurred(PyObject* exc_kind, const char* const msg) + : std::runtime_error(msg) + { + PyErr_SetString(exc_kind, msg); + } + + PyErrOccurred(PyObject* exc_kind, const std::string msg) + : std::runtime_error(msg) + { + // This copies the c_str, so we don't have any lifetime + // issues to worry about. + PyErr_SetString(exc_kind, msg.c_str()); + } + }; + + class TypeError : public PyErrOccurred + { + public: + TypeError(const char* const what) + : PyErrOccurred(PyExc_TypeError, what) + { + } + TypeError(const std::string what) + : PyErrOccurred(PyExc_TypeError, what) + { + } + }; + + class ValueError : public PyErrOccurred + { + public: + ValueError(const char* const what) + : PyErrOccurred(PyExc_ValueError, what) + { + } + }; + + class AttributeError : public PyErrOccurred + { + public: + AttributeError(const char* const what) + : PyErrOccurred(PyExc_AttributeError, what) + { + } + }; + + /** + * Calls `Py_FatalError` when constructed, so you can't actually + * throw this. It just makes static analysis easier. + */ + class PyFatalError : public std::runtime_error + { + public: + PyFatalError(const char* const msg) + : std::runtime_error(msg) + { + Py_FatalError(msg); + } + }; + + static inline PyObject* + Require(PyObject* p, const std::string& msg="") + { + if (!p) { + throw PyErrOccurred(msg); + } + return p; + }; + + static inline void + Require(const int retval) + { + if (retval < 0) { + throw PyErrOccurred(); + } + }; + + +}; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_greenlet.hpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_greenlet.hpp new file mode 100644 index 0000000..d52ce1f --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_greenlet.hpp @@ -0,0 +1,805 @@ +#ifndef GREENLET_GREENLET_HPP +#define GREENLET_GREENLET_HPP +/* + * Declarations of the core data structures. +*/ + +#define PY_SSIZE_T_CLEAN +#include + +#include "greenlet_compiler_compat.hpp" +#include "greenlet_refs.hpp" +#include "greenlet_cpython_compat.hpp" +#include "greenlet_allocator.hpp" + +using greenlet::refs::OwnedObject; +using greenlet::refs::OwnedGreenlet; +using greenlet::refs::OwnedMainGreenlet; +using greenlet::refs::BorrowedGreenlet; + +#if PY_VERSION_HEX < 0x30B00A6 +# define _PyCFrame CFrame +# define _PyInterpreterFrame _interpreter_frame +#endif + +#if GREENLET_PY312 +# include "internal/pycore_frame.h" +#endif + +// XXX: TODO: Work to remove all virtual functions +// for speed of calling and size of objects (no vtable). +// One pattern is the Curiously Recurring Template +namespace greenlet +{ + class ExceptionState + { + private: + G_NO_COPIES_OF_CLS(ExceptionState); + + // Even though these are borrowed objects, we actually own + // them, when they're not null. + // XXX: Express that in the API. + private: + _PyErr_StackItem* exc_info; + _PyErr_StackItem exc_state; + public: + ExceptionState(); + void operator<<(const PyThreadState *const tstate) noexcept; + void operator>>(PyThreadState* tstate) noexcept; + void clear() noexcept; + + int tp_traverse(visitproc visit, void* arg) noexcept; + void tp_clear() noexcept; + }; + + template + void operator<<(const PyThreadState *const tstate, T& exc); + + class PythonStateContext + { + protected: + greenlet::refs::OwnedContext _context; + public: + inline const greenlet::refs::OwnedContext& context() const + { + return this->_context; + } + inline greenlet::refs::OwnedContext& context() + { + return this->_context; + } + + inline void tp_clear() + { + this->_context.CLEAR(); + } + + template + inline static PyObject* context(T* tstate) + { + return tstate->context; + } + + template + inline static void context(T* tstate, PyObject* new_context) + { + tstate->context = new_context; + tstate->context_ver++; + } + }; + class SwitchingArgs; + class PythonState : public PythonStateContext + { + public: + typedef greenlet::refs::OwnedReference OwnedFrame; + private: + G_NO_COPIES_OF_CLS(PythonState); + // We own this if we're suspended (although currently we don't + // tp_traverse into it; that's a TODO). If we're running, it's + // empty. If we get deallocated and *still* have a frame, it + // won't be reachable from the place that normally decref's + // it, so we need to do it (hence owning it). + OwnedFrame _top_frame; +#if GREENLET_USE_CFRAME + _PyCFrame* cframe; + int use_tracing; +#endif +#if GREENLET_PY312 + int py_recursion_depth; + int c_recursion_depth; +#else + int recursion_depth; +#endif + int trash_delete_nesting; +#if GREENLET_PY311 + _PyInterpreterFrame* current_frame; + _PyStackChunk* datastack_chunk; + PyObject** datastack_top; + PyObject** datastack_limit; +#endif + // The PyInterpreterFrame list on 3.12+ contains some entries that are + // on the C stack, which can't be directly accessed while a greenlet is + // suspended. In order to keep greenlet gr_frame introspection working, + // we adjust stack switching to rewrite the interpreter frame list + // to skip these C-stack frames; we call this "exposing" the greenlet's + // frames because it makes them valid to work with in Python. Then when + // the greenlet is resumed we need to remember to reverse the operation + // we did. The C-stack frames are "entry frames" which are a low-level + // interpreter detail; they're not needed for introspection, but do + // need to be present for the eval loop to work. + void unexpose_frames(); + + public: + + PythonState(); + // You can use this for testing whether we have a frame + // or not. It returns const so they can't modify it. + const OwnedFrame& top_frame() const noexcept; + + inline void operator<<(const PyThreadState *const tstate) noexcept; + inline void operator>>(PyThreadState* tstate) noexcept; + void clear() noexcept; + + int tp_traverse(visitproc visit, void* arg, bool visit_top_frame) noexcept; + void tp_clear(bool own_top_frame) noexcept; + void set_initial_state(const PyThreadState* const tstate) noexcept; +#if GREENLET_USE_CFRAME + void set_new_cframe(_PyCFrame& frame) noexcept; +#endif + + inline void may_switch_away() noexcept; + inline void will_switch_from(PyThreadState *const origin_tstate) noexcept; + void did_finish(PyThreadState* tstate) noexcept; + }; + + class StackState + { + // By having only plain C (POD) members, no virtual functions + // or bases, we get a trivial assignment operator generated + // for us. However, that's not safe since we do manage memory. + // So we declare an assignment operator that only works if we + // don't have any memory allocated. (We don't use + // std::shared_ptr for reference counting just to keep this + // object small) + private: + char* _stack_start; + char* stack_stop; + char* stack_copy; + intptr_t _stack_saved; + StackState* stack_prev; + inline int copy_stack_to_heap_up_to(const char* const stop) noexcept; + inline void free_stack_copy() noexcept; + + public: + /** + * Creates a started, but inactive, state, using *current* + * as the previous. + */ + StackState(void* mark, StackState& current); + /** + * Creates an inactive, unstarted, state. + */ + StackState(); + ~StackState(); + StackState(const StackState& other); + StackState& operator=(const StackState& other); + inline void copy_heap_to_stack(const StackState& current) noexcept; + inline int copy_stack_to_heap(char* const stackref, const StackState& current) noexcept; + inline bool started() const noexcept; + inline bool main() const noexcept; + inline bool active() const noexcept; + inline void set_active() noexcept; + inline void set_inactive() noexcept; + inline intptr_t stack_saved() const noexcept; + inline char* stack_start() const noexcept; + static inline StackState make_main() noexcept; +#ifdef GREENLET_USE_STDIO + friend std::ostream& operator<<(std::ostream& os, const StackState& s); +#endif + + // Fill in [dest, dest + n) with the values that would be at + // [src, src + n) while this greenlet is running. This is like memcpy + // except that if the greenlet is suspended it accounts for the portion + // of the greenlet's stack that was spilled to the heap. `src` may + // be on this greenlet's stack, or on the heap, but not on a different + // greenlet's stack. + void copy_from_stack(void* dest, const void* src, size_t n) const; + }; +#ifdef GREENLET_USE_STDIO + std::ostream& operator<<(std::ostream& os, const StackState& s); +#endif + + class SwitchingArgs + { + private: + G_NO_ASSIGNMENT_OF_CLS(SwitchingArgs); + // If args and kwargs are both false (NULL), this is a *throw*, not a + // switch. PyErr_... must have been called already. + OwnedObject _args; + OwnedObject _kwargs; + public: + + SwitchingArgs() + {} + + SwitchingArgs(const OwnedObject& args, const OwnedObject& kwargs) + : _args(args), + _kwargs(kwargs) + {} + + SwitchingArgs(const SwitchingArgs& other) + : _args(other._args), + _kwargs(other._kwargs) + {} + + const OwnedObject& args() + { + return this->_args; + } + + const OwnedObject& kwargs() + { + return this->_kwargs; + } + + /** + * Moves ownership from the argument to this object. + */ + SwitchingArgs& operator<<=(SwitchingArgs& other) + { + if (this != &other) { + this->_args = other._args; + this->_kwargs = other._kwargs; + other.CLEAR(); + } + return *this; + } + + /** + * Acquires ownership of the argument (consumes the reference). + */ + SwitchingArgs& operator<<=(PyObject* args) + { + this->_args = OwnedObject::consuming(args); + this->_kwargs.CLEAR(); + return *this; + } + + /** + * Acquires ownership of the argument. + * + * Sets the args to be the given value; clears the kwargs. + */ + SwitchingArgs& operator<<=(OwnedObject& args) + { + assert(&args != &this->_args); + this->_args = args; + this->_kwargs.CLEAR(); + args.CLEAR(); + + return *this; + } + + explicit operator bool() const noexcept + { + return this->_args || this->_kwargs; + } + + inline void CLEAR() + { + this->_args.CLEAR(); + this->_kwargs.CLEAR(); + } + + const std::string as_str() const noexcept + { + return PyUnicode_AsUTF8( + OwnedObject::consuming( + PyUnicode_FromFormat( + "SwitchingArgs(args=%R, kwargs=%R)", + this->_args.borrow(), + this->_kwargs.borrow() + ) + ).borrow() + ); + } + }; + + class ThreadState; + + class UserGreenlet; + class MainGreenlet; + + class Greenlet + { + private: + G_NO_COPIES_OF_CLS(Greenlet); + private: + // XXX: Work to remove these. + friend class ThreadState; + friend class UserGreenlet; + friend class MainGreenlet; + protected: + ExceptionState exception_state; + SwitchingArgs switch_args; + StackState stack_state; + PythonState python_state; + Greenlet(PyGreenlet* p, const StackState& initial_state); + public: + Greenlet(PyGreenlet* p); + virtual ~Greenlet(); + + const OwnedObject context() const; + + // You MUST call this _very_ early in the switching process to + // prepare anything that may need prepared. This might perform + // garbage collections or otherwise run arbitrary Python code. + // + // One specific use of it is for Python 3.11+, preventing + // running arbitrary code at unsafe times. See + // PythonState::may_switch_away(). + inline void may_switch_away() + { + this->python_state.may_switch_away(); + } + + inline void context(refs::BorrowedObject new_context); + + inline SwitchingArgs& args() + { + return this->switch_args; + } + + virtual const refs::BorrowedMainGreenlet main_greenlet() const = 0; + + inline intptr_t stack_saved() const noexcept + { + return this->stack_state.stack_saved(); + } + + // This is used by the macro SLP_SAVE_STATE to compute the + // difference in stack sizes. It might be nice to handle the + // computation ourself, but the type of the result + // varies by platform, so doing it in the macro is the + // simplest way. + inline const char* stack_start() const noexcept + { + return this->stack_state.stack_start(); + } + + virtual OwnedObject throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state); + virtual OwnedObject g_switch() = 0; + /** + * Force the greenlet to appear dead. Used when it's not + * possible to throw an exception into a greenlet anymore. + * + * This losses access to the thread state and the main greenlet. + */ + virtual void murder_in_place(); + + /** + * Called when somebody notices we were running in a dead + * thread to allow cleaning up resources (because we can't + * raise GreenletExit into it anymore). + * This is very similar to ``murder_in_place()``, except that + * it DOES NOT lose the main greenlet or thread state. + */ + inline void deactivate_and_free(); + + + // Called when some thread wants to deallocate a greenlet + // object. + // The thread may or may not be the same thread the greenlet + // was running in. + // The thread state will be null if the thread the greenlet + // was running in was known to have exited. + void deallocing_greenlet_in_thread(const ThreadState* current_state); + + // Must be called on 3.12+ before exposing a suspended greenlet's + // frames to user code. This rewrites the linked list of interpreter + // frames to skip the ones that are being stored on the C stack (which + // can't be safely accessed while the greenlet is suspended because + // that stack space might be hosting a different greenlet), and + // sets PythonState::frames_were_exposed so we remember to restore + // the original list before resuming the greenlet. The C-stack frames + // are a low-level interpreter implementation detail; while they're + // important to the bytecode eval loop, they're superfluous for + // introspection purposes. + void expose_frames(); + + + // TODO: Figure out how to make these non-public. + inline void slp_restore_state() noexcept; + inline int slp_save_state(char *const stackref) noexcept; + + inline bool is_currently_running_in_some_thread() const; + virtual bool belongs_to_thread(const ThreadState* state) const; + + inline bool started() const + { + return this->stack_state.started(); + } + inline bool active() const + { + return this->stack_state.active(); + } + inline bool main() const + { + return this->stack_state.main(); + } + virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const = 0; + + virtual const OwnedGreenlet parent() const = 0; + virtual void parent(const refs::BorrowedObject new_parent) = 0; + + inline const PythonState::OwnedFrame& top_frame() + { + return this->python_state.top_frame(); + } + + virtual const OwnedObject& run() const = 0; + virtual void run(const refs::BorrowedObject nrun) = 0; + + + virtual int tp_traverse(visitproc visit, void* arg); + virtual int tp_clear(); + + + // Return the thread state that the greenlet is running in, or + // null if the greenlet is not running or the thread is known + // to have exited. + virtual ThreadState* thread_state() const noexcept = 0; + + // Return true if the greenlet is known to have been running + // (active) in a thread that has now exited. + virtual bool was_running_in_dead_thread() const noexcept = 0; + + // Return a borrowed greenlet that is the Python object + // this object represents. + virtual BorrowedGreenlet self() const noexcept = 0; + + // For testing. If this returns true, we should pretend that + // slp_switch() failed. + virtual bool force_slp_switch_error() const noexcept; + + protected: + inline void release_args(); + + // The functions that must not be inlined are declared virtual. + // We also mark them as protected, not private, so that the + // compiler is forced to call them through a function pointer. + // (A sufficiently smart compiler could directly call a private + // virtual function since it can never be overridden in a + // subclass). + + // Also TODO: Switch away from integer error codes and to enums, + // or throw exceptions when possible. + struct switchstack_result_t + { + int status; + Greenlet* the_new_current_greenlet; + OwnedGreenlet origin_greenlet; + + switchstack_result_t() + : status(0), + the_new_current_greenlet(nullptr) + {} + + switchstack_result_t(int err) + : status(err), + the_new_current_greenlet(nullptr) + {} + + switchstack_result_t(int err, Greenlet* state, OwnedGreenlet& origin) + : status(err), + the_new_current_greenlet(state), + origin_greenlet(origin) + { + } + + switchstack_result_t(int err, Greenlet* state, const BorrowedGreenlet& origin) + : status(err), + the_new_current_greenlet(state), + origin_greenlet(origin) + { + } + + switchstack_result_t(const switchstack_result_t& other) + : status(other.status), + the_new_current_greenlet(other.the_new_current_greenlet), + origin_greenlet(other.origin_greenlet) + {} + + switchstack_result_t& operator=(const switchstack_result_t& other) + { + this->status = other.status; + this->the_new_current_greenlet = other.the_new_current_greenlet; + this->origin_greenlet = other.origin_greenlet; + return *this; + } + }; + + OwnedObject on_switchstack_or_initialstub_failure( + Greenlet* target, + const switchstack_result_t& err, + const bool target_was_me=false, + const bool was_initial_stub=false); + + // Returns the previous greenlet we just switched away from. + virtual OwnedGreenlet g_switchstack_success() noexcept; + + + // Check the preconditions for switching to this greenlet; if they + // aren't met, throws PyErrOccurred. Most callers will want to + // catch this and clear the arguments + inline void check_switch_allowed() const; + class GreenletStartedWhileInPython : public std::runtime_error + { + public: + GreenletStartedWhileInPython() : std::runtime_error("") + {} + }; + + protected: + + + /** + Perform a stack switch into this greenlet. + + This temporarily sets the global variable + ``switching_thread_state`` to this greenlet; as soon as the + call to ``slp_switch`` completes, this is reset to NULL. + Consequently, this depends on the GIL. + + TODO: Adopt the stackman model and pass ``slp_switch`` a + callback function and context pointer; this eliminates the + need for global variables altogether. + + Because the stack switch happens in this function, this + function can't use its own stack (local) variables, set + before the switch, and then accessed after the switch. + + Further, you con't even access ``g_thread_state_global`` + before and after the switch from the global variable. + Because it is thread local some compilers cache it in a + register/on the stack, notably new versions of MSVC; this + breaks with strange crashes sometime later, because writing + to anything in ``g_thread_state_global`` after the switch + is actually writing to random memory. For this reason, we + call a non-inlined function to finish the operation. (XXX: + The ``/GT`` MSVC compiler argument probably fixes that.) + + It is very important that stack switch is 'atomic', i.e. no + calls into other Python code allowed (except very few that + are safe), because global variables are very fragile. (This + should no longer be the case with thread-local variables.) + + */ + // Made virtual to facilitate subclassing UserGreenlet for testing. + virtual switchstack_result_t g_switchstack(void); + +class TracingGuard +{ +private: + PyThreadState* tstate; +public: + TracingGuard() + : tstate(PyThreadState_GET()) + { + PyThreadState_EnterTracing(this->tstate); + } + + ~TracingGuard() + { + PyThreadState_LeaveTracing(this->tstate); + this->tstate = nullptr; + } + + inline void CallTraceFunction(const OwnedObject& tracefunc, + const greenlet::refs::ImmortalEventName& event, + const BorrowedGreenlet& origin, + const BorrowedGreenlet& target) + { + // TODO: This calls tracefunc(event, (origin, target)). Add a shortcut + // function for that that's specialized to avoid the Py_BuildValue + // string parsing, or start with just using "ON" format with PyTuple_Pack(2, + // origin, target). That seems like what the N format is meant + // for. + // XXX: Why does event not automatically cast back to a PyObject? + // It tries to call the "deleted constructor ImmortalEventName + // const" instead. + assert(tracefunc); + assert(event); + assert(origin); + assert(target); + greenlet::refs::NewReference retval( + PyObject_CallFunction( + tracefunc.borrow(), + "O(OO)", + event.borrow(), + origin.borrow(), + target.borrow() + )); + if (!retval) { + throw PyErrOccurred::from_current(); + } + } +}; + + static void + g_calltrace(const OwnedObject& tracefunc, + const greenlet::refs::ImmortalEventName& event, + const greenlet::refs::BorrowedGreenlet& origin, + const BorrowedGreenlet& target); + private: + OwnedObject g_switch_finish(const switchstack_result_t& err); + + }; + + class UserGreenlet : public Greenlet + { + private: + static greenlet::PythonAllocator allocator; + BorrowedGreenlet _self; + OwnedMainGreenlet _main_greenlet; + OwnedObject _run_callable; + OwnedGreenlet _parent; + public: + static void* operator new(size_t UNUSED(count)); + static void operator delete(void* ptr); + + UserGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent); + virtual ~UserGreenlet(); + + virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const; + virtual bool was_running_in_dead_thread() const noexcept; + virtual ThreadState* thread_state() const noexcept; + virtual OwnedObject g_switch(); + virtual const OwnedObject& run() const + { + if (this->started() || !this->_run_callable) { + throw AttributeError("run"); + } + return this->_run_callable; + } + virtual void run(const refs::BorrowedObject nrun); + + virtual const OwnedGreenlet parent() const; + virtual void parent(const refs::BorrowedObject new_parent); + + virtual const refs::BorrowedMainGreenlet main_greenlet() const; + + virtual BorrowedGreenlet self() const noexcept; + virtual void murder_in_place(); + virtual bool belongs_to_thread(const ThreadState* state) const; + virtual int tp_traverse(visitproc visit, void* arg); + virtual int tp_clear(); + class ParentIsCurrentGuard + { + private: + OwnedGreenlet oldparent; + UserGreenlet* greenlet; + G_NO_COPIES_OF_CLS(ParentIsCurrentGuard); + public: + ParentIsCurrentGuard(UserGreenlet* p, const ThreadState& thread_state); + ~ParentIsCurrentGuard(); + }; + virtual OwnedObject throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state); + protected: + virtual switchstack_result_t g_initialstub(void* mark); + private: + // This function isn't meant to return. + // This accepts raw pointers and the ownership of them at the + // same time. The caller should use ``inner_bootstrap(origin.relinquish_ownership())``. + void inner_bootstrap(PyGreenlet* origin_greenlet, PyObject* run); + }; + + class BrokenGreenlet : public UserGreenlet + { + private: + static greenlet::PythonAllocator allocator; + public: + bool _force_switch_error = false; + bool _force_slp_switch_error = false; + + static void* operator new(size_t UNUSED(count)); + static void operator delete(void* ptr); + BrokenGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent) + : UserGreenlet(p, the_parent) + {} + virtual ~BrokenGreenlet() + {} + + virtual switchstack_result_t g_switchstack(void); + virtual bool force_slp_switch_error() const noexcept; + + }; + + class MainGreenlet : public Greenlet + { + private: + static greenlet::PythonAllocator allocator; + refs::BorrowedMainGreenlet _self; + ThreadState* _thread_state; + G_NO_COPIES_OF_CLS(MainGreenlet); + public: + static void* operator new(size_t UNUSED(count)); + static void operator delete(void* ptr); + + MainGreenlet(refs::BorrowedMainGreenlet::PyType*, ThreadState*); + virtual ~MainGreenlet(); + + + virtual const OwnedObject& run() const; + virtual void run(const refs::BorrowedObject nrun); + + virtual const OwnedGreenlet parent() const; + virtual void parent(const refs::BorrowedObject new_parent); + + virtual const refs::BorrowedMainGreenlet main_greenlet() const; + + virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const; + virtual bool was_running_in_dead_thread() const noexcept; + virtual ThreadState* thread_state() const noexcept; + void thread_state(ThreadState*) noexcept; + virtual OwnedObject g_switch(); + virtual BorrowedGreenlet self() const noexcept; + virtual int tp_traverse(visitproc visit, void* arg); + }; + + // Instantiate one on the stack to save the GC state, + // and then disable GC. When it goes out of scope, GC will be + // restored to its original state. Sadly, these APIs are only + // available on 3.10+; luckily, we only need them on 3.11+. +#if GREENLET_PY310 + class GCDisabledGuard + { + private: + int was_enabled = 0; + public: + GCDisabledGuard() + : was_enabled(PyGC_IsEnabled()) + { + PyGC_Disable(); + } + + ~GCDisabledGuard() + { + if (this->was_enabled) { + PyGC_Enable(); + } + } + }; +#endif + + OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept; + + //TODO: Greenlet::g_switch() should call this automatically on its + //return value. As it is, the module code is calling it. + static inline OwnedObject + single_result(const OwnedObject& results) + { + if (results + && PyTuple_Check(results.borrow()) + && PyTuple_GET_SIZE(results.borrow()) == 1) { + PyObject* result = PyTuple_GET_ITEM(results.borrow(), 0); + assert(result); + return OwnedObject::owning(result); + } + return results; + } + + + static OwnedObject + g_handle_exit(const OwnedObject& greenlet_result); + + + template + void operator<<(const PyThreadState *const lhs, T& rhs) + { + rhs.operator<<(lhs); + } + +} // namespace greenlet ; + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_internal.hpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_internal.hpp new file mode 100644 index 0000000..c8e3849 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_internal.hpp @@ -0,0 +1,106 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ +#ifndef GREENLET_INTERNAL_H +#define GREENLET_INTERNAL_H +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-function" +# pragma clang diagnostic ignored "-Wmissing-field-initializers" +# pragma clang diagnostic ignored "-Wunused-variable" +#endif + +/** + * Implementation helpers. + * + * C++ templates and inline functions should go here. + */ +#define PY_SSIZE_T_CLEAN +#include "greenlet_compiler_compat.hpp" +#include "greenlet_cpython_compat.hpp" +#include "greenlet_exceptions.hpp" +#include "greenlet_greenlet.hpp" +#include "greenlet_allocator.hpp" + +#include +#include + +#define GREENLET_MODULE +struct _greenlet; +typedef struct _greenlet PyGreenlet; +namespace greenlet { + + class ThreadState; + +}; + + +#define implementation_ptr_t greenlet::Greenlet* + + +#include "greenlet.h" + +G_FP_TMPL_STATIC inline void +greenlet::refs::MainGreenletExactChecker(void *p) +{ + if (!p) { + return; + } + // We control the class of the main greenlet exactly. + if (Py_TYPE(p) != &PyGreenlet_Type) { + std::string err("MainGreenlet: Expected exactly a greenlet, not a "); + err += Py_TYPE(p)->tp_name; + throw greenlet::TypeError(err); + } + + // Greenlets from dead threads no longer respond to main() with a + // true value; so in that case we need to perform an additional + // check. + Greenlet* g = ((PyGreenlet*)p)->pimpl; + if (g->main()) { + return; + } + if (!dynamic_cast(g)) { + std::string err("MainGreenlet: Expected exactly a main greenlet, not a "); + err += Py_TYPE(p)->tp_name; + throw greenlet::TypeError(err); + } +} + + + +template +inline greenlet::Greenlet* greenlet::refs::_OwnedGreenlet::operator->() const noexcept +{ + return reinterpret_cast(this->p)->pimpl; +} + +template +inline greenlet::Greenlet* greenlet::refs::_BorrowedGreenlet::operator->() const noexcept +{ + return reinterpret_cast(this->p)->pimpl; +} + +#include +#include + + +extern PyTypeObject PyGreenlet_Type; + + + +/** + * Forward declarations needed in multiple files. + */ +static PyGreenlet* green_create_main(greenlet::ThreadState*); +static PyObject* green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs); +static int green_is_gc(BorrowedGreenlet self); + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + + +#endif + +// Local Variables: +// flycheck-clang-include-path: ("../../include" "/opt/local/Library/Frameworks/Python.framework/Versions/3.10/include/python3.10") +// End: diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_refs.hpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_refs.hpp new file mode 100644 index 0000000..72ee68b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_refs.hpp @@ -0,0 +1,1100 @@ +#ifndef GREENLET_REFS_HPP +#define GREENLET_REFS_HPP + +#define PY_SSIZE_T_CLEAN +#include +//#include "greenlet_internal.hpp" +#include "greenlet_compiler_compat.hpp" +#include "greenlet_cpython_compat.hpp" +#include "greenlet_exceptions.hpp" + +struct _greenlet; +struct _PyMainGreenlet; + +typedef struct _greenlet PyGreenlet; +extern PyTypeObject PyGreenlet_Type; + + +#ifdef GREENLET_USE_STDIO +#include +using std::cerr; +using std::endl; +#endif + +namespace greenlet +{ + class Greenlet; + + namespace refs + { + // Type checkers throw a TypeError if the argument is not + // null, and isn't of the required Python type. + // (We can't use most of the defined type checkers + // like PyList_Check, etc, directly, because they are + // implemented as macros.) + typedef void (*TypeChecker)(void*); + + G_FP_TMPL_STATIC inline void + NoOpChecker(void*) + { + return; + } + + G_FP_TMPL_STATIC inline void + GreenletChecker(void *p) + { + if (!p) { + return; + } + + PyTypeObject* typ = Py_TYPE(p); + // fast, common path. (PyObject_TypeCheck is a macro or + // static inline function, and it also does a + // direct comparison of the type pointers, but its fast + // path only handles one type) + if (typ == &PyGreenlet_Type) { + return; + } + + if (!PyObject_TypeCheck(p, &PyGreenlet_Type)) { + std::string err("GreenletChecker: Expected any type of greenlet, not "); + err += Py_TYPE(p)->tp_name; + throw TypeError(err); + } + } + + G_FP_TMPL_STATIC inline void + MainGreenletExactChecker(void *p); + + template + class PyObjectPointer; + + template + class OwnedReference; + + + template + class BorrowedReference; + + typedef BorrowedReference BorrowedObject; + typedef OwnedReference OwnedObject; + + class ImmortalObject; + class ImmortalString; + + template + class _OwnedGreenlet; + + typedef _OwnedGreenlet OwnedGreenlet; + typedef _OwnedGreenlet OwnedMainGreenlet; + + template + class _BorrowedGreenlet; + + typedef _BorrowedGreenlet BorrowedGreenlet; + + G_FP_TMPL_STATIC inline void + ContextExactChecker(void *p) + { + if (!p) { + return; + } + if (!PyContext_CheckExact(p)) { + throw TypeError( + "greenlet context must be a contextvars.Context or None" + ); + } + } + + typedef OwnedReference OwnedContext; + } +} + +namespace greenlet { + + + namespace refs { + // A set of classes to make reference counting rules in python + // code explicit. + // + // Rules of use: + // (1) Functions returning a new reference that the caller of the + // function is expected to dispose of should return a + // ``OwnedObject`` object. This object automatically releases its + // reference when it goes out of scope. It works like a ``std::shared_ptr`` + // and can be copied or used as a function parameter (but don't do + // that). Note that constructing a ``OwnedObject`` from a + // PyObject* steals the reference. + // (2) Parameters to functions should be either a + // ``OwnedObject&``, or, more generally, a ``PyObjectPointer&``. + // If the function needs to create its own new reference, it can + // do so by copying to a local ``OwnedObject``. + // (3) Functions returning an existing pointer that is NOT + // incref'd, and which the caller MUST NOT decref, + // should return a ``BorrowedObject``. + + // + // For a class with a single pointer member, whose constructor + // does nothing but copy a pointer parameter into the member, and + // which can then be converted back to the pointer type, compilers + // generate code that's the same as just passing the pointer. + // That is, func(BorrowedObject x) called like ``PyObject* p = + // ...; f(p)`` has 0 overhead. Similarly, they "unpack" to the + // pointer type with 0 overhead. + // + // If there are no virtual functions, no complex inheritance (maybe?) and + // no destructor, these can be directly used as parameters in + // Python callbacks like tp_init: the layout is the same as a + // single pointer. Only subclasses with trivial constructors that + // do nothing but set the single pointer member are safe to use + // that way. + + + // This is the base class for things that can be done with a + // PyObject pointer. It assumes nothing about memory management. + // NOTE: Nothing is virtual, so subclasses shouldn't add new + // storage fields or try to override these methods. + template + class PyObjectPointer + { + public: + typedef T PyType; + protected: + T* p; + public: + explicit PyObjectPointer(T* it=nullptr) : p(it) + { + TC(p); + } + + // We don't allow automatic casting to PyObject* at this + // level, because then we could be passed to Py_DECREF/INCREF, + // but we want nothing to do with memory management. If you + // know better, then you can use the get() method, like on a + // std::shared_ptr. Except we name it borrow() to clarify that + // if this is a reference-tracked object, the pointer you get + // back will go away when the object does. + // TODO: This should probably not exist here, but be moved + // down to relevant sub-types. + + inline T* borrow() const noexcept + { + return this->p; + } + + PyObject* borrow_o() const noexcept + { + return reinterpret_cast(this->p); + } + + inline T* operator->() const noexcept + { + return this->p; + } + + bool is_None() const noexcept + { + return this->p == Py_None; + } + + inline PyObject* acquire_or_None() const noexcept + { + PyObject* result = this->p ? reinterpret_cast(this->p) : Py_None; + Py_INCREF(result); + return result; + } + + explicit operator bool() const noexcept + { + return p != nullptr; + } + + inline Py_ssize_t REFCNT() const noexcept + { + return p ? Py_REFCNT(p) : -42; + } + + inline PyTypeObject* TYPE() const noexcept + { + return p ? Py_TYPE(p) : nullptr; + } + + inline OwnedObject PyStr() const noexcept; + inline const std::string as_str() const noexcept; + inline OwnedObject PyGetAttr(const ImmortalObject& name) const noexcept; + inline OwnedObject PyRequireAttr(const char* const name) const; + inline OwnedObject PyRequireAttr(const ImmortalString& name) const; + inline OwnedObject PyCall(const BorrowedObject& arg) const; + inline OwnedObject PyCall(PyGreenlet* arg) const ; + inline OwnedObject PyCall(PyObject* arg) const ; + // PyObject_Call(this, args, kwargs); + inline OwnedObject PyCall(const BorrowedObject args, + const BorrowedObject kwargs) const; + inline OwnedObject PyCall(const OwnedObject& args, + const OwnedObject& kwargs) const; + + protected: + void _set_raw_pointer(void* t) + { + TC(t); + p = reinterpret_cast(t); + } + void* _get_raw_pointer() const + { + return p; + } + }; + +#ifdef GREENLET_USE_STDIO + template + std::ostream& operator<<(std::ostream& os, const PyObjectPointer& s) + { + const std::type_info& t = typeid(s); + os << t.name() + << "(addr=" << s.borrow() + << ", refcnt=" << s.REFCNT() + << ", value=" << s.as_str() + << ")"; + + return os; + } +#endif + + template + inline bool operator==(const PyObjectPointer& lhs, const void* const rhs) noexcept + { + return lhs.borrow_o() == rhs; + } + + template + inline bool operator==(const PyObjectPointer& lhs, const PyObjectPointer& rhs) noexcept + { + return lhs.borrow_o() == rhs.borrow_o(); + } + + template + inline bool operator!=(const PyObjectPointer& lhs, + const PyObjectPointer& rhs) noexcept + { + return lhs.borrow_o() != rhs.borrow_o(); + } + + template + class OwnedReference : public PyObjectPointer + { + private: + friend class OwnedList; + + protected: + explicit OwnedReference(T* it) : PyObjectPointer(it) + { + } + + public: + + // Constructors + + static OwnedReference consuming(PyObject* p) + { + return OwnedReference(reinterpret_cast(p)); + } + + static OwnedReference owning(T* p) + { + OwnedReference result(p); + Py_XINCREF(result.p); + return result; + } + + OwnedReference() : PyObjectPointer(nullptr) + {} + + explicit OwnedReference(const PyObjectPointer<>& other) + : PyObjectPointer(nullptr) + { + T* op = other.borrow(); + TC(op); + this->p = other.borrow(); + Py_XINCREF(this->p); + } + + // It would be good to make use of the C++11 distinction + // between move and copy operations, e.g., constructing from a + // pointer should be a move operation. + // In the common case of ``OwnedObject x = Py_SomeFunction()``, + // the call to the copy constructor will be elided completely. + OwnedReference(const OwnedReference& other) + : PyObjectPointer(other.p) + { + Py_XINCREF(this->p); + } + + static OwnedReference None() + { + Py_INCREF(Py_None); + return OwnedReference(Py_None); + } + + // We can assign from exactly our type without any extra checking + OwnedReference& operator=(const OwnedReference& other) + { + Py_XINCREF(other.p); + const T* tmp = this->p; + this->p = other.p; + Py_XDECREF(tmp); + return *this; + } + + OwnedReference& operator=(const BorrowedReference other) + { + return this->operator=(other.borrow()); + } + + OwnedReference& operator=(T* const other) + { + TC(other); + Py_XINCREF(other); + T* tmp = this->p; + this->p = other; + Py_XDECREF(tmp); + return *this; + } + + // We can assign from an arbitrary reference type + // if it passes our check. + template + OwnedReference& operator=(const OwnedReference& other) + { + X* op = other.borrow(); + TC(op); + return this->operator=(reinterpret_cast(op)); + } + + inline void steal(T* other) + { + assert(this->p == nullptr); + TC(other); + this->p = other; + } + + T* relinquish_ownership() + { + T* result = this->p; + this->p = nullptr; + return result; + } + + T* acquire() const + { + // Return a new reference. + // TODO: This may go away when we have reference objects + // throughout the code. + Py_XINCREF(this->p); + return this->p; + } + + // Nothing else declares a destructor, we're the leaf, so we + // should be able to get away without virtual. + ~OwnedReference() + { + Py_CLEAR(this->p); + } + + void CLEAR() + { + Py_CLEAR(this->p); + assert(this->p == nullptr); + } + }; + + static inline + void operator<<=(PyObject*& target, OwnedObject& o) + { + target = o.relinquish_ownership(); + } + + class NewReference : public OwnedObject + { + private: + G_NO_COPIES_OF_CLS(NewReference); + public: + // Consumes the reference. Only use this + // for API return values. + NewReference(PyObject* it) : OwnedObject(it) + { + } + }; + + class NewDictReference : public NewReference + { + private: + G_NO_COPIES_OF_CLS(NewDictReference); + public: + NewDictReference() : NewReference(PyDict_New()) + { + if (!this->p) { + throw PyErrOccurred(); + } + } + + void SetItem(const char* const key, PyObject* value) + { + Require(PyDict_SetItemString(this->p, key, value)); + } + + void SetItem(const PyObjectPointer<>& key, PyObject* value) + { + Require(PyDict_SetItem(this->p, key.borrow_o(), value)); + } + }; + + template + class _OwnedGreenlet: public OwnedReference + { + private: + protected: + _OwnedGreenlet(T* it) : OwnedReference(it) + {} + + public: + _OwnedGreenlet() : OwnedReference() + {} + + _OwnedGreenlet(const _OwnedGreenlet& other) : OwnedReference(other) + { + } + _OwnedGreenlet(OwnedMainGreenlet& other) : + OwnedReference(reinterpret_cast(other.acquire())) + { + } + _OwnedGreenlet(const BorrowedGreenlet& other); + // Steals a reference. + static _OwnedGreenlet consuming(PyGreenlet* it) + { + return _OwnedGreenlet(reinterpret_cast(it)); + } + + inline _OwnedGreenlet& operator=(const OwnedGreenlet& other) + { + return this->operator=(other.borrow()); + } + + inline _OwnedGreenlet& operator=(const BorrowedGreenlet& other); + + _OwnedGreenlet& operator=(const OwnedMainGreenlet& other) + { + PyGreenlet* owned = other.acquire(); + Py_XDECREF(this->p); + this->p = reinterpret_cast(owned); + return *this; + } + + _OwnedGreenlet& operator=(T* const other) + { + OwnedReference::operator=(other); + return *this; + } + + T* relinquish_ownership() + { + T* result = this->p; + this->p = nullptr; + return result; + } + + PyObject* relinquish_ownership_o() + { + return reinterpret_cast(relinquish_ownership()); + } + + inline Greenlet* operator->() const noexcept; + inline operator Greenlet*() const noexcept; + }; + + template + class BorrowedReference : public PyObjectPointer + { + public: + // Allow implicit creation from PyObject* pointers as we + // transition to using these classes. Also allow automatic + // conversion to PyObject* for passing to C API calls and even + // for Py_INCREF/DECREF, because we ourselves do no memory management. + BorrowedReference(T* it) : PyObjectPointer(it) + {} + + BorrowedReference(const PyObjectPointer& ref) : PyObjectPointer(ref.borrow()) + {} + + BorrowedReference() : PyObjectPointer(nullptr) + {} + + operator T*() const + { + return this->p; + } + }; + + typedef BorrowedReference BorrowedObject; + //typedef BorrowedReference BorrowedGreenlet; + + template + class _BorrowedGreenlet : public BorrowedReference + { + public: + _BorrowedGreenlet() : + BorrowedReference(nullptr) + {} + + _BorrowedGreenlet(T* it) : + BorrowedReference(it) + {} + + _BorrowedGreenlet(const BorrowedObject& it); + + _BorrowedGreenlet(const OwnedGreenlet& it) : + BorrowedReference(it.borrow()) + {} + + _BorrowedGreenlet& operator=(const BorrowedObject& other); + + // We get one of these for PyGreenlet, but one for PyObject + // is handy as well + operator PyObject*() const + { + return reinterpret_cast(this->p); + } + inline Greenlet* operator->() const noexcept; + inline operator Greenlet*() const noexcept; + }; + + typedef _BorrowedGreenlet BorrowedGreenlet; + + template + _OwnedGreenlet::_OwnedGreenlet(const BorrowedGreenlet& other) + : OwnedReference(reinterpret_cast(other.borrow())) + { + Py_XINCREF(this->p); + } + + + class BorrowedMainGreenlet + : public _BorrowedGreenlet + { + public: + BorrowedMainGreenlet(const OwnedMainGreenlet& it) : + _BorrowedGreenlet(it.borrow()) + {} + BorrowedMainGreenlet(PyGreenlet* it=nullptr) + : _BorrowedGreenlet(it) + {} + }; + + template + _OwnedGreenlet& _OwnedGreenlet::operator=(const BorrowedGreenlet& other) + { + return this->operator=(other.borrow()); + } + + + class ImmortalObject : public PyObjectPointer<> + { + private: + G_NO_ASSIGNMENT_OF_CLS(ImmortalObject); + public: + explicit ImmortalObject(PyObject* it) : PyObjectPointer<>(it) + { + } + + ImmortalObject(const ImmortalObject& other) + : PyObjectPointer<>(other.p) + { + + } + + /** + * Become the new owner of the object. Does not change the + * reference count. + */ + ImmortalObject& operator=(PyObject* it) + { + assert(this->p == nullptr); + this->p = it; + return *this; + } + + static ImmortalObject consuming(PyObject* it) + { + return ImmortalObject(it); + } + + inline operator PyObject*() const + { + return this->p; + } + }; + + class ImmortalString : public ImmortalObject + { + private: + G_NO_COPIES_OF_CLS(ImmortalString); + const char* str; + public: + ImmortalString(const char* const str) : + ImmortalObject(str ? Require(PyUnicode_InternFromString(str)) : nullptr) + { + this->str = str; + } + + inline ImmortalString& operator=(const char* const str) + { + if (!this->p) { + this->p = Require(PyUnicode_InternFromString(str)); + this->str = str; + } + else { + assert(this->str == str); + } + return *this; + } + + inline operator std::string() const + { + return this->str; + } + + }; + + class ImmortalEventName : public ImmortalString + { + private: + G_NO_COPIES_OF_CLS(ImmortalEventName); + public: + ImmortalEventName(const char* const str) : ImmortalString(str) + {} + }; + + class ImmortalException : public ImmortalObject + { + private: + G_NO_COPIES_OF_CLS(ImmortalException); + public: + ImmortalException(const char* const name, PyObject* base=nullptr) : + ImmortalObject(name + // Python 2.7 isn't const correct + ? Require(PyErr_NewException((char*)name, base, nullptr)) + : nullptr) + {} + + inline bool PyExceptionMatches() const + { + return PyErr_ExceptionMatches(this->p) > 0; + } + + }; + + template + inline OwnedObject PyObjectPointer::PyStr() const noexcept + { + if (!this->p) { + return OwnedObject(); + } + return OwnedObject::consuming(PyObject_Str(reinterpret_cast(this->p))); + } + + template + inline const std::string PyObjectPointer::as_str() const noexcept + { + // NOTE: This is not Python exception safe. + if (this->p) { + // The Python APIs return a cached char* value that's only valid + // as long as the original object stays around, and we're + // about to (probably) toss it. Hence the copy to std::string. + OwnedObject py_str = this->PyStr(); + if (!py_str) { + return "(nil)"; + } + return PyUnicode_AsUTF8(py_str.borrow()); + } + return "(nil)"; + } + + template + inline OwnedObject PyObjectPointer::PyGetAttr(const ImmortalObject& name) const noexcept + { + assert(this->p); + return OwnedObject::consuming(PyObject_GetAttr(reinterpret_cast(this->p), name)); + } + + template + inline OwnedObject PyObjectPointer::PyRequireAttr(const char* const name) const + { + assert(this->p); + return OwnedObject::consuming(Require(PyObject_GetAttrString(this->p, name), name)); + } + + template + inline OwnedObject PyObjectPointer::PyRequireAttr(const ImmortalString& name) const + { + assert(this->p); + return OwnedObject::consuming(Require( + PyObject_GetAttr( + reinterpret_cast(this->p), + name + ), + name + )); + } + + template + inline OwnedObject PyObjectPointer::PyCall(const BorrowedObject& arg) const + { + return this->PyCall(arg.borrow()); + } + + template + inline OwnedObject PyObjectPointer::PyCall(PyGreenlet* arg) const + { + return this->PyCall(reinterpret_cast(arg)); + } + + template + inline OwnedObject PyObjectPointer::PyCall(PyObject* arg) const + { + assert(this->p); + return OwnedObject::consuming(PyObject_CallFunctionObjArgs(this->p, arg, NULL)); + } + + template + inline OwnedObject PyObjectPointer::PyCall(const BorrowedObject args, + const BorrowedObject kwargs) const + { + assert(this->p); + return OwnedObject::consuming(PyObject_Call(this->p, args, kwargs)); + } + + template + inline OwnedObject PyObjectPointer::PyCall(const OwnedObject& args, + const OwnedObject& kwargs) const + { + assert(this->p); + return OwnedObject::consuming(PyObject_Call(this->p, args.borrow(), kwargs.borrow())); + } + + G_FP_TMPL_STATIC inline void + ListChecker(void * p) + { + if (!p) { + return; + } + if (!PyList_Check(p)) { + throw TypeError("Expected a list"); + } + } + + class OwnedList : public OwnedReference + { + private: + G_NO_ASSIGNMENT_OF_CLS(OwnedList); + public: + // TODO: Would like to use move. + explicit OwnedList(const OwnedObject& other) + : OwnedReference(other) + { + } + + OwnedList& operator=(const OwnedObject& other) + { + if (other && PyList_Check(other.p)) { + // Valid list. Own a new reference to it, discard the + // reference to what we did own. + PyObject* new_ptr = other.p; + Py_INCREF(new_ptr); + Py_XDECREF(this->p); + this->p = new_ptr; + } + else { + // Either the other object was NULL (an error) or it + // wasn't a list. Either way, we're now invalidated. + Py_XDECREF(this->p); + this->p = nullptr; + } + return *this; + } + + inline bool empty() const + { + return PyList_GET_SIZE(p) == 0; + } + + inline Py_ssize_t size() const + { + return PyList_GET_SIZE(p); + } + + inline BorrowedObject at(const Py_ssize_t index) const + { + return PyList_GET_ITEM(p, index); + } + + inline void clear() + { + PyList_SetSlice(p, 0, PyList_GET_SIZE(p), NULL); + } + }; + + // Use this to represent the module object used at module init + // time. + // This could either be a borrowed (Py2) or new (Py3) reference; + // either way, we don't want to do any memory management + // on it here, Python itself will handle that. + // XXX: Actually, that's not quite right. On Python 3, if an + // exception occurs before we return to the interpreter, this will + // leak; but all previous versions also had that problem. + class CreatedModule : public PyObjectPointer<> + { + private: + G_NO_COPIES_OF_CLS(CreatedModule); + public: + CreatedModule(PyModuleDef& mod_def) : PyObjectPointer<>( + Require(PyModule_Create(&mod_def))) + { + } + + // PyAddObject(): Add a reference to the object to the module. + // On return, the reference count of the object is unchanged. + // + // The docs warn that PyModule_AddObject only steals the + // reference on success, so if it fails after we've incref'd + // or allocated, we're responsible for the decref. + void PyAddObject(const char* name, const long new_bool) + { + OwnedObject p = OwnedObject::consuming(Require(PyBool_FromLong(new_bool))); + this->PyAddObject(name, p); + } + + void PyAddObject(const char* name, const OwnedObject& new_object) + { + // The caller already owns a reference they will decref + // when their variable goes out of scope, we still need to + // incref/decref. + this->PyAddObject(name, new_object.borrow()); + } + + void PyAddObject(const char* name, const ImmortalObject& new_object) + { + this->PyAddObject(name, new_object.borrow()); + } + + void PyAddObject(const char* name, PyTypeObject& type) + { + this->PyAddObject(name, reinterpret_cast(&type)); + } + + void PyAddObject(const char* name, PyObject* new_object) + { + Py_INCREF(new_object); + try { + Require(PyModule_AddObject(this->p, name, new_object)); + } + catch (const PyErrOccurred&) { + Py_DECREF(p); + throw; + } + } + }; + + class PyErrFetchParam : public PyObjectPointer<> + { + // Not an owned object, because we can't be initialized with + // one, and we only sometimes acquire ownership. + private: + G_NO_COPIES_OF_CLS(PyErrFetchParam); + public: + // To allow declaring these and passing them to + // PyErr_Fetch we implement the empty constructor, + // and the address operator. + PyErrFetchParam() : PyObjectPointer<>(nullptr) + { + } + + PyObject** operator&() + { + return &this->p; + } + + // This allows us to pass one directly without the &, + // BUT it has higher precedence than the bool operator + // if it's not explicit. + operator PyObject**() + { + return &this->p; + } + + // We don't want to be able to pass these to Py_DECREF and + // such so we don't have the implicit PyObject* conversion. + + inline PyObject* relinquish_ownership() + { + PyObject* result = this->p; + this->p = nullptr; + return result; + } + + ~PyErrFetchParam() + { + Py_XDECREF(p); + } + }; + + class OwnedErrPiece : public OwnedObject + { + private: + + public: + // Unlike OwnedObject, this increments the refcount. + OwnedErrPiece(PyObject* p=nullptr) : OwnedObject(p) + { + this->acquire(); + } + + PyObject** operator&() + { + return &this->p; + } + + inline operator PyObject*() const + { + return this->p; + } + + operator PyTypeObject*() const + { + return reinterpret_cast(this->p); + } + }; + + class PyErrPieces + { + private: + OwnedErrPiece type; + OwnedErrPiece instance; + OwnedErrPiece traceback; + bool restored; + public: + // Takes new references; if we're destroyed before + // restoring the error, we drop the references. + PyErrPieces(PyObject* t, PyObject* v, PyObject* tb) : + type(t), + instance(v), + traceback(tb), + restored(0) + { + this->normalize(); + } + + PyErrPieces() : + restored(0) + { + // PyErr_Fetch transfers ownership to us, so + // we don't actually need to INCREF; but we *do* + // need to DECREF if we're not restored. + PyErrFetchParam t, v, tb; + PyErr_Fetch(&t, &v, &tb); + type.steal(t.relinquish_ownership()); + instance.steal(v.relinquish_ownership()); + traceback.steal(tb.relinquish_ownership()); + } + + void PyErrRestore() + { + // can only do this once + assert(!this->restored); + this->restored = true; + PyErr_Restore( + this->type.relinquish_ownership(), + this->instance.relinquish_ownership(), + this->traceback.relinquish_ownership()); + assert(!this->type && !this->instance && !this->traceback); + } + + private: + void normalize() + { + // First, check the traceback argument, replacing None, + // with NULL + if (traceback.is_None()) { + traceback = nullptr; + } + + if (traceback && !PyTraceBack_Check(traceback.borrow())) { + throw PyErrOccurred(PyExc_TypeError, + "throw() third argument must be a traceback object"); + } + + if (PyExceptionClass_Check(type)) { + // If we just had a type, we'll now have a type and + // instance. + // The type's refcount will have gone up by one + // because of the instance and the instance will have + // a refcount of one. Either way, we owned, and still + // do own, exactly one reference. + PyErr_NormalizeException(&type, &instance, &traceback); + + } + else if (PyExceptionInstance_Check(type)) { + /* Raising an instance --- usually that means an + object that is a subclass of BaseException, but on + Python 2, that can also mean an arbitrary old-style + object. The value should be a dummy. */ + if (instance && !instance.is_None()) { + throw PyErrOccurred( + PyExc_TypeError, + "instance exception may not have a separate value"); + } + /* Normalize to raise , */ + this->instance = this->type; + this->type = PyExceptionInstance_Class(instance.borrow()); + + /* + It would be tempting to do this: + + Py_ssize_t type_count = Py_REFCNT(Py_TYPE(instance.borrow())); + this->type = PyExceptionInstance_Class(instance.borrow()); + assert(this->type.REFCNT() == type_count + 1); + + But that doesn't work on Python 2 in the case of + old-style instances: The result of Py_TYPE is going to + be the global shared that all + old-style classes have, while the return of Instance_Class() + will be the Python-level class object. The two are unrelated. + */ + } + else { + /* Not something you can raise. throw() fails. */ + PyErr_Format(PyExc_TypeError, + "exceptions must be classes, or instances, not %s", + Py_TYPE(type.borrow())->tp_name); + throw PyErrOccurred(); + } + } + }; + + // PyArg_Parse's O argument returns a borrowed reference. + class PyArgParseParam : public BorrowedObject + { + private: + G_NO_COPIES_OF_CLS(PyArgParseParam); + public: + explicit PyArgParseParam(PyObject* p=nullptr) : BorrowedObject(p) + { + } + + inline PyObject** operator&() + { + return &this->p; + } + }; + +};}; + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_slp_switch.hpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_slp_switch.hpp new file mode 100644 index 0000000..bd4b7ae --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_slp_switch.hpp @@ -0,0 +1,99 @@ +#ifndef GREENLET_SLP_SWITCH_HPP +#define GREENLET_SLP_SWITCH_HPP + +#include "greenlet_compiler_compat.hpp" +#include "greenlet_refs.hpp" + +/* + * the following macros are spliced into the OS/compiler + * specific code, in order to simplify maintenance. + */ +// We can save about 10% of the time it takes to switch greenlets if +// we thread the thread state through the slp_save_state() and the +// following slp_restore_state() calls from +// slp_switch()->g_switchstack() (which already needs to access it). +// +// However: +// +// that requires changing the prototypes and implementations of the +// switching functions. If we just change the prototype of +// slp_switch() to accept the argument and update the macros, without +// changing the implementation of slp_switch(), we get crashes on +// 64-bit Linux and 32-bit x86 (for reasons that aren't 100% clear); +// on the other hand, 64-bit macOS seems to be fine. Also, 64-bit +// windows is an issue because slp_switch is written fully in assembly +// and currently ignores its argument so some code would have to be +// adjusted there to pass the argument on to the +// ``slp_save_state_asm()`` function (but interestingly, because of +// the calling convention, the extra argument is just ignored and +// things function fine, albeit slower, if we just modify +// ``slp_save_state_asm`()` to fetch the pointer to pass to the +// macro.) +// +// Our compromise is to use a *glabal*, untracked, weak, pointer +// to the necessary thread state during the process of switching only. +// This is safe because we're protected by the GIL, and if we're +// running this code, the thread isn't exiting. This also nets us a +// 10-12% speed improvement. + +static greenlet::Greenlet* volatile switching_thread_state = nullptr; + + +extern "C" { +static int GREENLET_NOINLINE(slp_save_state_trampoline)(char* stackref); +static void GREENLET_NOINLINE(slp_restore_state_trampoline)(); +} + + +#define SLP_SAVE_STATE(stackref, stsizediff) \ +do { \ + assert(switching_thread_state); \ + stackref += STACK_MAGIC; \ + if (slp_save_state_trampoline((char*)stackref)) \ + return -1; \ + if (!switching_thread_state->active()) \ + return 1; \ + stsizediff = switching_thread_state->stack_start() - (char*)stackref; \ +} while (0) + +#define SLP_RESTORE_STATE() slp_restore_state_trampoline() + +#define SLP_EVAL +extern "C" { +#define slp_switch GREENLET_NOINLINE(slp_switch) +#include "slp_platformselect.h" +} +#undef slp_switch + +#ifndef STACK_MAGIC +# error \ + "greenlet needs to be ported to this platform, or taught how to detect your compiler properly." +#endif /* !STACK_MAGIC */ + + + +#ifdef EXTERNAL_ASM +/* CCP addition: Make these functions, to be called from assembler. + * The token include file for the given platform should enable the + * EXTERNAL_ASM define so that this is included. + */ +extern "C" { +intptr_t +slp_save_state_asm(intptr_t* ref) +{ + intptr_t diff; + SLP_SAVE_STATE(ref, diff); + return diff; +} + +void +slp_restore_state_asm(void) +{ + SLP_RESTORE_STATE(); +} + +extern int slp_switch(void); +}; +#endif + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_thread_state.hpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_thread_state.hpp new file mode 100644 index 0000000..045371f --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_thread_state.hpp @@ -0,0 +1,543 @@ +#ifndef GREENLET_THREAD_STATE_HPP +#define GREENLET_THREAD_STATE_HPP + +#include +#include + +#include "greenlet_internal.hpp" +#include "greenlet_refs.hpp" +#include "greenlet_thread_support.hpp" + +using greenlet::refs::BorrowedObject; +using greenlet::refs::BorrowedGreenlet; +using greenlet::refs::BorrowedMainGreenlet; +using greenlet::refs::OwnedMainGreenlet; +using greenlet::refs::OwnedObject; +using greenlet::refs::OwnedGreenlet; +using greenlet::refs::OwnedList; +using greenlet::refs::PyErrFetchParam; +using greenlet::refs::PyArgParseParam; +using greenlet::refs::ImmortalString; +using greenlet::refs::CreatedModule; +using greenlet::refs::PyErrPieces; +using greenlet::refs::NewReference; + +namespace greenlet { +/** + * Thread-local state of greenlets. + * + * Each native thread will get exactly one of these objects, + * automatically accessed through the best available thread-local + * mechanism the compiler supports (``thread_local`` for C++11 + * compilers or ``__thread``/``declspec(thread)`` for older GCC/clang + * or MSVC, respectively.) + * + * Previously, we kept thread-local state mostly in a bunch of + * ``static volatile`` variables in the main greenlet file.. This had + * the problem of requiring extra checks, loops, and great care + * accessing these variables if we potentially invoked any Python code + * that could release the GIL, because the state could change out from + * under us. Making the variables thread-local solves this problem. + * + * When we detected that a greenlet API accessing the current greenlet + * was invoked from a different thread than the greenlet belonged to, + * we stored a reference to the greenlet in the Python thread + * dictionary for the thread the greenlet belonged to. This could lead + * to memory leaks if the thread then exited (because of a reference + * cycle, as greenlets referred to the thread dictionary, and deleting + * non-current greenlets leaked their frame plus perhaps arguments on + * the C stack). If a thread exited while still having running + * greenlet objects (perhaps that had just switched back to the main + * greenlet), and did not invoke one of the greenlet APIs *in that + * thread, immediately before it exited, without some other thread + * then being invoked*, such a leak was guaranteed. + * + * This can be partly solved by using compiler thread-local variables + * instead of the Python thread dictionary, thus avoiding a cycle. + * + * To fully solve this problem, we need a reliable way to know that a + * thread is done and we should clean up the main greenlet. On POSIX, + * we can use the destructor function of ``pthread_key_create``, but + * there's nothing similar on Windows; a C++11 thread local object + * reliably invokes its destructor when the thread it belongs to exits + * (non-C++11 compilers offer ``__thread`` or ``declspec(thread)`` to + * create thread-local variables, but they can't hold C++ objects that + * invoke destructors; the C++11 version is the most portable solution + * I found). When the thread exits, we can drop references and + * otherwise manipulate greenlets and frames that we know can no + * longer be switched to. For compilers that don't support C++11 + * thread locals, we have a solution that uses the python thread + * dictionary, though it may not collect everything as promptly as + * other compilers do, if some other library is using the thread + * dictionary and has a cycle or extra reference. + * + * There are two small wrinkles. The first is that when the thread + * exits, it is too late to actually invoke Python APIs: the Python + * thread state is gone, and the GIL is released. To solve *this* + * problem, our destructor uses ``Py_AddPendingCall`` to transfer the + * destruction work to the main thread. (This is not an issue for the + * dictionary solution.) + * + * The second is that once the thread exits, the thread local object + * is invalid and we can't even access a pointer to it, so we can't + * pass it to ``Py_AddPendingCall``. This is handled by actually using + * a second object that's thread local (ThreadStateCreator) and having + * it dynamically allocate this object so it can live until the + * pending call runs. + */ + + + +class ThreadState { +private: + // As of commit 08ad1dd7012b101db953f492e0021fb08634afad + // this class needed 56 bytes in o Py_DEBUG build + // on 64-bit macOS 11. + // Adding the vector takes us up to 80 bytes () + + /* Strong reference to the main greenlet */ + OwnedMainGreenlet main_greenlet; + + /* Strong reference to the current greenlet. */ + OwnedGreenlet current_greenlet; + + /* Strong reference to the trace function, if any. */ + OwnedObject tracefunc; + + typedef std::vector > deleteme_t; + /* A vector of raw PyGreenlet pointers representing things that need + deleted when this thread is running. The vector owns the + references, but you need to manually INCREF/DECREF as you use + them. We don't use a vector because we + make copy of this vector, and that would become O(n) as all the + refcounts are incremented in the copy. + */ + deleteme_t deleteme; + +#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED + void* exception_state; +#endif + + static std::clock_t _clocks_used_doing_gc; + static ImmortalString get_referrers_name; + static PythonAllocator allocator; + + G_NO_COPIES_OF_CLS(ThreadState); + +public: + static void* operator new(size_t UNUSED(count)) + { + return ThreadState::allocator.allocate(1); + } + + static void operator delete(void* ptr) + { + return ThreadState::allocator.deallocate(static_cast(ptr), + 1); + } + + static void init() + { + ThreadState::get_referrers_name = "get_referrers"; + ThreadState::_clocks_used_doing_gc = 0; + } + + ThreadState() + : main_greenlet(OwnedMainGreenlet::consuming(green_create_main(this))), + current_greenlet(main_greenlet) + { + if (!this->main_greenlet) { + // We failed to create the main greenlet. That's bad. + throw PyFatalError("Failed to create main greenlet"); + } + // The main greenlet starts with 1 refs: The returned one. We + // then copied it to the current greenlet. + assert(this->main_greenlet.REFCNT() == 2); + +#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED + this->exception_state = slp_get_exception_state(); +#endif + } + + inline void restore_exception_state() + { +#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED + // It's probably important this be inlined and only call C + // functions to avoid adding an SEH frame. + slp_set_exception_state(this->exception_state); +#endif + } + + inline bool has_main_greenlet() + { + return !!this->main_greenlet; + } + + // Called from the ThreadStateCreator when we're in non-standard + // threading mode. In that case, there is an object in the Python + // thread state dictionary that points to us. The main greenlet + // also traverses into us, in which case it's crucial not to + // traverse back into the main greenlet. + int tp_traverse(visitproc visit, void* arg, bool traverse_main=true) + { + if (traverse_main) { + Py_VISIT(main_greenlet.borrow_o()); + } + if (traverse_main || current_greenlet != main_greenlet) { + Py_VISIT(current_greenlet.borrow_o()); + } + Py_VISIT(tracefunc.borrow()); + return 0; + } + + inline BorrowedMainGreenlet borrow_main_greenlet() const + { + assert(this->main_greenlet); + assert(this->main_greenlet.REFCNT() >= 2); + return this->main_greenlet; + }; + + inline OwnedMainGreenlet get_main_greenlet() + { + return this->main_greenlet; + } + + /** + * In addition to returning a new reference to the currunt + * greenlet, this performs any maintenance needed. + */ + inline OwnedGreenlet get_current() + { + /* green_dealloc() cannot delete greenlets from other threads, so + it stores them in the thread dict; delete them now. */ + this->clear_deleteme_list(); + //assert(this->current_greenlet->main_greenlet == this->main_greenlet); + //assert(this->main_greenlet->main_greenlet == this->main_greenlet); + return this->current_greenlet; + } + + /** + * As for non-const get_current(); + */ + inline BorrowedGreenlet borrow_current() + { + this->clear_deleteme_list(); + return this->current_greenlet; + } + + /** + * Does no maintenance. + */ + inline OwnedGreenlet get_current() const + { + return this->current_greenlet; + } + + template + inline bool is_current(const refs::PyObjectPointer& obj) const + { + return this->current_greenlet.borrow_o() == obj.borrow_o(); + } + + inline void set_current(const OwnedGreenlet& target) + { + this->current_greenlet = target; + } + +private: + /** + * Deref and remove the greenlets from the deleteme list. Must be + * holding the GIL. + * + * If *murder* is true, then we must be called from a different + * thread than the one that these greenlets were running in. + * In that case, if the greenlet was actually running, we destroy + * the frame reference and otherwise make it appear dead before + * proceeding; otherwise, we would try (and fail) to raise an + * exception in it and wind up right back in this list. + */ + inline void clear_deleteme_list(const bool murder=false) + { + if (!this->deleteme.empty()) { + // It's possible we could add items to this list while + // running Python code if there's a thread switch, so we + // need to defensively copy it before that can happen. + deleteme_t copy = this->deleteme; + this->deleteme.clear(); // in case things come back on the list + for(deleteme_t::iterator it = copy.begin(), end = copy.end(); + it != end; + ++it ) { + PyGreenlet* to_del = *it; + if (murder) { + // Force each greenlet to appear dead; we can't raise an + // exception into it anymore anyway. + to_del->pimpl->murder_in_place(); + } + + // The only reference to these greenlets should be in + // this list, decreffing them should let them be + // deleted again, triggering calls to green_dealloc() + // in the correct thread (if we're not murdering). + // This may run arbitrary Python code and switch + // threads or greenlets! + Py_DECREF(to_del); + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(nullptr); + PyErr_Clear(); + } + } + } + } + +public: + + /** + * Returns a new reference, or a false object. + */ + inline OwnedObject get_tracefunc() const + { + return tracefunc; + }; + + + inline void set_tracefunc(BorrowedObject tracefunc) + { + assert(tracefunc); + if (tracefunc == BorrowedObject(Py_None)) { + this->tracefunc.CLEAR(); + } + else { + this->tracefunc = tracefunc; + } + } + + /** + * Given a reference to a greenlet that some other thread + * attempted to delete (has a refcount of 0) store it for later + * deletion when the thread this state belongs to is current. + */ + inline void delete_when_thread_running(PyGreenlet* to_del) + { + Py_INCREF(to_del); + this->deleteme.push_back(to_del); + } + + /** + * Set to std::clock_t(-1) to disable. + */ + inline static std::clock_t& clocks_used_doing_gc() + { + return ThreadState::_clocks_used_doing_gc; + } + + ~ThreadState() + { + if (!PyInterpreterState_Head()) { + // We shouldn't get here (our callers protect us) + // but if we do, all we can do is bail early. + return; + } + + // We should not have an "origin" greenlet; that only exists + // for the temporary time during a switch, which should not + // be in progress as the thread dies. + //assert(!this->switching_state.origin); + + this->tracefunc.CLEAR(); + + // Forcibly GC as much as we can. + this->clear_deleteme_list(true); + + // The pending call did this. + assert(this->main_greenlet->thread_state() == nullptr); + + // If the main greenlet is the current greenlet, + // then we "fell off the end" and the thread died. + // It's possible that there is some other greenlet that + // switched to us, leaving a reference to the main greenlet + // on the stack, somewhere uncollectible. Try to detect that. + if (this->current_greenlet == this->main_greenlet && this->current_greenlet) { + assert(this->current_greenlet->is_currently_running_in_some_thread()); + // Drop one reference we hold. + this->current_greenlet.CLEAR(); + assert(!this->current_greenlet); + // Only our reference to the main greenlet should be left, + // But hold onto the pointer in case we need to do extra cleanup. + PyGreenlet* old_main_greenlet = this->main_greenlet.borrow(); + Py_ssize_t cnt = this->main_greenlet.REFCNT(); + this->main_greenlet.CLEAR(); + if (ThreadState::_clocks_used_doing_gc != std::clock_t(-1) + && cnt == 2 && Py_REFCNT(old_main_greenlet) == 1) { + // Highly likely that the reference is somewhere on + // the stack, not reachable by GC. Verify. + // XXX: This is O(n) in the total number of objects. + // TODO: Add a way to disable this at runtime, and + // another way to report on it. + std::clock_t begin = std::clock(); + NewReference gc(PyImport_ImportModule("gc")); + if (gc) { + OwnedObject get_referrers = gc.PyRequireAttr(ThreadState::get_referrers_name); + OwnedList refs(get_referrers.PyCall(old_main_greenlet)); + if (refs && refs.empty()) { + assert(refs.REFCNT() == 1); + // We found nothing! So we left a dangling + // reference: Probably the last thing some + // other greenlet did was call + // 'getcurrent().parent.switch()' to switch + // back to us. Clean it up. This will be the + // case on CPython 3.7 and newer, as they use + // an internal calling conversion that avoids + // creating method objects and storing them on + // the stack. + Py_DECREF(old_main_greenlet); + } + else if (refs + && refs.size() == 1 + && PyCFunction_Check(refs.at(0)) + && Py_REFCNT(refs.at(0)) == 2) { + assert(refs.REFCNT() == 1); + // Ok, we found a C method that refers to the + // main greenlet, and its only referenced + // twice, once in the list we just created, + // once from...somewhere else. If we can't + // find where else, then this is a leak. + // This happens in older versions of CPython + // that create a bound method object somewhere + // on the stack that we'll never get back to. + if (PyCFunction_GetFunction(refs.at(0).borrow()) == (PyCFunction)green_switch) { + BorrowedObject function_w = refs.at(0); + refs.clear(); // destroy the reference + // from the list. + // back to one reference. Can *it* be + // found? + assert(function_w.REFCNT() == 1); + refs = get_referrers.PyCall(function_w); + if (refs && refs.empty()) { + // Nope, it can't be found so it won't + // ever be GC'd. Drop it. + Py_CLEAR(function_w); + } + } + } + std::clock_t end = std::clock(); + ThreadState::_clocks_used_doing_gc += (end - begin); + } + } + } + + // We need to make sure this greenlet appears to be dead, + // because otherwise deallocing it would fail to raise an + // exception in it (the thread is dead) and put it back in our + // deleteme list. + if (this->current_greenlet) { + this->current_greenlet->murder_in_place(); + this->current_greenlet.CLEAR(); + } + + if (this->main_greenlet) { + // Couldn't have been the main greenlet that was running + // when the thread exited (because we already cleared this + // pointer if it was). This shouldn't be possible? + + // If the main greenlet was current when the thread died (it + // should be, right?) then we cleared its self pointer above + // when we cleared the current greenlet's main greenlet pointer. + // assert(this->main_greenlet->main_greenlet == this->main_greenlet + // || !this->main_greenlet->main_greenlet); + // // self reference, probably gone + // this->main_greenlet->main_greenlet.CLEAR(); + + // This will actually go away when the ivar is destructed. + this->main_greenlet.CLEAR(); + } + + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(NULL); + PyErr_Clear(); + } + + } + +}; + +ImmortalString ThreadState::get_referrers_name(nullptr); +PythonAllocator ThreadState::allocator; +std::clock_t ThreadState::_clocks_used_doing_gc(0); + +template +class ThreadStateCreator +{ +private: + // Initialized to 1, and, if still 1, created on access. + // Set to 0 on destruction. + ThreadState* _state; + G_NO_COPIES_OF_CLS(ThreadStateCreator); +public: + + // Only one of these, auto created per thread + ThreadStateCreator() : + _state((ThreadState*)1) + { + } + + ~ThreadStateCreator() + { + ThreadState* tmp = this->_state; + this->_state = nullptr; + if (tmp && tmp != (ThreadState*)1) { + Destructor x(tmp); + } + } + + inline ThreadState& state() + { + // The main greenlet will own this pointer when it is created, + // which will be right after this. The plan is to give every + // greenlet a pointer to the main greenlet for the thread it + // runs in; if we are doing something cross-thread, we need to + // access the pointer from the main greenlet. Deleting the + // thread, and hence the thread-local storage, will delete the + // state pointer in the main greenlet. + if (this->_state == (ThreadState*)1) { + // XXX: Assuming allocation never fails + this->_state = new ThreadState; + // For non-standard threading, we need to store an object + // in the Python thread state dictionary so that it can be + // DECREF'd when the thread ends (ideally; the dict could + // last longer) and clean this object up. + } + if (!this->_state) { + throw std::runtime_error("Accessing state after destruction."); + } + return *this->_state; + } + + operator ThreadState&() + { + return this->state(); + } + + operator ThreadState*() + { + return &this->state(); + } + + inline int tp_traverse(visitproc visit, void* arg) + { + if (this->_state) { + return this->_state->tp_traverse(visit, arg); + } + return 0; + } + +}; + + +// We can't use the PythonAllocator for this, because we push to it +// from the thread state destructor, which doesn't have the GIL, +// and Python's allocators can only be called with the GIL. +typedef std::vector cleanup_queue_t; + +}; // namespace greenlet + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_thread_state_dict_cleanup.hpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_thread_state_dict_cleanup.hpp new file mode 100644 index 0000000..acf39c8 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_thread_state_dict_cleanup.hpp @@ -0,0 +1,118 @@ +#ifndef GREENLET_THREAD_STATE_DICT_CLEANUP_HPP +#define GREENLET_THREAD_STATE_DICT_CLEANUP_HPP + +#include "greenlet_internal.hpp" +#include "greenlet_thread_state.hpp" + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wmissing-field-initializers" +#endif + +#ifndef G_THREAD_STATE_DICT_CLEANUP_TYPE +// shut the compiler up if it looks at this file in isolation +#define ThreadStateCreator int +#endif + +// Define a Python object that goes in the Python thread state dict +// when the greenlet thread state is created, and which owns the +// reference to the greenlet thread local state. +// When the thread state dict is cleaned up, so too is the thread +// state. This works best if we make sure there are no circular +// references to the thread state. +typedef struct _PyGreenletCleanup { + PyObject_HEAD + ThreadStateCreator* thread_state_creator; +} PyGreenletCleanup; + +static void +cleanup_do_dealloc(PyGreenletCleanup* self) +{ + ThreadStateCreator* tmp = self->thread_state_creator; + self->thread_state_creator = nullptr; + if (tmp) { + delete tmp; + } +} + +static void +cleanup_dealloc(PyGreenletCleanup* self) +{ + PyObject_GC_UnTrack(self); + cleanup_do_dealloc(self); +} + +static int +cleanup_clear(PyGreenletCleanup* self) +{ + // This method is never called by our test cases. + cleanup_do_dealloc(self); + return 0; +} + +static int +cleanup_traverse(PyGreenletCleanup* self, visitproc visit, void* arg) +{ + if (self->thread_state_creator) { + return self->thread_state_creator->tp_traverse(visit, arg); + } + return 0; +} + +static int +cleanup_is_gc(PyGreenlet* UNUSED(self)) +{ + return 1; +} + +static PyTypeObject PyGreenletCleanup_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "greenlet._greenlet.ThreadStateCleanup", + sizeof(struct _PyGreenletCleanup), + 0, /* tp_itemsize */ + /* methods */ + (destructor)cleanup_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as _number*/ + 0, /* tp_as _sequence*/ + 0, /* tp_as _mapping*/ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer*/ + G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Internal use only", /* tp_doc */ + (traverseproc)cleanup_traverse, /* tp_traverse */ + (inquiry)cleanup_clear, /* tp_clear */ + 0, /* tp_richcompare */ + // XXX: Don't our flags promise a weakref? + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + PyType_GenericAlloc, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + PyObject_GC_Del, /* tp_free */ + (inquiry)cleanup_is_gc, /* tp_is_gc */ +}; + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_thread_support.hpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_thread_support.hpp new file mode 100644 index 0000000..3ded7d2 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/greenlet_thread_support.hpp @@ -0,0 +1,31 @@ +#ifndef GREENLET_THREAD_SUPPORT_HPP +#define GREENLET_THREAD_SUPPORT_HPP + +/** + * Defines various utility functions to help greenlet integrate well + * with threads. This used to be needed when we supported Python + * 2.7 on Windows, which used a very old compiler. We wrote an + * alternative implementation using Python APIs and POSIX or Windows + * APIs, but that's no longer needed. So this file is a shadow of its + * former self --- but may be needed in the future. + */ + +#include +#include +#include + +#include "greenlet_compiler_compat.hpp" + +namespace greenlet { + typedef std::mutex Mutex; + typedef std::lock_guard LockGuard; + class LockInitError : public std::runtime_error + { + public: + LockInitError(const char* what) : std::runtime_error(what) + {}; + }; +}; + + +#endif /* GREENLET_THREAD_SUPPORT_HPP */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/setup_switch_x64_masm.cmd b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/setup_switch_x64_masm.cmd new file mode 100644 index 0000000..0928595 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/setup_switch_x64_masm.cmd @@ -0,0 +1,2 @@ +call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" amd64 +ml64 /nologo /c /Fo switch_x64_masm.obj switch_x64_masm.asm diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_aarch64_gcc.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_aarch64_gcc.h new file mode 100644 index 0000000..058617c --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_aarch64_gcc.h @@ -0,0 +1,124 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 07-Sep-16 Add clang support using x register naming. Fredrik Fornwall + * 13-Apr-13 Add support for strange GCC caller-save decisions + * 08-Apr-13 File creation. Michael Matz + * + * NOTES + * + * Simply save all callee saved registers + * + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL +#define STACK_MAGIC 0 +#define REGS_TO_SAVE "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", \ + "x27", "x28", "x30" /* aka lr */, \ + "v8", "v9", "v10", "v11", \ + "v12", "v13", "v14", "v15" + +/* + * Recall: + asm asm-qualifiers ( AssemblerTemplate + : OutputOperands + [ : InputOperands + [ : Clobbers ] ]) + + or (if asm-qualifiers contains 'goto') + + asm asm-qualifiers ( AssemblerTemplate + : OutputOperands + : InputOperands + : Clobbers + : GotoLabels) + + and OutputOperands are + + [ [asmSymbolicName] ] constraint (cvariablename) + + When a name is given, refer to it as ``%[the name]``. + When not given, ``%i`` where ``i`` is the zero-based index. + + constraints starting with ``=`` means only writing; ``+`` means + reading and writing. + + This is followed by ``r`` (must be register) or ``m`` (must be memory) + and these can be combined. + + The ``cvariablename`` is actually an lvalue expression. + + In AArch65, 31 general purpose registers. If named X0... they are + 64-bit. If named W0... they are the bottom 32 bits of the + corresponding 64 bit register. + + XZR and WZR are hardcoded to 0, and ignore writes. + + Arguments are in X0..X7. C++ uses X0 for ``this``. X0 holds simple return + values (?) + + Whenever a W register is written, the top half of the X register is zeroed. + */ + +static int +slp_switch(void) +{ + int err; + void *fp; + /* Windowz uses a 32-bit long on a 64-bit platform, unlike the rest of + the world, and in theory we can be compiled with GCC/llvm on 64-bit + windows. So we need a fixed-width type. + */ + int64_t *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("str x29, %0" : "=m"(fp) : : ); + __asm__ ("mov %0, sp" : "=r" (stackref)); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "add sp,sp,%0\n" + "add x29,x29,%0\n" + : + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + /* SLP_SAVE_STATE macro contains some return statements + (of -1 and 1). It falls through only when + the return value of slp_save_state() is zero, which + is placed in x0. + In that case we (slp_switch) also want to return zero + (also in x0 of course). + Now, some GCC versions (seen with 4.8) think it's a + good idea to save/restore x0 around the call to + slp_restore_state(), instead of simply zeroing it + at the return below. But slp_restore_state + writes random values to the stack slot used for this + save/restore (from when it once was saved above in + SLP_SAVE_STATE, when it was still uninitialized), so + "restoring" that precious zero actually makes us + return random values. There are some ways to make + GCC not use that zero value in the normal return path + (e.g. making err volatile, but that costs a little + stack space), and the simplest is to call a function + that returns an unknown value (which happens to be zero), + so the saved/restored value is unused. + + Thus, this line stores a 0 into the ``err`` variable + (which must be held in a register for this instruction, + of course). The ``w`` qualifier causes the instruction + to use W0 instead of X0, otherwise we get a warning + about a value size mismatch (because err is an int, + and aarch64 platforms are LP64: 32-bit int, 64 bit long + and pointer). + */ + __asm__ volatile ("mov %w0, #0" : "=r" (err)); + } + __asm__ volatile ("ldr x29, %0" : : "m" (fp) :); + __asm__ volatile ("" : : : REGS_TO_SAVE); + return err; +} + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_alpha_unix.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_alpha_unix.h new file mode 100644 index 0000000..7e07abf --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_alpha_unix.h @@ -0,0 +1,30 @@ +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL +#define STACK_MAGIC 0 + +#define REGS_TO_SAVE "$9", "$10", "$11", "$12", "$13", "$14", "$15", \ + "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9" + +static int +slp_switch(void) +{ + int ret; + long *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("mov $30, %0" : "=r" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "addq $30, %0, $30\n\t" + : /* no outputs */ + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("mov $31, %0" : "=r" (ret) : ); + return ret; +} + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_amd64_unix.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_amd64_unix.h new file mode 100644 index 0000000..d470110 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_amd64_unix.h @@ -0,0 +1,87 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 3-May-13 Ralf Schmitt + * Add support for strange GCC caller-save decisions + * (ported from switch_aarch64_gcc.h) + * 18-Aug-11 Alexey Borzenkov + * Correctly save rbp, csr and cw + * 01-Apr-04 Hye-Shik Chang + * Ported from i386 to amd64. + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for spark + * 31-Avr-02 Armin Rigo + * Added ebx, esi and edi register-saves. + * 01-Mar-02 Samual M. Rushing + * Ported from i386. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +/* #define STACK_MAGIC 3 */ +/* the above works fine with gcc 2.96, but 2.95.3 wants this */ +#define STACK_MAGIC 0 + +#define REGS_TO_SAVE "r12", "r13", "r14", "r15" + +static int +slp_switch(void) +{ + int err; + void* rbp; + void* rbx; + unsigned int csr; + unsigned short cw; + /* This used to be declared 'register', but that does nothing in + modern compilers and is explicitly forbidden in some new + standards. */ + long *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("fstcw %0" : "=m" (cw)); + __asm__ volatile ("stmxcsr %0" : "=m" (csr)); + __asm__ volatile ("movq %%rbp, %0" : "=m" (rbp)); + __asm__ volatile ("movq %%rbx, %0" : "=m" (rbx)); + __asm__ ("movq %%rsp, %0" : "=g" (stackref)); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "addq %0, %%rsp\n" + "addq %0, %%rbp\n" + : + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + __asm__ volatile ("xorq %%rax, %%rax" : "=a" (err)); + } + __asm__ volatile ("movq %0, %%rbx" : : "m" (rbx)); + __asm__ volatile ("movq %0, %%rbp" : : "m" (rbp)); + __asm__ volatile ("ldmxcsr %0" : : "m" (csr)); + __asm__ volatile ("fldcw %0" : : "m" (cw)); + __asm__ volatile ("" : : : REGS_TO_SAVE); + return err; +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_gcc.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_gcc.h new file mode 100644 index 0000000..655003a --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_gcc.h @@ -0,0 +1,79 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 14-Aug-06 File creation. Ported from Arm Thumb. Sylvain Baro + * 3-Sep-06 Commented out saving of r1-r3 (r4 already commented out) as I + * read that these do not need to be saved. Also added notes and + * errors related to the frame pointer. Richard Tew. + * + * NOTES + * + * It is not possible to detect if fp is used or not, so the supplied + * switch function needs to support it, so that you can remove it if + * it does not apply to you. + * + * POSSIBLE ERRORS + * + * "fp cannot be used in asm here" + * + * - Try commenting out "fp" in REGS_TO_SAVE. + * + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL +#define STACK_MAGIC 0 +#define REG_SP "sp" +#define REG_SPSP "sp,sp" +#ifdef __thumb__ +#define REG_FP "r7" +#define REG_FPFP "r7,r7" +#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r9", "r10", "r11", "lr" +#else +#define REG_FP "fp" +#define REG_FPFP "fp,fp" +#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r7", "r8", "r9", "r10", "lr" +#endif +#if defined(__SOFTFP__) +#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL +#elif defined(__VFP_FP__) +#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \ + "d12", "d13", "d14", "d15" +#elif defined(__MAVERICK__) +#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "mvf4", "mvf5", "mvf6", "mvf7", \ + "mvf8", "mvf9", "mvf10", "mvf11", \ + "mvf12", "mvf13", "mvf14", "mvf15" +#else +#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "f4", "f5", "f6", "f7" +#endif + +static int +#ifdef __GNUC__ +__attribute__((optimize("no-omit-frame-pointer"))) +#endif +slp_switch(void) +{ + void *fp; + int *stackref, stsizediff; + int result; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("mov r0," REG_FP "\n\tstr r0,%0" : "=m" (fp) : : "r0"); + __asm__ ("mov %0," REG_SP : "=r" (stackref)); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "add " REG_SPSP ",%0\n" + "add " REG_FPFP ",%0\n" + : + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ("ldr r0,%1\n\tmov " REG_FP ",r0\n\tmov %0, #0" : "=r" (result) : "m" (fp) : "r0"); + __asm__ volatile ("" : : : REGS_TO_SAVE); + return result; +} + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_ios.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_ios.h new file mode 100644 index 0000000..9e640e1 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm32_ios.h @@ -0,0 +1,67 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 31-May-15 iOS support. Ported from arm32. Proton + * + * NOTES + * + * It is not possible to detect if fp is used or not, so the supplied + * switch function needs to support it, so that you can remove it if + * it does not apply to you. + * + * POSSIBLE ERRORS + * + * "fp cannot be used in asm here" + * + * - Try commenting out "fp" in REGS_TO_SAVE. + * + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 0 +#define REG_SP "sp" +#define REG_SPSP "sp,sp" +#define REG_FP "r7" +#define REG_FPFP "r7,r7" +#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r10", "r11", "lr" +#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \ + "d12", "d13", "d14", "d15" + +static int +#ifdef __GNUC__ +__attribute__((optimize("no-omit-frame-pointer"))) +#endif +slp_switch(void) +{ + void *fp; + int *stackref, stsizediff, result; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("str " REG_FP ",%0" : "=m" (fp)); + __asm__ ("mov %0," REG_SP : "=r" (stackref)); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "add " REG_SPSP ",%0\n" + "add " REG_FPFP ",%0\n" + : + : "r" (stsizediff) + : REGS_TO_SAVE /* Clobber registers, force compiler to + * recalculate address of void *fp from REG_SP or REG_FP */ + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ( + "ldr " REG_FP ", %1\n\t" + "mov %0, #0" + : "=r" (result) + : "m" (fp) + : REGS_TO_SAVE /* Force compiler to restore saved registers after this */ + ); + return result; +} + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.asm b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.asm new file mode 100644 index 0000000..29f9c22 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.asm @@ -0,0 +1,53 @@ + AREA switch_arm64_masm, CODE, READONLY; + GLOBAL slp_switch [FUNC] + EXTERN slp_save_state_asm + EXTERN slp_restore_state_asm + +slp_switch + ; push callee saved registers to stack + stp x19, x20, [sp, #-16]! + stp x21, x22, [sp, #-16]! + stp x23, x24, [sp, #-16]! + stp x25, x26, [sp, #-16]! + stp x27, x28, [sp, #-16]! + stp x29, x30, [sp, #-16]! + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + + ; call slp_save_state_asm with stack pointer + mov x0, sp + bl slp_save_state_asm + + ; early return for return value of 1 and -1 + cmp x0, #-1 + b.eq RETURN + cmp x0, #1 + b.eq RETURN + + ; increment stack and frame pointer + add sp, sp, x0 + add x29, x29, x0 + + bl slp_restore_state_asm + + ; store return value for successful completion of routine + mov x0, #0 + +RETURN + ; pop registers from stack + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + ldp x29, x30, [sp], #16 + ldp x27, x28, [sp], #16 + ldp x25, x26, [sp], #16 + ldp x23, x24, [sp], #16 + ldp x21, x22, [sp], #16 + ldp x19, x20, [sp], #16 + + ret + + END diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.obj b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_masm.obj new file mode 100644 index 0000000000000000000000000000000000000000..f6f220e4310baaa9756110685ce7d6a2bdf90c37 GIT binary patch literal 746 zcma)4PiqrF6n~qoo~*PNZ{i+=wji4b#Xu2~wiJpGk)*AMF07NyB(9n1#+i+!)I;v| zBKQG3?t1eB$T(lYgGb4+lu{_QmQrebldQB_4?cMF-uu0IZ{DA2e8@rZ+e^~30488W z`B{KPh%yV{HEIpyeum^wI#7P*HfX)ux?9U&c!SFK-$o|OFtKn{Q|a-#N>2inp0-tb zCRKXAtg2SolaoLv$Ll&ds_Epj?SH+8K{t?XSjKaFsEy%yh}=V70BaHj zEY5kWk_zcJo{$SSUL~=K(zW|tnhm$rA z<%dZ$q?>RX*18r{!azhaYR1lVb;g;mR-6h!#F>|p@;aje%0a|CZrE7s+SXuT>MS=Y ziQPiMY-5DD&5+S7^H03fy7qt7ir}L34kK|h68s-MU>{lXOqlr?!Y=`~WwviNenFS_ zZalVSHh-0FU4lj#X8u5`ODn6@#{f?dHE&%XdP~_I3;$RS9-(z*>>ydkm*f@oWlUn~ Qn+^;lsEi}=H#!Q3U&UU-WdHyG literal 0 HcmV?d00001 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_msvc.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_msvc.h new file mode 100644 index 0000000..7ab7f45 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_arm64_msvc.h @@ -0,0 +1,17 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 21-Oct-21 Niyas Sait + * First version to enable win/arm64 support. + */ + +#define STACK_REFPLUS 1 +#define STACK_MAGIC 0 + +/* Use the generic support for an external assembly language slp_switch function. */ +#define EXTERNAL_ASM + +#ifdef SLP_EVAL +/* This always uses the external masm assembly file. */ +#endif \ No newline at end of file diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_csky_gcc.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_csky_gcc.h new file mode 100644 index 0000000..ac469d3 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_csky_gcc.h @@ -0,0 +1,48 @@ +#ifdef SLP_EVAL +#define STACK_MAGIC 0 +#define REG_FP "r8" +#ifdef __CSKYABIV2__ +#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r7", "r9", "r10", "r11", "r15",\ + "r16", "r17", "r18", "r19", "r20", "r21", "r22",\ + "r23", "r24", "r25" + +#if defined (__CSKY_HARD_FLOAT__) || (__CSKY_VDSP__) +#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "vr8", "vr9", "vr10", "vr11", "vr12",\ + "vr13", "vr14", "vr15" +#else +#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL +#endif +#else +#define REGS_TO_SAVE "r9", "r10", "r11", "r12", "r13", "r15" +#endif + + +static int +#ifdef __GNUC__ +__attribute__((optimize("no-omit-frame-pointer"))) +#endif +slp_switch(void) +{ + int *stackref, stsizediff; + int result; + + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("mov %0, sp" : "=r" (stackref)); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "addu sp,%0\n" + "addu "REG_FP",%0\n" + : + : "r" (stsizediff) + ); + + SLP_RESTORE_STATE(); + } + __asm__ volatile ("movi %0, 0" : "=r" (result)); + __asm__ volatile ("" : : : REGS_TO_SAVE); + + return result; +} + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_loongarch64_linux.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_loongarch64_linux.h new file mode 100644 index 0000000..9eaf34e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_loongarch64_linux.h @@ -0,0 +1,31 @@ +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL +#define STACK_MAGIC 0 + +#define REGS_TO_SAVE "s0", "s1", "s2", "s3", "s4", "s5", \ + "s6", "s7", "s8", "fp", \ + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31" + +static int +slp_switch(void) +{ + int ret; + long *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("move %0, $sp" : "=r" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "add.d $sp, $sp, %0\n\t" + : /* no outputs */ + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("move %0, $zero" : "=r" (ret) : ); + return ret; +} + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_m68k_gcc.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_m68k_gcc.h new file mode 100644 index 0000000..da761c2 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_m68k_gcc.h @@ -0,0 +1,38 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 2014-01-06 Andreas Schwab + * File created. + */ + +#ifdef SLP_EVAL + +#define STACK_MAGIC 0 + +#define REGS_TO_SAVE "%d2", "%d3", "%d4", "%d5", "%d6", "%d7", \ + "%a2", "%a3", "%a4" + +static int +slp_switch(void) +{ + int err; + int *stackref, stsizediff; + void *fp, *a5; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("move.l %%fp, %0" : "=m"(fp)); + __asm__ volatile ("move.l %%a5, %0" : "=m"(a5)); + __asm__ ("move.l %%sp, %0" : "=r"(stackref)); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ("add.l %0, %%sp; add.l %0, %%fp" : : "r"(stsizediff)); + SLP_RESTORE_STATE(); + __asm__ volatile ("clr.l %0" : "=g" (err)); + } + __asm__ volatile ("move.l %0, %%a5" : : "m"(a5)); + __asm__ volatile ("move.l %0, %%fp" : : "m"(fp)); + __asm__ volatile ("" : : : REGS_TO_SAVE); + return err; +} + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_mips_unix.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_mips_unix.h new file mode 100644 index 0000000..b9003e9 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_mips_unix.h @@ -0,0 +1,64 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 20-Sep-14 Matt Madison + * Re-code the saving of the gp register for MIPS64. + * 05-Jan-08 Thiemo Seufer + * Ported from ppc. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 0 + +#define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \ + "$23", "$30" +static int +slp_switch(void) +{ + int err; + int *stackref, stsizediff; +#ifdef __mips64 + uint64_t gpsave; +#endif + __asm__ __volatile__ ("" : : : REGS_TO_SAVE); +#ifdef __mips64 + __asm__ __volatile__ ("sd $28,%0" : "=m" (gpsave) : : ); +#endif + __asm__ ("move %0, $29" : "=r" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ __volatile__ ( +#ifdef __mips64 + "daddu $29, %0\n" +#else + "addu $29, %0\n" +#endif + : /* no outputs */ + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + } +#ifdef __mips64 + __asm__ __volatile__ ("ld $28,%0" : : "m" (gpsave) : ); +#endif + __asm__ __volatile__ ("" : : : REGS_TO_SAVE); + __asm__ __volatile__ ("move %0, $0" : "=r" (err)); + return err; +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc64_aix.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc64_aix.h new file mode 100644 index 0000000..e7e0b87 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc64_aix.h @@ -0,0 +1,103 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 16-Oct-20 Jesse Gorzinski + * Copied from Linux PPC64 implementation + * 04-Sep-18 Alexey Borzenkov + * Workaround a gcc bug using manual save/restore of r30 + * 21-Mar-18 Tulio Magno Quites Machado Filho + * Added r30 to the list of saved registers in order to fully comply with + * both ppc64 ELFv1 ABI and the ppc64le ELFv2 ABI, that classify this + * register as a nonvolatile register used for local variables. + * 21-Mar-18 Laszlo Boszormenyi + * Save r2 (TOC pointer) manually. + * 10-Dec-13 Ulrich Weigand + * Support ELFv2 ABI. Save float/vector registers. + * 09-Mar-12 Michael Ellerman + * 64-bit implementation, copied from 32-bit. + * 07-Sep-05 (py-dev mailing list discussion) + * removed 'r31' from the register-saved. !!!! WARNING !!!! + * It means that this file can no longer be compiled statically! + * It is now only suitable as part of a dynamic library! + * 14-Jan-04 Bob Ippolito + * added cr2-cr4 to the registers to be saved. + * Open questions: Should we save FP registers? + * What about vector registers? + * Differences between darwin and unix? + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 04-Oct-02 Gustavo Niemeyer + * Ported from MacOS version. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for sparc + * 29-Jun-02 Christian Tismer + * Added register 13-29, 31 saves. The same way as + * Armin Rigo did for the x86_unix version. + * This seems to be now fully functional! + * 04-Mar-02 Hye-Shik Chang + * Ported from i386. + * 31-Jul-12 Trevor Bowen + * Changed memory constraints to register only. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 6 + +#if defined(__ALTIVEC__) +#define ALTIVEC_REGS \ + "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", \ + "v28", "v29", "v30", "v31", +#else +#define ALTIVEC_REGS +#endif + +#define REGS_TO_SAVE "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ + "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ + "r31", \ + "fr14", "fr15", "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", \ + "fr22", "fr23", "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", \ + "fr30", "fr31", \ + ALTIVEC_REGS \ + "cr2", "cr3", "cr4" + +static int +slp_switch(void) +{ + int err; + long *stackref, stsizediff; + void * toc; + void * r30; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("std 2, %0" : "=m" (toc)); + __asm__ volatile ("std 30, %0" : "=m" (r30)); + __asm__ ("mr %0, 1" : "=r" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "mr 11, %0\n" + "add 1, 1, 11\n" + : /* no outputs */ + : "r" (stsizediff) + : "11" + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ("ld 30, %0" : : "m" (r30)); + __asm__ volatile ("ld 2, %0" : : "m" (toc)); + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("li %0, 0" : "=r" (err)); + return err; +} + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc64_linux.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc64_linux.h new file mode 100644 index 0000000..3c324d0 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc64_linux.h @@ -0,0 +1,105 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 04-Sep-18 Alexey Borzenkov + * Workaround a gcc bug using manual save/restore of r30 + * 21-Mar-18 Tulio Magno Quites Machado Filho + * Added r30 to the list of saved registers in order to fully comply with + * both ppc64 ELFv1 ABI and the ppc64le ELFv2 ABI, that classify this + * register as a nonvolatile register used for local variables. + * 21-Mar-18 Laszlo Boszormenyi + * Save r2 (TOC pointer) manually. + * 10-Dec-13 Ulrich Weigand + * Support ELFv2 ABI. Save float/vector registers. + * 09-Mar-12 Michael Ellerman + * 64-bit implementation, copied from 32-bit. + * 07-Sep-05 (py-dev mailing list discussion) + * removed 'r31' from the register-saved. !!!! WARNING !!!! + * It means that this file can no longer be compiled statically! + * It is now only suitable as part of a dynamic library! + * 14-Jan-04 Bob Ippolito + * added cr2-cr4 to the registers to be saved. + * Open questions: Should we save FP registers? + * What about vector registers? + * Differences between darwin and unix? + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 04-Oct-02 Gustavo Niemeyer + * Ported from MacOS version. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for sparc + * 29-Jun-02 Christian Tismer + * Added register 13-29, 31 saves. The same way as + * Armin Rigo did for the x86_unix version. + * This seems to be now fully functional! + * 04-Mar-02 Hye-Shik Chang + * Ported from i386. + * 31-Jul-12 Trevor Bowen + * Changed memory constraints to register only. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#if _CALL_ELF == 2 +#define STACK_MAGIC 4 +#else +#define STACK_MAGIC 6 +#endif + +#if defined(__ALTIVEC__) +#define ALTIVEC_REGS \ + "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", \ + "v28", "v29", "v30", "v31", +#else +#define ALTIVEC_REGS +#endif + +#define REGS_TO_SAVE "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ + "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ + "r31", \ + "fr14", "fr15", "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", \ + "fr22", "fr23", "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", \ + "fr30", "fr31", \ + ALTIVEC_REGS \ + "cr2", "cr3", "cr4" + +static int +slp_switch(void) +{ + int err; + long *stackref, stsizediff; + void * toc; + void * r30; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("std 2, %0" : "=m" (toc)); + __asm__ volatile ("std 30, %0" : "=m" (r30)); + __asm__ ("mr %0, 1" : "=r" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "mr 11, %0\n" + "add 1, 1, 11\n" + : /* no outputs */ + : "r" (stsizediff) + : "11" + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ("ld 30, %0" : : "m" (r30)); + __asm__ volatile ("ld 2, %0" : : "m" (toc)); + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("li %0, 0" : "=r" (err)); + return err; +} + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_aix.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_aix.h new file mode 100644 index 0000000..6d93c13 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_aix.h @@ -0,0 +1,87 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 07-Mar-11 Floris Bruynooghe + * Do not add stsizediff to general purpose + * register (GPR) 30 as this is a non-volatile and + * unused by the PowerOpen Environment, therefore + * this was modifying a user register instead of the + * frame pointer (which does not seem to exist). + * 07-Sep-05 (py-dev mailing list discussion) + * removed 'r31' from the register-saved. !!!! WARNING !!!! + * It means that this file can no longer be compiled statically! + * It is now only suitable as part of a dynamic library! + * 14-Jan-04 Bob Ippolito + * added cr2-cr4 to the registers to be saved. + * Open questions: Should we save FP registers? + * What about vector registers? + * Differences between darwin and unix? + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 04-Oct-02 Gustavo Niemeyer + * Ported from MacOS version. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for sparc + * 29-Jun-02 Christian Tismer + * Added register 13-29, 31 saves. The same way as + * Armin Rigo did for the x86_unix version. + * This seems to be now fully functional! + * 04-Mar-02 Hye-Shik Chang + * Ported from i386. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 3 + +/* !!!!WARNING!!!! need to add "r31" in the next line if this header file + * is meant to be compiled non-dynamically! + */ +#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ + "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ + "cr2", "cr3", "cr4" +static int +slp_switch(void) +{ + int err; + int *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("mr %0, 1" : "=r" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "mr 11, %0\n" + "add 1, 1, 11\n" + : /* no outputs */ + : "r" (stsizediff) + : "11" + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("li %0, 0" : "=r" (err)); + return err; +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_linux.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_linux.h new file mode 100644 index 0000000..e83ad70 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_linux.h @@ -0,0 +1,84 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 07-Sep-05 (py-dev mailing list discussion) + * removed 'r31' from the register-saved. !!!! WARNING !!!! + * It means that this file can no longer be compiled statically! + * It is now only suitable as part of a dynamic library! + * 14-Jan-04 Bob Ippolito + * added cr2-cr4 to the registers to be saved. + * Open questions: Should we save FP registers? + * What about vector registers? + * Differences between darwin and unix? + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 04-Oct-02 Gustavo Niemeyer + * Ported from MacOS version. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for sparc + * 29-Jun-02 Christian Tismer + * Added register 13-29, 31 saves. The same way as + * Armin Rigo did for the x86_unix version. + * This seems to be now fully functional! + * 04-Mar-02 Hye-Shik Chang + * Ported from i386. + * 31-Jul-12 Trevor Bowen + * Changed memory constraints to register only. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 3 + +/* !!!!WARNING!!!! need to add "r31" in the next line if this header file + * is meant to be compiled non-dynamically! + */ +#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ + "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ + "cr2", "cr3", "cr4" +static int +slp_switch(void) +{ + int err; + int *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("mr %0, 1" : "=r" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "mr 11, %0\n" + "add 1, 1, 11\n" + "add 30, 30, 11\n" + : /* no outputs */ + : "r" (stsizediff) + : "11" + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("li %0, 0" : "=r" (err)); + return err; +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_macosx.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_macosx.h new file mode 100644 index 0000000..d6e5a03 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_macosx.h @@ -0,0 +1,82 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 07-Sep-05 (py-dev mailing list discussion) + * removed 'r31' from the register-saved. !!!! WARNING !!!! + * It means that this file can no longer be compiled statically! + * It is now only suitable as part of a dynamic library! + * 14-Jan-04 Bob Ippolito + * added cr2-cr4 to the registers to be saved. + * Open questions: Should we save FP registers? + * What about vector registers? + * Differences between darwin and unix? + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for sparc + * 29-Jun-02 Christian Tismer + * Added register 13-29, 31 saves. The same way as + * Armin Rigo did for the x86_unix version. + * This seems to be now fully functional! + * 04-Mar-02 Hye-Shik Chang + * Ported from i386. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 3 + +/* !!!!WARNING!!!! need to add "r31" in the next line if this header file + * is meant to be compiled non-dynamically! + */ +#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ + "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ + "cr2", "cr3", "cr4" + +static int +slp_switch(void) +{ + int err; + int *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("; asm block 2\n\tmr %0, r1" : "=g" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "; asm block 3\n" + "\tmr r11, %0\n" + "\tadd r1, r1, r11\n" + "\tadd r30, r30, r11\n" + : /* no outputs */ + : "g" (stsizediff) + : "r11" + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("li %0, 0" : "=r" (err)); + return err; +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_unix.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_unix.h new file mode 100644 index 0000000..ca590a5 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_ppc_unix.h @@ -0,0 +1,82 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 07-Sep-05 (py-dev mailing list discussion) + * removed 'r31' from the register-saved. !!!! WARNING !!!! + * It means that this file can no longer be compiled statically! + * It is now only suitable as part of a dynamic library! + * 14-Jan-04 Bob Ippolito + * added cr2-cr4 to the registers to be saved. + * Open questions: Should we save FP registers? + * What about vector registers? + * Differences between darwin and unix? + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 04-Oct-02 Gustavo Niemeyer + * Ported from MacOS version. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for sparc + * 29-Jun-02 Christian Tismer + * Added register 13-29, 31 saves. The same way as + * Armin Rigo did for the x86_unix version. + * This seems to be now fully functional! + * 04-Mar-02 Hye-Shik Chang + * Ported from i386. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 3 + +/* !!!!WARNING!!!! need to add "r31" in the next line if this header file + * is meant to be compiled non-dynamically! + */ +#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ + "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ + "cr2", "cr3", "cr4" +static int +slp_switch(void) +{ + int err; + int *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ ("mr %0, 1" : "=g" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "mr 11, %0\n" + "add 1, 1, 11\n" + "add 30, 30, 11\n" + : /* no outputs */ + : "g" (stsizediff) + : "11" + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("li %0, 0" : "=r" (err)); + return err; +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_riscv_unix.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_riscv_unix.h new file mode 100644 index 0000000..24df9db --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_riscv_unix.h @@ -0,0 +1,32 @@ +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL +#define STACK_MAGIC 0 + +#define REGS_TO_SAVE "s0", "s1", "s2", "s3", "s4", "s5", \ + "s6", "s7", "s8", "s9", "s10", "s11", "fs0", "fs1", \ + "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9", \ + "fs10", "fs11" + +static int +slp_switch(void) +{ + int ret; + long *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("mv %0, sp" : "=r" (stackref) : ); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "add sp, sp, %0\n\t" + : /* no outputs */ + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("mv %0, zero" : "=r" (ret) : ); + return ret; +} + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_s390_unix.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_s390_unix.h new file mode 100644 index 0000000..9199367 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_s390_unix.h @@ -0,0 +1,87 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 25-Jan-12 Alexey Borzenkov + * Fixed Linux/S390 port to work correctly with + * different optimization options both on 31-bit + * and 64-bit. Thanks to Stefan Raabe for lots + * of testing. + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 06-Oct-02 Gustavo Niemeyer + * Ported to Linux/S390. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#ifdef __s390x__ +#define STACK_MAGIC 20 /* 20 * 8 = 160 bytes of function call area */ +#else +#define STACK_MAGIC 24 /* 24 * 4 = 96 bytes of function call area */ +#endif + +/* Technically, r11-r13 also need saving, but function prolog starts + with stm(g) and since there are so many saved registers already + it won't be optimized, resulting in all r6-r15 being saved */ +#define REGS_TO_SAVE "r6", "r7", "r8", "r9", "r10", "r14", \ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15" + +static int +slp_switch(void) +{ + int ret; + long *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); +#ifdef __s390x__ + __asm__ volatile ("lgr %0, 15" : "=r" (stackref) : ); +#else + __asm__ volatile ("lr %0, 15" : "=r" (stackref) : ); +#endif + { + SLP_SAVE_STATE(stackref, stsizediff); +/* N.B. + r11 may be used as the frame pointer, and in that case it cannot be + clobbered and needs offsetting just like the stack pointer (but in cases + where frame pointer isn't used we might clobber it accidentally). What's + scary is that r11 is 2nd (and even 1st when GOT is used) callee saved + register that gcc would chose for surviving function calls. However, + since r6-r10 are clobbered above, their cost for reuse is reduced, so + gcc IRA will chose them over r11 (not seeing r11 is implicitly saved), + making it relatively safe to offset in all cases. :) */ + __asm__ volatile ( +#ifdef __s390x__ + "agr 15, %0\n\t" + "agr 11, %0" +#else + "ar 15, %0\n\t" + "ar 11, %0" +#endif + : /* no outputs */ + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("lhi %0, 0" : "=r" (ret) : ); + return ret; +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_sparc_sun_gcc.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_sparc_sun_gcc.h new file mode 100644 index 0000000..96990c3 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_sparc_sun_gcc.h @@ -0,0 +1,92 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 16-May-15 Alexey Borzenkov + * Move stack spilling code inside save/restore functions + * 30-Aug-13 Floris Bruynooghe + Clean the register windows again before returning. + This does not clobber the PIC register as it leaves + the current window intact and is required for multi- + threaded code to work correctly. + * 08-Mar-11 Floris Bruynooghe + * No need to set return value register explicitly + * before the stack and framepointer are adjusted + * as none of the other registers are influenced by + * this. Also don't needlessly clean the windows + * ('ta %0" :: "i" (ST_CLEAN_WINDOWS)') as that + * clobbers the gcc PIC register (%l7). + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * added support for SunOS sparc with gcc + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + + +#define STACK_MAGIC 0 + + +#if defined(__sparcv9) +#define SLP_FLUSHW __asm__ volatile ("flushw") +#else +#define SLP_FLUSHW __asm__ volatile ("ta 3") /* ST_FLUSH_WINDOWS */ +#endif + +/* On sparc we need to spill register windows inside save/restore functions */ +#define SLP_BEFORE_SAVE_STATE() SLP_FLUSHW +#define SLP_BEFORE_RESTORE_STATE() SLP_FLUSHW + + +static int +slp_switch(void) +{ + int err; + int *stackref, stsizediff; + + /* Put current stack pointer into stackref. + * Register spilling is done in save/restore. + */ + __asm__ volatile ("mov %%sp, %0" : "=r" (stackref)); + + { + /* Thou shalt put SLP_SAVE_STATE into a local block */ + /* Copy the current stack onto the heap */ + SLP_SAVE_STATE(stackref, stsizediff); + + /* Increment stack and frame pointer by stsizediff */ + __asm__ volatile ( + "add %0, %%sp, %%sp\n\t" + "add %0, %%fp, %%fp" + : : "r" (stsizediff)); + + /* Copy new stack from it's save store on the heap */ + SLP_RESTORE_STATE(); + + __asm__ volatile ("mov %1, %0" : "=r" (err) : "i" (0)); + return err; + } +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x32_unix.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x32_unix.h new file mode 100644 index 0000000..893369c --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x32_unix.h @@ -0,0 +1,63 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 17-Aug-12 Fantix King + * Ported from amd64. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 0 + +#define REGS_TO_SAVE "r12", "r13", "r14", "r15" + + +static int +slp_switch(void) +{ + void* ebp; + void* ebx; + unsigned int csr; + unsigned short cw; + int err; + int *stackref, stsizediff; + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("fstcw %0" : "=m" (cw)); + __asm__ volatile ("stmxcsr %0" : "=m" (csr)); + __asm__ volatile ("movl %%ebp, %0" : "=m" (ebp)); + __asm__ volatile ("movl %%ebx, %0" : "=m" (ebx)); + __asm__ ("movl %%esp, %0" : "=g" (stackref)); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "addl %0, %%esp\n" + "addl %0, %%ebp\n" + : + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + } + __asm__ volatile ("movl %0, %%ebx" : : "m" (ebx)); + __asm__ volatile ("movl %0, %%ebp" : : "m" (ebp)); + __asm__ volatile ("ldmxcsr %0" : : "m" (csr)); + __asm__ volatile ("fldcw %0" : : "m" (cw)); + __asm__ volatile ("" : : : REGS_TO_SAVE); + __asm__ volatile ("xorl %%eax, %%eax" : "=a" (err)); + return err; +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x64_masm.asm b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x64_masm.asm new file mode 100644 index 0000000..f5c72a2 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x64_masm.asm @@ -0,0 +1,111 @@ +; +; stack switching code for MASM on x641 +; Kristjan Valur Jonsson, sept 2005 +; + + +;prototypes for our calls +slp_save_state_asm PROTO +slp_restore_state_asm PROTO + + +pushxmm MACRO reg + sub rsp, 16 + .allocstack 16 + movaps [rsp], reg ; faster than movups, but we must be aligned + ; .savexmm128 reg, offset (don't know what offset is, no documentation) +ENDM +popxmm MACRO reg + movaps reg, [rsp] ; faster than movups, but we must be aligned + add rsp, 16 +ENDM + +pushreg MACRO reg + push reg + .pushreg reg +ENDM +popreg MACRO reg + pop reg +ENDM + + +.code +slp_switch PROC FRAME + ;realign stack to 16 bytes after return address push, makes the following faster + sub rsp,8 + .allocstack 8 + + pushxmm xmm15 + pushxmm xmm14 + pushxmm xmm13 + pushxmm xmm12 + pushxmm xmm11 + pushxmm xmm10 + pushxmm xmm9 + pushxmm xmm8 + pushxmm xmm7 + pushxmm xmm6 + + pushreg r15 + pushreg r14 + pushreg r13 + pushreg r12 + + pushreg rbp + pushreg rbx + pushreg rdi + pushreg rsi + + sub rsp, 10h ;allocate the singlefunction argument (must be multiple of 16) + .allocstack 10h +.endprolog + + lea rcx, [rsp+10h] ;load stack base that we are saving + call slp_save_state_asm ;pass stackpointer, return offset in eax + cmp rax, 1 + je EXIT1 + cmp rax, -1 + je EXIT2 + ;actual stack switch: + add rsp, rax + call slp_restore_state_asm + xor rax, rax ;return 0 + +EXIT: + + add rsp, 10h + popreg rsi + popreg rdi + popreg rbx + popreg rbp + + popreg r12 + popreg r13 + popreg r14 + popreg r15 + + popxmm xmm6 + popxmm xmm7 + popxmm xmm8 + popxmm xmm9 + popxmm xmm10 + popxmm xmm11 + popxmm xmm12 + popxmm xmm13 + popxmm xmm14 + popxmm xmm15 + + add rsp, 8 + ret + +EXIT1: + mov rax, 1 + jmp EXIT + +EXIT2: + sar rax, 1 + jmp EXIT + +slp_switch ENDP + +END \ No newline at end of file diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x64_masm.obj b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x64_masm.obj new file mode 100644 index 0000000000000000000000000000000000000000..64e3e6b898ec765d4e37075f7b1635ad24c9efa2 GIT binary patch literal 1078 zcmZ{j&ubG=5XWb`DJB@*%~BA=L%=;Gk}d_~52VO$4J4q2U~MY6&1RFl{E&?scGnt@ zn(9GNy!ihFEO@PV4?T&H9`x2*oO!!jlNJZwd!P4xlX&_;U$Bg3z>p zje>}2kp+MsxE|w5hOUr>YC~(=fz6fwPdZd5+Hlb^P3{;ZO@Yuv96GG&+Gx?QfclNd zhy2KN&~>fNnlHQRR;U1cMyQ?hlQ$~k<0KBbB<0uD2#PTjVo+na7Q;#m=@=3m;xJOa zs2V#)&Db`cY;WzTF9)11;SjkVQWE!?bPTC%x3h3^F2;aBns5!i%m4&-*h69;~AUpZR%rDpm!zuXY+kc zFCz-n*^4&c)5~}y3e?r-Evy*n*(lp9r%ti58Y#l5&)rDjx5EbRd}nC+_8znRzz&#& XZ_Fi+`GM=5Rl{n4%KxAK>jC@)Nz=zi literal 0 HcmV?d00001 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x64_msvc.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x64_msvc.h new file mode 100644 index 0000000..601ea56 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x64_msvc.h @@ -0,0 +1,60 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 26-Sep-02 Christian Tismer + * again as a result of virtualized stack access, + * the compiler used less registers. Needed to + * explicit mention registers in order to get them saved. + * Thanks to Jeff Senn for pointing this out and help. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for sparc + * 01-Mar-02 Christian Tismer + * Initial final version after lots of iterations for i386. + */ + +/* Avoid alloca redefined warning on mingw64 */ +#ifndef alloca +#define alloca _alloca +#endif + +#define STACK_REFPLUS 1 +#define STACK_MAGIC 0 + +/* Use the generic support for an external assembly language slp_switch function. */ +#define EXTERNAL_ASM + +#ifdef SLP_EVAL +/* This always uses the external masm assembly file. */ +#endif + +/* + * further self-processing support + */ + +/* we have IsBadReadPtr available, so we can peek at objects */ +/* +#define STACKLESS_SPY + +#ifdef IMPLEMENT_STACKLESSMODULE +#include "Windows.h" +#define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) + +static int IS_ON_STACK(void*p) +{ + int stackref; + intptr_t stackbase = ((intptr_t)&stackref) & 0xfffff000; + return (intptr_t)p >= stackbase && (intptr_t)p < stackbase + 0x00100000; +} + +#endif +*/ \ No newline at end of file diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x86_msvc.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x86_msvc.h new file mode 100644 index 0000000..0f3a59f --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x86_msvc.h @@ -0,0 +1,326 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 26-Sep-02 Christian Tismer + * again as a result of virtualized stack access, + * the compiler used less registers. Needed to + * explicit mention registers in order to get them saved. + * Thanks to Jeff Senn for pointing this out and help. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for sparc + * 01-Mar-02 Christian Tismer + * Initial final version after lots of iterations for i386. + */ + +#define alloca _alloca + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +#define STACK_MAGIC 0 + +/* Some magic to quell warnings and keep slp_switch() from crashing when built + with VC90. Disable global optimizations, and the warning: frame pointer + register 'ebp' modified by inline assembly code. + + We used to just disable global optimizations ("g") but upstream stackless + Python, as well as stackman, turn off all optimizations. + +References: +https://github.com/stackless-dev/stackman/blob/dbc72fe5207a2055e658c819fdeab9731dee78b9/stackman/platforms/switch_x86_msvc.h +https://github.com/stackless-dev/stackless/blob/main-slp/Stackless/platf/switch_x86_msvc.h +*/ +#define WIN32_LEAN_AND_MEAN +#include + +#pragma optimize("", off) /* so that autos are stored on the stack */ +#pragma warning(disable:4731) +#pragma warning(disable:4733) /* disable warning about modifying FS[0] */ + +/** + * Most modern compilers and environments handle C++ exceptions without any + * special help from us. MSVC on 32-bit windows is an exception. There, C++ + * exceptions are dealt with using Windows' Structured Exception Handling + * (SEH). + * + * SEH is implemented as a singly linked list of nodes. The + * head of this list is stored in the Thread Information Block, which itself + * is pointed to from the FS register. It's the first field in the structure, + * or offset 0, so we can access it using assembly FS:[0], or the compiler + * intrinsics and field offset information from the headers (as we do below). + * Somewhat unusually, the tail of the list doesn't have prev == NULL, it has + * prev == 0xFFFFFFFF. + * + * SEH was designed for C, and traditionally uses the MSVC compiler + * intrinsincs __try{}/__except{}. It is also utilized for C++ exceptions by + * MSVC; there, every throw of a C++ exception raises a SEH error with the + * ExceptionCode 0xE06D7363; the SEH handler list is then traversed to + * deal with the exception. + * + * If the SEH list is corrupt, then when a C++ exception is thrown the program + * will abruptly exit with exit code 1. This does not use std::terminate(), so + * std::set_terminate() is useless to debug this. + * + * The SEH list is closely tied to the call stack; entering a function that + * uses __try{} or most C++ functions will push a new handler onto the front + * of the list. Returning from the function will remove the handler. Saving + * and restoring the head node of the SEH list (FS:[0]) per-greenlet is NOT + * ENOUGH to make SEH or exceptions work. + * + * Stack switching breaks SEH because the call stack no longer necessarily + * matches the SEH list. For example, given greenlet A that switches to + * greenlet B, at the moment of entering greenlet B, we will have any SEH + * handlers from greenlet A on the SEH list; greenlet B can then add its own + * handlers to the SEH list. When greenlet B switches back to greenlet A, + * greenlet B's handlers would still be on the SEH stack, but when switch() + * returns control to greenlet A, we have replaced the contents of the stack + * in memory, so all the address that greenlet B added to the SEH list are now + * invalid: part of the call stack has been unwound, but the SEH list was out + * of sync with the call stack. The net effect is that exception handling + * stops working. + * + * Thus, when switching greenlets, we need to be sure that the SEH list + * matches the effective call stack, "cutting out" any handlers that were + * pushed by the greenlet that switched out and which are no longer valid. + * + * The easiest way to do this is to capture the SEH list at the time the main + * greenlet for a thread is created, and, when initially starting a greenlet, + * start a new SEH list for it, which contains nothing but the handler + * established for the new greenlet itself, with the tail being the handlers + * for the main greenlet. If we then save and restore the SEH per-greenlet, + * they won't interfere with each others SEH lists. (No greenlet can unwind + * the call stack past the handlers established by the main greenlet). + * + * By observation, a new thread starts with three SEH handlers on the list. By + * the time we get around to creating the main greenlet, though, there can be + * many more, established by transient calls that lead to the creation of the + * main greenlet. Therefore, 3 is a magic constant telling us when to perform + * the initial slice. + * + * All of this can be debugged using a vectored exception handler, which + * operates independently of the SEH handler list, and is called first. + * Walking the SEH list at key points can also be helpful. + * + * References: + * https://en.wikipedia.org/wiki/Win32_Thread_Information_Block + * https://devblogs.microsoft.com/oldnewthing/20100730-00/?p=13273 + * https://docs.microsoft.com/en-us/cpp/cpp/try-except-statement?view=msvc-160 + * https://docs.microsoft.com/en-us/cpp/cpp/structured-exception-handling-c-cpp?view=msvc-160 + * https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling + * https://docs.microsoft.com/en-us/windows/win32/debug/using-a-vectored-exception-handler + * https://bytepointer.com/resources/pietrek_crash_course_depths_of_win32_seh.htm + */ +#define GREENLET_NEEDS_EXCEPTION_STATE_SAVED + + +typedef struct _GExceptionRegistration { + struct _GExceptionRegistration* prev; + void* handler_f; +} GExceptionRegistration; + +static void +slp_set_exception_state(const void *const seh_state) +{ + // Because the stack from from which we do this is ALSO a handler, and + // that one we want to keep, we need to relink the current SEH handler + // frame to point to this one, cutting out the middle men, as it were. + // + // Entering a try block doesn't change the SEH frame, but entering a + // function containing a try block does. + GExceptionRegistration* current_seh_state = (GExceptionRegistration*)__readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); + current_seh_state->prev = (GExceptionRegistration*)seh_state; +} + + +static GExceptionRegistration* +x86_slp_get_third_oldest_handler() +{ + GExceptionRegistration* a = NULL; /* Closest to the top */ + GExceptionRegistration* b = NULL; /* second */ + GExceptionRegistration* c = NULL; + GExceptionRegistration* seh_state = (GExceptionRegistration*)__readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); + a = b = c = seh_state; + + while (seh_state && seh_state != (GExceptionRegistration*)0xFFFFFFFF) { + if ((void*)seh_state->prev < (void*)100) { + fprintf(stderr, "\tERROR: Broken SEH chain.\n"); + return NULL; + } + a = b; + b = c; + c = seh_state; + + seh_state = seh_state->prev; + } + return a ? a : (b ? b : c); +} + + +static void* +slp_get_exception_state() +{ + // XXX: There appear to be three SEH handlers on the stack already at the + // start of the thread. Is that a guarantee? Almost certainly not. Yet in + // all observed cases it has been three. This is consistent with + // faulthandler off or on, and optimizations off or on. It may not be + // consistent with other operating system versions, though: we only have + // CI on one or two versions (don't ask what there are). + // In theory we could capture the number of handlers on the chain when + // PyInit__greenlet is called: there are probably only the default + // handlers at that point (unless we're embedded and people have used + // __try/__except or a C++ handler)? + return x86_slp_get_third_oldest_handler(); +} + +static int +slp_switch(void) +{ + /* MASM syntax is typically reversed from other assemblers. + It is usually + */ + int *stackref, stsizediff; + /* store the structured exception state for this stack */ + DWORD seh_state = __readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); + __asm mov stackref, esp; + /* modify EBX, ESI and EDI in order to get them preserved */ + __asm mov ebx, ebx; + __asm xchg esi, edi; + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm { + mov eax, stsizediff + add esp, eax + add ebp, eax + } + SLP_RESTORE_STATE(); + } + __writefsdword(FIELD_OFFSET(NT_TIB, ExceptionList), seh_state); + return 0; +} + +/* re-enable ebp warning and global optimizations. */ +#pragma optimize("", on) +#pragma warning(default:4731) +#pragma warning(default:4733) /* disable warning about modifying FS[0] */ + + +#endif + +/* + * further self-processing support + */ + +/* we have IsBadReadPtr available, so we can peek at objects */ +#define STACKLESS_SPY + +#ifdef GREENLET_DEBUG + +#define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) + +static int IS_ON_STACK(void*p) +{ + int stackref; + int stackbase = ((int)&stackref) & 0xfffff000; + return (int)p >= stackbase && (int)p < stackbase + 0x00100000; +} + +static void +x86_slp_show_seh_chain() +{ + GExceptionRegistration* seh_state = (GExceptionRegistration*)__readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); + fprintf(stderr, "====== SEH Chain ======\n"); + while (seh_state && seh_state != (GExceptionRegistration*)0xFFFFFFFF) { + fprintf(stderr, "\tSEH_chain addr: %p handler: %p prev: %p\n", + seh_state, + seh_state->handler_f, seh_state->prev); + if ((void*)seh_state->prev < (void*)100) { + fprintf(stderr, "\tERROR: Broken chain.\n"); + break; + } + seh_state = seh_state->prev; + } + fprintf(stderr, "====== End SEH Chain ======\n"); + fflush(NULL); + return; +} + +//addVectoredExceptionHandler constants: +//CALL_FIRST means call this exception handler first; +//CALL_LAST means call this exception handler last +#define CALL_FIRST 1 +#define CALL_LAST 0 + +LONG WINAPI +GreenletVectorHandler(PEXCEPTION_POINTERS ExceptionInfo) +{ + // We get one of these for every C++ exception, with code + // E06D7363 + // This is a special value that means "C++ exception from MSVC" + // https://devblogs.microsoft.com/oldnewthing/20100730-00/?p=13273 + // + // Install in the module init function with: + // AddVectoredExceptionHandler(CALL_FIRST, GreenletVectorHandler); + PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord; + + fprintf(stderr, + "GOT VECTORED EXCEPTION:\n" + "\tExceptionCode : %p\n" + "\tExceptionFlags : %p\n" + "\tExceptionAddr : %p\n" + "\tNumberparams : %ld\n", + ExceptionRecord->ExceptionCode, + ExceptionRecord->ExceptionFlags, + ExceptionRecord->ExceptionAddress, + ExceptionRecord->NumberParameters + ); + if (ExceptionRecord->ExceptionFlags & 1) { + fprintf(stderr, "\t\tEH_NONCONTINUABLE\n" ); + } + if (ExceptionRecord->ExceptionFlags & 2) { + fprintf(stderr, "\t\tEH_UNWINDING\n" ); + } + if (ExceptionRecord->ExceptionFlags & 4) { + fprintf(stderr, "\t\tEH_EXIT_UNWIND\n" ); + } + if (ExceptionRecord->ExceptionFlags & 8) { + fprintf(stderr, "\t\tEH_STACK_INVALID\n" ); + } + if (ExceptionRecord->ExceptionFlags & 0x10) { + fprintf(stderr, "\t\tEH_NESTED_CALL\n" ); + } + if (ExceptionRecord->ExceptionFlags & 0x20) { + fprintf(stderr, "\t\tEH_TARGET_UNWIND\n" ); + } + if (ExceptionRecord->ExceptionFlags & 0x40) { + fprintf(stderr, "\t\tEH_COLLIDED_UNWIND\n" ); + } + fprintf(stderr, "\n"); + fflush(NULL); + for(DWORD i = 0; i < ExceptionRecord->NumberParameters; i++) { + fprintf(stderr, "\t\t\tParam %ld: %lX\n", i, ExceptionRecord->ExceptionInformation[i]); + } + + if (ExceptionRecord->NumberParameters == 3) { + fprintf(stderr, "\tAbout to traverse SEH chain\n"); + // C++ Exception records have 3 params. + x86_slp_show_seh_chain(); + } + + return EXCEPTION_CONTINUE_SEARCH; +} + + + + +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x86_unix.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x86_unix.h new file mode 100644 index 0000000..493fa6b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/platform/switch_x86_unix.h @@ -0,0 +1,105 @@ +/* + * this is the internal transfer function. + * + * HISTORY + * 3-May-13 Ralf Schmitt + * Add support for strange GCC caller-save decisions + * (ported from switch_aarch64_gcc.h) + * 19-Aug-11 Alexey Borzenkov + * Correctly save ebp, ebx and cw + * 07-Sep-05 (py-dev mailing list discussion) + * removed 'ebx' from the register-saved. !!!! WARNING !!!! + * It means that this file can no longer be compiled statically! + * It is now only suitable as part of a dynamic library! + * 24-Nov-02 Christian Tismer + * needed to add another magic constant to insure + * that f in slp_eval_frame(PyFrameObject *f) + * STACK_REFPLUS will probably be 1 in most cases. + * gets included into the saved stack area. + * 17-Sep-02 Christian Tismer + * after virtualizing stack save/restore, the + * stack size shrunk a bit. Needed to introduce + * an adjustment STACK_MAGIC per platform. + * 15-Sep-02 Gerd Woetzel + * slightly changed framework for spark + * 31-Avr-02 Armin Rigo + * Added ebx, esi and edi register-saves. + * 01-Mar-02 Samual M. Rushing + * Ported from i386. + */ + +#define STACK_REFPLUS 1 + +#ifdef SLP_EVAL + +/* #define STACK_MAGIC 3 */ +/* the above works fine with gcc 2.96, but 2.95.3 wants this */ +#define STACK_MAGIC 0 + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +# define ATTR_NOCLONE __attribute__((noclone)) +#else +# define ATTR_NOCLONE +#endif + +static int +slp_switch(void) +{ + int err; +#ifdef _WIN32 + void *seh; +#endif + void *ebp, *ebx; + unsigned short cw; + int *stackref, stsizediff; + __asm__ volatile ("" : : : "esi", "edi"); + __asm__ volatile ("fstcw %0" : "=m" (cw)); + __asm__ volatile ("movl %%ebp, %0" : "=m" (ebp)); + __asm__ volatile ("movl %%ebx, %0" : "=m" (ebx)); +#ifdef _WIN32 + __asm__ volatile ( + "movl %%fs:0x0, %%eax\n" + "movl %%eax, %0\n" + : "=m" (seh) + : + : "eax"); +#endif + __asm__ ("movl %%esp, %0" : "=g" (stackref)); + { + SLP_SAVE_STATE(stackref, stsizediff); + __asm__ volatile ( + "addl %0, %%esp\n" + "addl %0, %%ebp\n" + : + : "r" (stsizediff) + ); + SLP_RESTORE_STATE(); + __asm__ volatile ("xorl %%eax, %%eax" : "=a" (err)); + } +#ifdef _WIN32 + __asm__ volatile ( + "movl %0, %%eax\n" + "movl %%eax, %%fs:0x0\n" + : + : "m" (seh) + : "eax"); +#endif + __asm__ volatile ("movl %0, %%ebx" : : "m" (ebx)); + __asm__ volatile ("movl %0, %%ebp" : : "m" (ebp)); + __asm__ volatile ("fldcw %0" : : "m" (cw)); + __asm__ volatile ("" : : : "esi", "edi"); + return err; +} + +#endif + +/* + * further self-processing support + */ + +/* + * if you want to add self-inspection tools, place them + * here. See the x86_msvc for the necessary defines. + * These features are highly experimental und not + * essential yet. + */ diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/slp_platformselect.h b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/slp_platformselect.h new file mode 100644 index 0000000..c959f0f --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/slp_platformselect.h @@ -0,0 +1,71 @@ +/* + * Platform Selection for Stackless Python + */ +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86) && defined(_MSC_VER) +# include "platform/switch_x86_msvc.h" /* MS Visual Studio on X86 */ +#elif defined(MS_WIN64) && defined(_M_X64) && defined(_MSC_VER) || defined(__MINGW64__) +# include "platform/switch_x64_msvc.h" /* MS Visual Studio on X64 */ +#elif defined(MS_WIN64) && defined(_M_ARM64) +# include "platform/switch_arm64_msvc.h" /* MS Visual Studio on ARM64 */ +#elif defined(__GNUC__) && defined(__amd64__) && defined(__ILP32__) +# include "platform/switch_x32_unix.h" /* gcc on amd64 with x32 ABI */ +#elif defined(__GNUC__) && defined(__amd64__) +# include "platform/switch_amd64_unix.h" /* gcc on amd64 */ +#elif defined(__GNUC__) && defined(__i386__) +# include "platform/switch_x86_unix.h" /* gcc on X86 */ +#elif defined(__GNUC__) && defined(__powerpc64__) && (defined(__linux__) || defined(__FreeBSD__)) +# include "platform/switch_ppc64_linux.h" /* gcc on PowerPC 64-bit */ +#elif defined(__GNUC__) && defined(__PPC__) && (defined(__linux__) || defined(__FreeBSD__)) +# include "platform/switch_ppc_linux.h" /* gcc on PowerPC */ +#elif defined(__GNUC__) && defined(__ppc__) && defined(__APPLE__) +# include "platform/switch_ppc_macosx.h" /* Apple MacOS X on PowerPC */ +#elif defined(__GNUC__) && defined(__powerpc64__) && defined(_AIX) +# include "platform/switch_ppc64_aix.h" /* gcc on AIX/PowerPC 64-bit */ +#elif defined(__GNUC__) && defined(_ARCH_PPC) && defined(_AIX) +# include "platform/switch_ppc_aix.h" /* gcc on AIX/PowerPC */ +#elif defined(__GNUC__) && defined(sparc) +# include "platform/switch_sparc_sun_gcc.h" /* SunOS sparc with gcc */ +#elif defined(__SUNPRO_C) && defined(sparc) && defined(sun) +# iiclude "platform/switch_sparc_sun_gcc.h" /* SunStudio on amd64 */ +#elif defined(__SUNPRO_C) && defined(__amd64__) && defined(sun) +# include "platform/switch_amd64_unix.h" /* SunStudio on amd64 */ +#elif defined(__SUNPRO_C) && defined(__i386__) && defined(sun) +# include "platform/switch_x86_unix.h" /* SunStudio on x86 */ +#elif defined(__GNUC__) && defined(__s390__) && defined(__linux__) +# include "platform/switch_s390_unix.h" /* Linux/S390 */ +#elif defined(__GNUC__) && defined(__s390x__) && defined(__linux__) +# include "platform/switch_s390_unix.h" /* Linux/S390 zSeries (64-bit) */ +#elif defined(__GNUC__) && defined(__arm__) +# ifdef __APPLE__ +# include +# endif +# if TARGET_OS_IPHONE +# include "platform/switch_arm32_ios.h" /* iPhone OS on arm32 */ +# else +# include "platform/switch_arm32_gcc.h" /* gcc using arm32 */ +# endif +#elif defined(__GNUC__) && defined(__mips__) && defined(__linux__) +# include "platform/switch_mips_unix.h" /* Linux/MIPS */ +#elif defined(__GNUC__) && defined(__aarch64__) +# include "platform/switch_aarch64_gcc.h" /* Aarch64 ABI */ +#elif defined(__GNUC__) && defined(__mc68000__) +# include "platform/switch_m68k_gcc.h" /* gcc on m68k */ +#elif defined(__GNUC__) && defined(__csky__) +#include "platform/switch_csky_gcc.h" /* gcc on csky */ +# elif defined(__GNUC__) && defined(__riscv) +# include "platform/switch_riscv_unix.h" /* gcc on RISC-V */ +#elif defined(__GNUC__) && defined(__alpha__) +# include "platform/switch_alpha_unix.h" /* gcc on DEC Alpha */ +#elif defined(MS_WIN32) && defined(__llvm__) && defined(__aarch64__) +# include "platform/switch_aarch64_gcc.h" /* LLVM Aarch64 ABI for Windows */ +#elif defined(__GNUC__) && defined(__loongarch64) && defined(__linux__) +# include "platform/switch_loongarch64_linux.h" /* LoongArch64 */ +#endif + +#ifdef __cplusplus +}; +#endif diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/__init__.py new file mode 100644 index 0000000..e249e35 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/__init__.py @@ -0,0 +1,237 @@ +# -*- coding: utf-8 -*- +""" +Tests for greenlet. + +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import sys +import unittest + +from gc import collect +from gc import get_objects +from threading import active_count as active_thread_count +from time import sleep +from time import time + +import psutil + +from greenlet import greenlet as RawGreenlet +from greenlet import getcurrent + +from greenlet._greenlet import get_pending_cleanup_count +from greenlet._greenlet import get_total_main_greenlets + +from . import leakcheck + +PY312 = sys.version_info[:2] >= (3, 12) +WIN = sys.platform.startswith("win") + +class TestCaseMetaClass(type): + # wrap each test method with + # a) leak checks + def __new__(cls, classname, bases, classDict): + # pylint and pep8 fight over what this should be called (mcs or cls). + # pylint gets it right, but we can't scope disable pep8, so we go with + # its convention. + # pylint: disable=bad-mcs-classmethod-argument + check_totalrefcount = True + + # Python 3: must copy, we mutate the classDict. Interestingly enough, + # it doesn't actually error out, but under 3.6 we wind up wrapping + # and re-wrapping the same items over and over and over. + for key, value in list(classDict.items()): + if key.startswith('test') and callable(value): + classDict.pop(key) + if check_totalrefcount: + value = leakcheck.wrap_refcount(value) + classDict[key] = value + return type.__new__(cls, classname, bases, classDict) + + +class TestCase(TestCaseMetaClass( + "NewBase", + (unittest.TestCase,), + {})): + + cleanup_attempt_sleep_duration = 0.001 + cleanup_max_sleep_seconds = 1 + + def wait_for_pending_cleanups(self, + initial_active_threads=None, + initial_main_greenlets=None): + initial_active_threads = initial_active_threads or self.threads_before_test + initial_main_greenlets = initial_main_greenlets or self.main_greenlets_before_test + sleep_time = self.cleanup_attempt_sleep_duration + # NOTE: This is racy! A Python-level thread object may be dead + # and gone, but the C thread may not yet have fired its + # destructors and added to the queue. There's no particular + # way to know that's about to happen. We try to watch the + # Python threads to make sure they, at least, have gone away. + # Counting the main greenlets, which we can easily do deterministically, + # also helps. + + # Always sleep at least once to let other threads run + sleep(sleep_time) + quit_after = time() + self.cleanup_max_sleep_seconds + # TODO: We could add an API that calls us back when a particular main greenlet is deleted? + # It would have to drop the GIL + while ( + get_pending_cleanup_count() + or active_thread_count() > initial_active_threads + or (not self.expect_greenlet_leak + and get_total_main_greenlets() > initial_main_greenlets)): + sleep(sleep_time) + if time() > quit_after: + print("Time limit exceeded.") + print("Threads: Waiting for only", initial_active_threads, + "-->", active_thread_count()) + print("MGlets : Waiting for only", initial_main_greenlets, + "-->", get_total_main_greenlets()) + break + collect() + + def count_objects(self, kind=list, exact_kind=True): + # pylint:disable=unidiomatic-typecheck + # Collect the garbage. + for _ in range(3): + collect() + if exact_kind: + return sum( + 1 + for x in get_objects() + if type(x) is kind + ) + # instances + return sum( + 1 + for x in get_objects() + if isinstance(x, kind) + ) + + greenlets_before_test = 0 + threads_before_test = 0 + main_greenlets_before_test = 0 + expect_greenlet_leak = False + + def count_greenlets(self): + """ + Find all the greenlets and subclasses tracked by the GC. + """ + return self.count_objects(RawGreenlet, False) + + def setUp(self): + # Ensure the main greenlet exists, otherwise the first test + # gets a false positive leak + super().setUp() + getcurrent() + self.threads_before_test = active_thread_count() + self.main_greenlets_before_test = get_total_main_greenlets() + self.wait_for_pending_cleanups(self.threads_before_test, self.main_greenlets_before_test) + self.greenlets_before_test = self.count_greenlets() + + def tearDown(self): + if getattr(self, 'skipTearDown', False): + return + + self.wait_for_pending_cleanups(self.threads_before_test, self.main_greenlets_before_test) + super().tearDown() + + def get_expected_returncodes_for_aborted_process(self): + import signal + # The child should be aborted in an unusual way. On POSIX + # platforms, this is done with abort() and signal.SIGABRT, + # which is reflected in a negative return value; however, on + # Windows, even though we observe the child print "Fatal + # Python error: Aborted" and in older versions of the C + # runtime "This application has requested the Runtime to + # terminate it in an unusual way," it always has an exit code + # of 3. This is interesting because 3 is the error code for + # ERROR_PATH_NOT_FOUND; BUT: the C runtime abort() function + # also uses this code. + # + # If we link to the static C library on Windows, the error + # code changes to '0xc0000409' (hex(3221226505)), which + # apparently is STATUS_STACK_BUFFER_OVERRUN; but "What this + # means is that nowadays when you get a + # STATUS_STACK_BUFFER_OVERRUN, it doesn’t actually mean that + # there is a stack buffer overrun. It just means that the + # application decided to terminate itself with great haste." + # + # + # On windows, we've also seen '0xc0000005' (hex(3221225477)). + # That's "Access Violation" + # + # See + # https://devblogs.microsoft.com/oldnewthing/20110519-00/?p=10623 + # and + # https://docs.microsoft.com/en-us/previous-versions/k089yyh0(v=vs.140)?redirectedfrom=MSDN + # and + # https://devblogs.microsoft.com/oldnewthing/20190108-00/?p=100655 + expected_exit = ( + -signal.SIGABRT, + # But beginning on Python 3.11, the faulthandler + # that prints the C backtraces sometimes segfaults after + # reporting the exception but before printing the stack. + # This has only been seen on linux/gcc. + -signal.SIGSEGV, + ) if not WIN else ( + 3, + 0xc0000409, + 0xc0000005, + ) + return expected_exit + + def get_process_uss(self): + """ + Return the current process's USS in bytes. + + uss is available on Linux, macOS, Windows. Also known as + "Unique Set Size", this is the memory which is unique to a + process and which would be freed if the process was terminated + right now. + + If this is not supported by ``psutil``, this raises the + :exc:`unittest.SkipTest` exception. + """ + try: + return psutil.Process().memory_full_info().uss + except AttributeError as e: + raise unittest.SkipTest("uss not supported") from e + + def run_script(self, script_name, show_output=True): + import subprocess + import os + script = os.path.join( + os.path.dirname(__file__), + script_name, + ) + + try: + return subprocess.check_output([sys.executable, script], + encoding='utf-8', + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as ex: + if show_output: + print('-----') + print('Failed to run script', script) + print('~~~~~') + print(ex.output) + print('------') + raise + + + def assertScriptRaises(self, script_name, exitcodes=None): + import subprocess + with self.assertRaises(subprocess.CalledProcessError) as exc: + output = self.run_script(script_name, show_output=False) + __traceback_info__ = output + # We're going to fail the assertion if we get here, at least + # preserve the output in the traceback. + + if exitcodes is None: + exitcodes = self.get_expected_returncodes_for_aborted_process() + self.assertIn(exc.exception.returncode, exitcodes) + return exc.exception diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension.c b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension.c new file mode 100644 index 0000000..05e81c0 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension.c @@ -0,0 +1,231 @@ +/* This is a set of functions used by test_extension_interface.py to test the + * Greenlet C API. + */ + +#include "../greenlet.h" + +#ifndef Py_RETURN_NONE +# define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None +#endif + +#define TEST_MODULE_NAME "_test_extension" + +static PyObject* +test_switch(PyObject* self, PyObject* greenlet) +{ + PyObject* result = NULL; + + if (greenlet == NULL || !PyGreenlet_Check(greenlet)) { + PyErr_BadArgument(); + return NULL; + } + + result = PyGreenlet_Switch((PyGreenlet*)greenlet, NULL, NULL); + if (result == NULL) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_AssertionError, + "greenlet.switch() failed for some reason."); + } + return NULL; + } + Py_INCREF(result); + return result; +} + +static PyObject* +test_switch_kwargs(PyObject* self, PyObject* args, PyObject* kwargs) +{ + PyGreenlet* g = NULL; + PyObject* result = NULL; + + PyArg_ParseTuple(args, "O!", &PyGreenlet_Type, &g); + + if (g == NULL || !PyGreenlet_Check(g)) { + PyErr_BadArgument(); + return NULL; + } + + result = PyGreenlet_Switch(g, NULL, kwargs); + if (result == NULL) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_AssertionError, + "greenlet.switch() failed for some reason."); + } + return NULL; + } + Py_XINCREF(result); + return result; +} + +static PyObject* +test_getcurrent(PyObject* self) +{ + PyGreenlet* g = PyGreenlet_GetCurrent(); + if (g == NULL || !PyGreenlet_Check(g) || !PyGreenlet_ACTIVE(g)) { + PyErr_SetString(PyExc_AssertionError, + "getcurrent() returned an invalid greenlet"); + Py_XDECREF(g); + return NULL; + } + Py_DECREF(g); + Py_RETURN_NONE; +} + +static PyObject* +test_setparent(PyObject* self, PyObject* arg) +{ + PyGreenlet* current; + PyGreenlet* greenlet = NULL; + + if (arg == NULL || !PyGreenlet_Check(arg)) { + PyErr_BadArgument(); + return NULL; + } + if ((current = PyGreenlet_GetCurrent()) == NULL) { + return NULL; + } + greenlet = (PyGreenlet*)arg; + if (PyGreenlet_SetParent(greenlet, current)) { + Py_DECREF(current); + return NULL; + } + Py_DECREF(current); + if (PyGreenlet_Switch(greenlet, NULL, NULL) == NULL) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject* +test_new_greenlet(PyObject* self, PyObject* callable) +{ + PyObject* result = NULL; + PyGreenlet* greenlet = PyGreenlet_New(callable, NULL); + + if (!greenlet) { + return NULL; + } + + result = PyGreenlet_Switch(greenlet, NULL, NULL); + Py_CLEAR(greenlet); + if (result == NULL) { + return NULL; + } + + Py_INCREF(result); + return result; +} + +static PyObject* +test_raise_dead_greenlet(PyObject* self) +{ + PyErr_SetString(PyExc_GreenletExit, "test GreenletExit exception."); + return NULL; +} + +static PyObject* +test_raise_greenlet_error(PyObject* self) +{ + PyErr_SetString(PyExc_GreenletError, "test greenlet.error exception"); + return NULL; +} + +static PyObject* +test_throw(PyObject* self, PyGreenlet* g) +{ + const char msg[] = "take that sucka!"; + PyObject* msg_obj = Py_BuildValue("s", msg); + PyGreenlet_Throw(g, PyExc_ValueError, msg_obj, NULL); + Py_DECREF(msg_obj); + if (PyErr_Occurred()) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject* +test_throw_exact(PyObject* self, PyObject* args) +{ + PyGreenlet* g = NULL; + PyObject* typ = NULL; + PyObject* val = NULL; + PyObject* tb = NULL; + + if (!PyArg_ParseTuple(args, "OOOO:throw", &g, &typ, &val, &tb)) { + return NULL; + } + + PyGreenlet_Throw(g, typ, val, tb); + if (PyErr_Occurred()) { + return NULL; + } + Py_RETURN_NONE; +} + +static PyMethodDef test_methods[] = { + {"test_switch", + (PyCFunction)test_switch, + METH_O, + "Switch to the provided greenlet sending provided arguments, and \n" + "return the results."}, + {"test_switch_kwargs", + (PyCFunction)test_switch_kwargs, + METH_VARARGS | METH_KEYWORDS, + "Switch to the provided greenlet sending the provided keyword args."}, + {"test_getcurrent", + (PyCFunction)test_getcurrent, + METH_NOARGS, + "Test PyGreenlet_GetCurrent()"}, + {"test_setparent", + (PyCFunction)test_setparent, + METH_O, + "Se the parent of the provided greenlet and switch to it."}, + {"test_new_greenlet", + (PyCFunction)test_new_greenlet, + METH_O, + "Test PyGreenlet_New()"}, + {"test_raise_dead_greenlet", + (PyCFunction)test_raise_dead_greenlet, + METH_NOARGS, + "Just raise greenlet.GreenletExit"}, + {"test_raise_greenlet_error", + (PyCFunction)test_raise_greenlet_error, + METH_NOARGS, + "Just raise greenlet.error"}, + {"test_throw", + (PyCFunction)test_throw, + METH_O, + "Throw a ValueError at the provided greenlet"}, + {"test_throw_exact", + (PyCFunction)test_throw_exact, + METH_VARARGS, + "Throw exactly the arguments given at the provided greenlet"}, + {NULL, NULL, 0, NULL} +}; + + +#define INITERROR return NULL + +static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, + TEST_MODULE_NAME, + NULL, + 0, + test_methods, + NULL, + NULL, + NULL, + NULL}; + +PyMODINIT_FUNC +PyInit__test_extension(void) +{ + PyObject* module = NULL; + module = PyModule_Create(&moduledef); + + if (module == NULL) { + return NULL; + } + + PyGreenlet_Import(); + return module; +} diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension.cpython-310-x86_64-linux-gnu.so b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension.cpython-310-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..b305b5c48f3e6078b03ee02b86f01af133185ea5 GIT binary patch literal 36200 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 literal 0 HcmV?d00001 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension_cpp.cpp b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension_cpp.cpp new file mode 100644 index 0000000..5cbe6a7 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension_cpp.cpp @@ -0,0 +1,226 @@ +/* This is a set of functions used to test C++ exceptions are not + * broken during greenlet switches + */ + +#include "../greenlet.h" +#include "../greenlet_compiler_compat.hpp" +#include +#include + +struct exception_t { + int depth; + exception_t(int depth) : depth(depth) {} +}; + +/* Functions are called via pointers to prevent inlining */ +static void (*p_test_exception_throw_nonstd)(int depth); +static void (*p_test_exception_throw_std)(); +static PyObject* (*p_test_exception_switch_recurse)(int depth, int left); + +static void +test_exception_throw_nonstd(int depth) +{ + throw exception_t(depth); +} + +static void +test_exception_throw_std() +{ + throw std::runtime_error("Thrown from an extension."); +} + +static PyObject* +test_exception_switch_recurse(int depth, int left) +{ + if (left > 0) { + return p_test_exception_switch_recurse(depth, left - 1); + } + + PyObject* result = NULL; + PyGreenlet* self = PyGreenlet_GetCurrent(); + if (self == NULL) + return NULL; + + try { + if (PyGreenlet_Switch(PyGreenlet_GET_PARENT(self), NULL, NULL) == NULL) { + Py_DECREF(self); + return NULL; + } + p_test_exception_throw_nonstd(depth); + PyErr_SetString(PyExc_RuntimeError, + "throwing C++ exception didn't work"); + } + catch (const exception_t& e) { + if (e.depth != depth) + PyErr_SetString(PyExc_AssertionError, "depth mismatch"); + else + result = PyLong_FromLong(depth); + } + catch (...) { + PyErr_SetString(PyExc_RuntimeError, "unexpected C++ exception"); + } + + Py_DECREF(self); + return result; +} + +/* test_exception_switch(int depth) + * - recurses depth times + * - switches to parent inside try/catch block + * - throws an exception that (expected to be caught in the same function) + * - verifies depth matches (exceptions shouldn't be caught in other greenlets) + */ +static PyObject* +test_exception_switch(PyObject* UNUSED(self), PyObject* args) +{ + int depth; + if (!PyArg_ParseTuple(args, "i", &depth)) + return NULL; + return p_test_exception_switch_recurse(depth, depth); +} + + +static PyObject* +py_test_exception_throw_nonstd(PyObject* self, PyObject* args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + p_test_exception_throw_nonstd(0); + PyErr_SetString(PyExc_AssertionError, "unreachable code running after throw"); + return NULL; +} + +static PyObject* +py_test_exception_throw_std(PyObject* self, PyObject* args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + p_test_exception_throw_std(); + PyErr_SetString(PyExc_AssertionError, "unreachable code running after throw"); + return NULL; +} + +static PyObject* +py_test_call(PyObject* self, PyObject* arg) +{ + PyObject* noargs = PyTuple_New(0); + PyObject* ret = PyObject_Call(arg, noargs, nullptr); + Py_DECREF(noargs); + return ret; +} + + + +/* test_exception_switch_and_do_in_g2(g2func) + * - creates new greenlet g2 to run g2func + * - switches to g2 inside try/catch block + * - verifies that no exception has been caught + * + * it is used together with test_exception_throw to verify that unhandled + * exceptions thrown in one greenlet do not propagate to other greenlet nor + * segfault the process. + */ +static PyObject* +test_exception_switch_and_do_in_g2(PyObject* self, PyObject* args) +{ + PyObject* g2func = NULL; + PyObject* result = NULL; + + if (!PyArg_ParseTuple(args, "O", &g2func)) + return NULL; + PyGreenlet* g2 = PyGreenlet_New(g2func, NULL); + if (!g2) { + return NULL; + } + + try { + result = PyGreenlet_Switch(g2, NULL, NULL); + if (!result) { + return NULL; + } + } + catch (const exception_t& e) { + /* if we are here the memory can be already corrupted and the program + * might crash before below py-level exception might become printed. + * -> print something to stderr to make it clear that we had entered + * this catch block. + * See comments in inner_bootstrap() + */ +#if defined(WIN32) || defined(_WIN32) + fprintf(stderr, "C++ exception unexpectedly caught in g1\n"); + PyErr_SetString(PyExc_AssertionError, "C++ exception unexpectedly caught in g1"); + Py_XDECREF(result); + return NULL; +#else + throw; +#endif + } + + Py_XDECREF(result); + Py_RETURN_NONE; +} + +static PyMethodDef test_methods[] = { + {"test_exception_switch", + (PyCFunction)&test_exception_switch, + METH_VARARGS, + "Switches to parent twice, to test exception handling and greenlet " + "switching."}, + {"test_exception_switch_and_do_in_g2", + (PyCFunction)&test_exception_switch_and_do_in_g2, + METH_VARARGS, + "Creates new greenlet g2 to run g2func and switches to it inside try/catch " + "block. Used together with test_exception_throw to verify that unhandled " + "C++ exceptions thrown in a greenlet doe not corrupt memory."}, + {"test_exception_throw_nonstd", + (PyCFunction)&py_test_exception_throw_nonstd, + METH_VARARGS, + "Throws non-standard C++ exception. Calling this function directly should abort the process." + }, + {"test_exception_throw_std", + (PyCFunction)&py_test_exception_throw_std, + METH_VARARGS, + "Throws standard C++ exception. Calling this function directly should abort the process." + }, + {"test_call", + (PyCFunction)&py_test_call, + METH_O, + "Call the given callable. Unlike calling it directly, this creates a " + "new C-level stack frame, which may be helpful in testing." + }, + {NULL, NULL, 0, NULL} +}; + + +static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, + "greenlet.tests._test_extension_cpp", + NULL, + 0, + test_methods, + NULL, + NULL, + NULL, + NULL}; + +PyMODINIT_FUNC +PyInit__test_extension_cpp(void) +{ + PyObject* module = NULL; + + module = PyModule_Create(&moduledef); + + if (module == NULL) { + return NULL; + } + + PyGreenlet_Import(); + if (_PyGreenlet_API == NULL) { + return NULL; + } + + p_test_exception_throw_nonstd = test_exception_throw_nonstd; + p_test_exception_throw_std = test_exception_throw_std; + p_test_exception_switch_recurse = test_exception_switch_recurse; + + return module; +} diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension_cpp.cpython-310-x86_64-linux-gnu.so b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/_test_extension_cpp.cpython-310-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..daa26e4e806cae7315304bedc0b2049d7b327a55 GIT binary patch literal 57264 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 vBo`;r?f7lSkIljBTHIsb1?tnWlW3j literal 0 HcmV?d00001 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_clearing_run_switches.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_clearing_run_switches.py new file mode 100644 index 0000000..6dd1492 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_clearing_run_switches.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +""" +If we have a run callable passed to the constructor or set as an +attribute, but we don't actually use that (because ``__getattribute__`` +or the like interferes), then when we clear callable before beginning +to run, there's an opportunity for Python code to run. + +""" +import greenlet + +g = None +main = greenlet.getcurrent() + +results = [] + +class RunCallable: + + def __del__(self): + results.append(('RunCallable', '__del__')) + main.switch('from RunCallable') + + +class G(greenlet.greenlet): + + def __getattribute__(self, name): + if name == 'run': + results.append(('G.__getattribute__', 'run')) + return run_func + return object.__getattribute__(self, name) + + +def run_func(): + results.append(('run_func', 'enter')) + + +g = G(RunCallable()) +# Try to start G. It will get to the point where it deletes +# its run callable C++ variable in inner_bootstrap. That triggers +# the __del__ method, which switches back to main before g +# actually even starts running. +x = g.switch() +results.append(('main: g.switch()', x)) +# In the C++ code, this results in g->g_switch() appearing to return, even though +# it has yet to run. +print('In main with', x, flush=True) +g.switch() +print('RESULTS', results) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_cpp_exception.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_cpp_exception.py new file mode 100644 index 0000000..fa4dc2e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_cpp_exception.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +""" +Helper for testing a C++ exception throw aborts the process. + +Takes one argument, the name of the function in :mod:`_test_extension_cpp` to call. +""" +import sys +import greenlet +from greenlet.tests import _test_extension_cpp +print('fail_cpp_exception is running') + +def run_unhandled_exception_in_greenlet_aborts(): + def _(): + _test_extension_cpp.test_exception_switch_and_do_in_g2( + _test_extension_cpp.test_exception_throw_nonstd + ) + g1 = greenlet.greenlet(_) + g1.switch() + + +func_name = sys.argv[1] +try: + func = getattr(_test_extension_cpp, func_name) +except AttributeError: + if func_name == run_unhandled_exception_in_greenlet_aborts.__name__: + func = run_unhandled_exception_in_greenlet_aborts + elif func_name == 'run_as_greenlet_target': + g = greenlet.greenlet(_test_extension_cpp.test_exception_throw_std) + func = g.switch + else: + raise +print('raising', func, flush=True) +func() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_initialstub_already_started.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_initialstub_already_started.py new file mode 100644 index 0000000..c1a44ef --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_initialstub_already_started.py @@ -0,0 +1,78 @@ +""" +Testing initialstub throwing an already started exception. +""" + +import greenlet + +a = None +b = None +c = None +main = greenlet.getcurrent() + +# If we switch into a dead greenlet, +# we go looking for its parents. +# if a parent is not yet started, we start it. + +results = [] + +def a_run(*args): + #results.append('A') + results.append(('Begin A', args)) + + +def c_run(): + results.append('Begin C') + b.switch('From C') + results.append('C done') + +class A(greenlet.greenlet): pass + +class B(greenlet.greenlet): + doing_it = False + def __getattribute__(self, name): + if name == 'run' and not self.doing_it: + assert greenlet.getcurrent() is c + self.doing_it = True + results.append('Switch to b from B.__getattribute__ in ' + + type(greenlet.getcurrent()).__name__) + b.switch() + results.append('B.__getattribute__ back from main in ' + + type(greenlet.getcurrent()).__name__) + if name == 'run': + name = '_B_run' + return object.__getattribute__(self, name) + + def _B_run(self, *arg): + results.append(('Begin B', arg)) + results.append('_B_run switching to main') + main.switch('From B') + +class C(greenlet.greenlet): + pass +a = A(a_run) +b = B(parent=a) +c = C(c_run, b) + +# Start a child; while running, it will start B, +# but starting B will ALSO start B. +result = c.switch() +results.append(('main from c', result)) + +# Switch back to C, which was in the middle of switching +# already. This will throw the ``GreenletStartedWhileInPython`` +# exception, which results in parent A getting started (B is finished) +c.switch() + +results.append(('A dead?', a.dead, 'B dead?', b.dead, 'C dead?', c.dead)) + +# A and B should both be dead now. +assert a.dead +assert b.dead +assert not c.dead + +result = c.switch() +results.append(('main from c.2', result)) +# Now C is dead +assert c.dead + +print("RESULTS:", results) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_slp_switch.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_slp_switch.py new file mode 100644 index 0000000..0990526 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_slp_switch.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +""" +A test helper for seeing what happens when slp_switch() +fails. +""" +# pragma: no cover + +import greenlet + + +print('fail_slp_switch is running', flush=True) + +runs = [] +def func(): + runs.append(1) + greenlet.getcurrent().parent.switch() + runs.append(2) + greenlet.getcurrent().parent.switch() + runs.append(3) + +g = greenlet._greenlet.UnswitchableGreenlet(func) +g.switch() +assert runs == [1] +g.switch() +assert runs == [1, 2] +g.force_slp_switch_error = True + +# This should crash. +g.switch() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_switch_three_greenlets.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_switch_three_greenlets.py new file mode 100644 index 0000000..e151b19 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_switch_three_greenlets.py @@ -0,0 +1,44 @@ +""" +Uses a trace function to switch greenlets at unexpected times. + +In the trace function, we switch from the current greenlet to another +greenlet, which switches +""" +import greenlet + +g1 = None +g2 = None + +switch_to_g2 = False + +def tracefunc(*args): + print('TRACE', *args) + global switch_to_g2 + if switch_to_g2: + switch_to_g2 = False + g2.switch() + print('\tLEAVE TRACE', *args) + +def g1_run(): + print('In g1_run') + global switch_to_g2 + switch_to_g2 = True + from_parent = greenlet.getcurrent().parent.switch() + print('Return to g1_run') + print('From parent', from_parent) + +def g2_run(): + #g1.switch() + greenlet.getcurrent().parent.switch() + +greenlet.settrace(tracefunc) + +g1 = greenlet.greenlet(g1_run) +g2 = greenlet.greenlet(g2_run) + +# This switch didn't actually finish! +# And if it did, it would raise TypeError +# because g1_run() doesn't take any arguments. +g1.switch(1) +print('Back in main') +g1.switch(2) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_switch_three_greenlets2.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_switch_three_greenlets2.py new file mode 100644 index 0000000..1f6b66b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_switch_three_greenlets2.py @@ -0,0 +1,55 @@ +""" +Like fail_switch_three_greenlets, but the call into g1_run would actually be +valid. +""" +import greenlet + +g1 = None +g2 = None + +switch_to_g2 = True + +results = [] + +def tracefunc(*args): + results.append(('trace', args[0])) + print('TRACE', *args) + global switch_to_g2 + if switch_to_g2: + switch_to_g2 = False + g2.switch('g2 from tracefunc') + print('\tLEAVE TRACE', *args) + +def g1_run(arg): + results.append(('g1 arg', arg)) + print('In g1_run') + from_parent = greenlet.getcurrent().parent.switch('from g1_run') + results.append(('g1 from parent', from_parent)) + return 'g1 done' + +def g2_run(arg): + #g1.switch() + results.append(('g2 arg', arg)) + parent = greenlet.getcurrent().parent.switch('from g2_run') + global switch_to_g2 + switch_to_g2 = False + results.append(('g2 from parent', parent)) + return 'g2 done' + + +greenlet.settrace(tracefunc) + +g1 = greenlet.greenlet(g1_run) +g2 = greenlet.greenlet(g2_run) + +x = g1.switch('g1 from main') +results.append(('main g1', x)) +print('Back in main', x) +x = g1.switch('g2 from main') +results.append(('main g2', x)) +print('back in amain again', x) +x = g1.switch('g1 from main 2') +results.append(('main g1.2', x)) +x = g2.switch() +results.append(('main g2.2', x)) +print("RESULTS:", results) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_switch_two_greenlets.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_switch_two_greenlets.py new file mode 100644 index 0000000..3e52345 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/fail_switch_two_greenlets.py @@ -0,0 +1,41 @@ +""" +Uses a trace function to switch greenlets at unexpected times. + +In the trace function, we switch from the current greenlet to another +greenlet, which switches +""" +import greenlet + +g1 = None +g2 = None + +switch_to_g2 = False + +def tracefunc(*args): + print('TRACE', *args) + global switch_to_g2 + if switch_to_g2: + switch_to_g2 = False + g2.switch() + print('\tLEAVE TRACE', *args) + +def g1_run(): + print('In g1_run') + global switch_to_g2 + switch_to_g2 = True + greenlet.getcurrent().parent.switch() + print('Return to g1_run') + print('Falling off end of g1_run') + +def g2_run(): + g1.switch() + print('Falling off end of g2') + +greenlet.settrace(tracefunc) + +g1 = greenlet.greenlet(g1_run) +g2 = greenlet.greenlet(g2_run) + +g1.switch() +print('Falling off end of main') +g2.switch() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/leakcheck.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/leakcheck.py new file mode 100644 index 0000000..a5152fb --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/leakcheck.py @@ -0,0 +1,319 @@ +# Copyright (c) 2018 gevent community +# Copyright (c) 2021 greenlet community +# +# This was originally part of gevent's test suite. The main author +# (Jason Madden) vendored a copy of it into greenlet. +# +# 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. +from __future__ import print_function + +import os +import sys +import gc + +from functools import wraps +import unittest + + +import objgraph + +# graphviz 0.18 (Nov 7 2021), available only on Python 3.6 and newer, +# has added type hints (sigh). It wants to use ``typing.Literal`` for +# some stuff, but that's only available on Python 3.9+. If that's not +# found, it creates a ``unittest.mock.MagicMock`` object and annotates +# with that. These are GC'able objects, and doing almost *anything* +# with them results in an explosion of objects. For example, trying to +# compare them for equality creates new objects. This causes our +# leakchecks to fail, with reports like: +# +# greenlet.tests.leakcheck.LeakCheckError: refcount increased by [337, 1333, 343, 430, 530, 643, 769] +# _Call 1820 +546 +# dict 4094 +76 +# MagicProxy 585 +73 +# tuple 2693 +66 +# _CallList 24 +3 +# weakref 1441 +1 +# function 5996 +1 +# type 736 +1 +# cell 592 +1 +# MagicMock 8 +1 +# +# To avoid this, we *could* filter this type of object out early. In +# principle it could leak, but we don't use mocks in greenlet, so it +# doesn't leak from us. However, a further issue is that ``MagicMock`` +# objects have subobjects that are also GC'able, like ``_Call``, and +# those create new mocks of their own too. So we'd have to filter them +# as well, and they're not public. That's OK, we can workaround the +# problem by being very careful to never compare by equality or other +# user-defined operators, only using object identity or other builtin +# functions. + +RUNNING_ON_GITHUB_ACTIONS = os.environ.get('GITHUB_ACTIONS') +RUNNING_ON_TRAVIS = os.environ.get('TRAVIS') or RUNNING_ON_GITHUB_ACTIONS +RUNNING_ON_APPVEYOR = os.environ.get('APPVEYOR') +RUNNING_ON_CI = RUNNING_ON_TRAVIS or RUNNING_ON_APPVEYOR +RUNNING_ON_MANYLINUX = os.environ.get('GREENLET_MANYLINUX') +SKIP_LEAKCHECKS = RUNNING_ON_MANYLINUX or os.environ.get('GREENLET_SKIP_LEAKCHECKS') +SKIP_FAILING_LEAKCHECKS = os.environ.get('GREENLET_SKIP_FAILING_LEAKCHECKS') +ONLY_FAILING_LEAKCHECKS = os.environ.get('GREENLET_ONLY_FAILING_LEAKCHECKS') + +def ignores_leakcheck(func): + """ + Ignore the given object during leakchecks. + + Can be applied to a method, in which case the method will run, but + will not be subject to leak checks. + + If applied to a class, the entire class will be skipped during leakchecks. This + is intended to be used for classes that are very slow and cause problems such as + test timeouts; typically it will be used for classes that are subclasses of a base + class and specify variants of behaviour (such as pool sizes). + """ + func.ignore_leakcheck = True + return func + +def fails_leakcheck(func): + """ + Mark that the function is known to leak. + """ + func.fails_leakcheck = True + if SKIP_FAILING_LEAKCHECKS: + func = unittest.skip("Skipping known failures")(func) + return func + +class LeakCheckError(AssertionError): + pass + +if hasattr(sys, 'getobjects'): + # In a Python build with ``--with-trace-refs``, make objgraph + # trace *all* the objects, not just those that are tracked by the + # GC + class _MockGC(object): + def get_objects(self): + return sys.getobjects(0) # pylint:disable=no-member + def __getattr__(self, name): + return getattr(gc, name) + objgraph.gc = _MockGC() + fails_strict_leakcheck = fails_leakcheck +else: + def fails_strict_leakcheck(func): + """ + Decorator for a function that is known to fail when running + strict (``sys.getobjects()``) leakchecks. + + This type of leakcheck finds all objects, even those, such as + strings, which are not tracked by the garbage collector. + """ + return func + +class ignores_types_in_strict_leakcheck(object): + def __init__(self, types): + self.types = types + def __call__(self, func): + func.leakcheck_ignore_types = self.types + return func + +class _RefCountChecker(object): + + # Some builtin things that we ignore + # XXX: Those things were ignored by gevent, but they're important here, + # presumably. + IGNORED_TYPES = () #(tuple, dict, types.FrameType, types.TracebackType) + + def __init__(self, testcase, function): + self.testcase = testcase + self.function = function + self.deltas = [] + self.peak_stats = {} + self.ignored_types = () + + # The very first time we are called, we have already been + # self.setUp() by the test runner, so we don't need to do it again. + self.needs_setUp = False + + def _include_object_p(self, obj): + # pylint:disable=too-many-return-statements + # + # See the comment block at the top. We must be careful to + # avoid invoking user-defined operations. + if obj is self: + return False + kind = type(obj) + # ``self._include_object_p == obj`` returns NotImplemented + # for non-function objects, which causes the interpreter + # to try to reverse the order of arguments...which leads + # to the explosion of mock objects. We don't want that, so we implement + # the check manually. + if kind == type(self._include_object_p): + try: + # pylint:disable=not-callable + exact_method_equals = self._include_object_p.__eq__(obj) + except AttributeError: + # Python 2.7 methods may only have __cmp__, and that raises a + # TypeError for non-method arguments + # pylint:disable=no-member + exact_method_equals = self._include_object_p.__cmp__(obj) == 0 + + if exact_method_equals is not NotImplemented and exact_method_equals: + return False + + # Similarly, we need to check identity in our __dict__ to avoid mock explosions. + for x in self.__dict__.values(): + if obj is x: + return False + + + if kind in self.ignored_types or kind in self.IGNORED_TYPES: + return False + + return True + + def _growth(self): + return objgraph.growth(limit=None, peak_stats=self.peak_stats, + filter=self._include_object_p) + + def _report_diff(self, growth): + if not growth: + return "" + + lines = [] + width = max(len(name) for name, _, _ in growth) + for name, count, delta in growth: + lines.append('%-*s%9d %+9d' % (width, name, count, delta)) + + diff = '\n'.join(lines) + return diff + + + def _run_test(self, args, kwargs): + gc_enabled = gc.isenabled() + gc.disable() + + if self.needs_setUp: + self.testcase.setUp() + self.testcase.skipTearDown = False + try: + self.function(self.testcase, *args, **kwargs) + finally: + self.testcase.tearDown() + self.testcase.doCleanups() + self.testcase.skipTearDown = True + self.needs_setUp = True + if gc_enabled: + gc.enable() + + def _growth_after(self): + # Grab post snapshot + # pylint:disable=no-member + if 'urlparse' in sys.modules: + sys.modules['urlparse'].clear_cache() + if 'urllib.parse' in sys.modules: + sys.modules['urllib.parse'].clear_cache() + + return self._growth() + + def _check_deltas(self, growth): + # Return false when we have decided there is no leak, + # true if we should keep looping, raises an assertion + # if we have decided there is a leak. + + deltas = self.deltas + if not deltas: + # We haven't run yet, no data, keep looping + return True + + if gc.garbage: + raise LeakCheckError("Generated uncollectable garbage %r" % (gc.garbage,)) + + + # the following configurations are classified as "no leak" + # [0, 0] + # [x, 0, 0] + # [... a, b, c, d] where a+b+c+d = 0 + # + # the following configurations are classified as "leak" + # [... z, z, z] where z > 0 + + if deltas[-2:] == [0, 0] and len(deltas) in (2, 3): + return False + + if deltas[-3:] == [0, 0, 0]: + return False + + if len(deltas) >= 4 and sum(deltas[-4:]) == 0: + return False + + if len(deltas) >= 3 and deltas[-1] > 0 and deltas[-1] == deltas[-2] and deltas[-2] == deltas[-3]: + diff = self._report_diff(growth) + raise LeakCheckError('refcount increased by %r\n%s' % (deltas, diff)) + + # OK, we don't know for sure yet. Let's search for more + if sum(deltas[-3:]) <= 0 or sum(deltas[-4:]) <= 0 or deltas[-4:].count(0) >= 2: + # this is suspicious, so give a few more runs + limit = 11 + else: + limit = 7 + if len(deltas) >= limit: + raise LeakCheckError('refcount increased by %r\n%s' + % (deltas, + self._report_diff(growth))) + + # We couldn't decide yet, keep going + return True + + def __call__(self, args, kwargs): + for _ in range(3): + gc.collect() + + expect_failure = getattr(self.function, 'fails_leakcheck', False) + if expect_failure: + self.testcase.expect_greenlet_leak = True + self.ignored_types = getattr(self.function, "leakcheck_ignore_types", ()) + + # Capture state before; the incremental will be + # updated by each call to _growth_after + growth = self._growth() + + try: + while self._check_deltas(growth): + self._run_test(args, kwargs) + + growth = self._growth_after() + + self.deltas.append(sum((stat[2] for stat in growth))) + except LeakCheckError: + if not expect_failure: + raise + else: + if expect_failure: + raise LeakCheckError("Expected %s to leak but it did not." % (self.function,)) + +def wrap_refcount(method): + if getattr(method, 'ignore_leakcheck', False) or SKIP_LEAKCHECKS: + return method + + @wraps(method) + def wrapper(self, *args, **kwargs): # pylint:disable=too-many-branches + if getattr(self, 'ignore_leakcheck', False): + raise unittest.SkipTest("This class ignored during leakchecks") + if ONLY_FAILING_LEAKCHECKS and not getattr(method, 'fails_leakcheck', False): + raise unittest.SkipTest("Only running tests that fail leakchecks.") + return _RefCountChecker(self, method)(args, kwargs) + + return wrapper diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_contextvars.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_contextvars.py new file mode 100644 index 0000000..9a16f67 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_contextvars.py @@ -0,0 +1,310 @@ +from __future__ import print_function + +import gc +import sys +import unittest + +from functools import partial +from unittest import skipUnless +from unittest import skipIf + +from greenlet import greenlet +from greenlet import getcurrent +from . import TestCase + + +try: + from contextvars import Context + from contextvars import ContextVar + from contextvars import copy_context + # From the documentation: + # + # Important: Context Variables should be created at the top module + # level and never in closures. Context objects hold strong + # references to context variables which prevents context variables + # from being properly garbage collected. + ID_VAR = ContextVar("id", default=None) + VAR_VAR = ContextVar("var", default=None) + ContextVar = None +except ImportError: + Context = ContextVar = copy_context = None + +# We don't support testing if greenlet's built-in context var support is disabled. +@skipUnless(Context is not None, "ContextVar not supported") +class ContextVarsTests(TestCase): + def _new_ctx_run(self, *args, **kwargs): + return copy_context().run(*args, **kwargs) + + def _increment(self, greenlet_id, callback, counts, expect): + ctx_var = ID_VAR + if expect is None: + self.assertIsNone(ctx_var.get()) + else: + self.assertEqual(ctx_var.get(), expect) + ctx_var.set(greenlet_id) + for _ in range(2): + counts[ctx_var.get()] += 1 + callback() + + def _test_context(self, propagate_by): + # pylint:disable=too-many-branches + ID_VAR.set(0) + + callback = getcurrent().switch + counts = dict((i, 0) for i in range(5)) + + lets = [ + greenlet(partial( + partial( + copy_context().run, + self._increment + ) if propagate_by == "run" else self._increment, + greenlet_id=i, + callback=callback, + counts=counts, + expect=( + i - 1 if propagate_by == "share" else + 0 if propagate_by in ("set", "run") else None + ) + )) + for i in range(1, 5) + ] + + for let in lets: + if propagate_by == "set": + let.gr_context = copy_context() + elif propagate_by == "share": + let.gr_context = getcurrent().gr_context + + for i in range(2): + counts[ID_VAR.get()] += 1 + for let in lets: + let.switch() + + if propagate_by == "run": + # Must leave each context.run() in reverse order of entry + for let in reversed(lets): + let.switch() + else: + # No context.run(), so fine to exit in any order. + for let in lets: + let.switch() + + for let in lets: + self.assertTrue(let.dead) + # When using run(), we leave the run() as the greenlet dies, + # and there's no context "underneath". When not using run(), + # gr_context still reflects the context the greenlet was + # running in. + if propagate_by == 'run': + self.assertIsNone(let.gr_context) + else: + self.assertIsNotNone(let.gr_context) + + + if propagate_by == "share": + self.assertEqual(counts, {0: 1, 1: 1, 2: 1, 3: 1, 4: 6}) + else: + self.assertEqual(set(counts.values()), set([2])) + + def test_context_propagated_by_context_run(self): + self._new_ctx_run(self._test_context, "run") + + def test_context_propagated_by_setting_attribute(self): + self._new_ctx_run(self._test_context, "set") + + def test_context_not_propagated(self): + self._new_ctx_run(self._test_context, None) + + def test_context_shared(self): + self._new_ctx_run(self._test_context, "share") + + def test_break_ctxvars(self): + let1 = greenlet(copy_context().run) + let2 = greenlet(copy_context().run) + let1.switch(getcurrent().switch) + let2.switch(getcurrent().switch) + # Since let2 entered the current context and let1 exits its own, the + # interpreter emits: + # RuntimeError: cannot exit context: thread state references a different context object + let1.switch() + + def test_not_broken_if_using_attribute_instead_of_context_run(self): + let1 = greenlet(getcurrent().switch) + let2 = greenlet(getcurrent().switch) + let1.gr_context = copy_context() + let2.gr_context = copy_context() + let1.switch() + let2.switch() + let1.switch() + let2.switch() + + def test_context_assignment_while_running(self): + # pylint:disable=too-many-statements + ID_VAR.set(None) + + def target(): + self.assertIsNone(ID_VAR.get()) + self.assertIsNone(gr.gr_context) + + # Context is created on first use + ID_VAR.set(1) + self.assertIsInstance(gr.gr_context, Context) + self.assertEqual(ID_VAR.get(), 1) + self.assertEqual(gr.gr_context[ID_VAR], 1) + + # Clearing the context makes it get re-created as another + # empty context when next used + old_context = gr.gr_context + gr.gr_context = None # assign None while running + self.assertIsNone(ID_VAR.get()) + self.assertIsNone(gr.gr_context) + ID_VAR.set(2) + self.assertIsInstance(gr.gr_context, Context) + self.assertEqual(ID_VAR.get(), 2) + self.assertEqual(gr.gr_context[ID_VAR], 2) + + new_context = gr.gr_context + getcurrent().parent.switch((old_context, new_context)) + # parent switches us back to old_context + + self.assertEqual(ID_VAR.get(), 1) + gr.gr_context = new_context # assign non-None while running + self.assertEqual(ID_VAR.get(), 2) + + getcurrent().parent.switch() + # parent switches us back to no context + self.assertIsNone(ID_VAR.get()) + self.assertIsNone(gr.gr_context) + gr.gr_context = old_context + self.assertEqual(ID_VAR.get(), 1) + + getcurrent().parent.switch() + # parent switches us back to no context + self.assertIsNone(ID_VAR.get()) + self.assertIsNone(gr.gr_context) + + gr = greenlet(target) + + with self.assertRaisesRegex(AttributeError, "can't delete context attribute"): + del gr.gr_context + + self.assertIsNone(gr.gr_context) + old_context, new_context = gr.switch() + self.assertIs(new_context, gr.gr_context) + self.assertEqual(old_context[ID_VAR], 1) + self.assertEqual(new_context[ID_VAR], 2) + self.assertEqual(new_context.run(ID_VAR.get), 2) + gr.gr_context = old_context # assign non-None while suspended + gr.switch() + self.assertIs(gr.gr_context, new_context) + gr.gr_context = None # assign None while suspended + gr.switch() + self.assertIs(gr.gr_context, old_context) + gr.gr_context = None + gr.switch() + self.assertIsNone(gr.gr_context) + + # Make sure there are no reference leaks + gr = None + gc.collect() + self.assertEqual(sys.getrefcount(old_context), 2) + self.assertEqual(sys.getrefcount(new_context), 2) + + def test_context_assignment_different_thread(self): + import threading + VAR_VAR.set(None) + ctx = Context() + + is_running = threading.Event() + should_suspend = threading.Event() + did_suspend = threading.Event() + should_exit = threading.Event() + holder = [] + + def greenlet_in_thread_fn(): + VAR_VAR.set(1) + is_running.set() + should_suspend.wait(10) + VAR_VAR.set(2) + getcurrent().parent.switch() + holder.append(VAR_VAR.get()) + + def thread_fn(): + gr = greenlet(greenlet_in_thread_fn) + gr.gr_context = ctx + holder.append(gr) + gr.switch() + did_suspend.set() + should_exit.wait(10) + gr.switch() + del gr + greenlet() # trigger cleanup + + thread = threading.Thread(target=thread_fn, daemon=True) + thread.start() + is_running.wait(10) + gr = holder[0] + + # Can't access or modify context if the greenlet is running + # in a different thread + with self.assertRaisesRegex(ValueError, "running in a different"): + getattr(gr, 'gr_context') + with self.assertRaisesRegex(ValueError, "running in a different"): + gr.gr_context = None + + should_suspend.set() + did_suspend.wait(10) + + # OK to access and modify context if greenlet is suspended + self.assertIs(gr.gr_context, ctx) + self.assertEqual(gr.gr_context[VAR_VAR], 2) + gr.gr_context = None + + should_exit.set() + thread.join(10) + + self.assertEqual(holder, [gr, None]) + + # Context can still be accessed/modified when greenlet is dead: + self.assertIsNone(gr.gr_context) + gr.gr_context = ctx + self.assertIs(gr.gr_context, ctx) + + # Otherwise we leak greenlets on some platforms. + # XXX: Should be able to do this automatically + del holder[:] + gr = None + thread = None + + def test_context_assignment_wrong_type(self): + g = greenlet() + with self.assertRaisesRegex(TypeError, + "greenlet context must be a contextvars.Context or None"): + g.gr_context = self + + +@skipIf(Context is not None, "ContextVar supported") +class NoContextVarsTests(TestCase): + def test_contextvars_errors(self): + let1 = greenlet(getcurrent().switch) + self.assertFalse(hasattr(let1, 'gr_context')) + with self.assertRaises(AttributeError): + getattr(let1, 'gr_context') + + with self.assertRaises(AttributeError): + let1.gr_context = None + + let1.switch() + + with self.assertRaises(AttributeError): + getattr(let1, 'gr_context') + + with self.assertRaises(AttributeError): + let1.gr_context = None + + del let1 + + +if __name__ == '__main__': + unittest.main() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_cpp.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_cpp.py new file mode 100644 index 0000000..2d0cc9c --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_cpp.py @@ -0,0 +1,73 @@ +from __future__ import print_function +from __future__ import absolute_import + +import subprocess +import unittest + +import greenlet +from . import _test_extension_cpp +from . import TestCase +from . import WIN + +class CPPTests(TestCase): + def test_exception_switch(self): + greenlets = [] + for i in range(4): + g = greenlet.greenlet(_test_extension_cpp.test_exception_switch) + g.switch(i) + greenlets.append(g) + for i, g in enumerate(greenlets): + self.assertEqual(g.switch(), i) + + def _do_test_unhandled_exception(self, target): + import os + import sys + script = os.path.join( + os.path.dirname(__file__), + 'fail_cpp_exception.py', + ) + args = [sys.executable, script, target.__name__ if not isinstance(target, str) else target] + __traceback_info__ = args + with self.assertRaises(subprocess.CalledProcessError) as exc: + subprocess.check_output( + args, + encoding='utf-8', + stderr=subprocess.STDOUT + ) + + ex = exc.exception + expected_exit = self.get_expected_returncodes_for_aborted_process() + self.assertIn(ex.returncode, expected_exit) + self.assertIn('fail_cpp_exception is running', ex.output) + return ex.output + + + def test_unhandled_nonstd_exception_aborts(self): + # verify that plain unhandled throw aborts + self._do_test_unhandled_exception(_test_extension_cpp.test_exception_throw_nonstd) + + def test_unhandled_std_exception_aborts(self): + # verify that plain unhandled throw aborts + self._do_test_unhandled_exception(_test_extension_cpp.test_exception_throw_std) + + @unittest.skipIf(WIN, "XXX: This does not crash on Windows") + # Meaning the exception is getting lost somewhere... + def test_unhandled_std_exception_as_greenlet_function_aborts(self): + # verify that plain unhandled throw aborts + output = self._do_test_unhandled_exception('run_as_greenlet_target') + self.assertIn( + # We really expect this to be prefixed with "greenlet: Unhandled C++ exception:" + # as added by our handler for std::exception (see TUserGreenlet.cpp), but + # that's not correct everywhere --- our handler never runs before std::terminate + # gets called (for example, on arm32). + 'Thrown from an extension.', + output + ) + + def test_unhandled_exception_in_greenlet_aborts(self): + # verify that unhandled throw called in greenlet aborts too + self._do_test_unhandled_exception('run_unhandled_exception_in_greenlet_aborts') + + +if __name__ == '__main__': + unittest.main() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_extension_interface.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_extension_interface.py new file mode 100644 index 0000000..34b6656 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_extension_interface.py @@ -0,0 +1,115 @@ +from __future__ import print_function +from __future__ import absolute_import + +import sys + +import greenlet +from . import _test_extension +from . import TestCase + +# pylint:disable=c-extension-no-member + +class CAPITests(TestCase): + def test_switch(self): + self.assertEqual( + 50, _test_extension.test_switch(greenlet.greenlet(lambda: 50))) + + def test_switch_kwargs(self): + def adder(x, y): + return x * y + g = greenlet.greenlet(adder) + self.assertEqual(6, _test_extension.test_switch_kwargs(g, x=3, y=2)) + + def test_setparent(self): + # pylint:disable=disallowed-name + def foo(): + def bar(): + greenlet.getcurrent().parent.switch() + + # This final switch should go back to the main greenlet, since + # the test_setparent() function in the C extension should have + # reparented this greenlet. + greenlet.getcurrent().parent.switch() + raise AssertionError("Should never have reached this code") + child = greenlet.greenlet(bar) + child.switch() + greenlet.getcurrent().parent.switch(child) + greenlet.getcurrent().parent.throw( + AssertionError("Should never reach this code")) + foo_child = greenlet.greenlet(foo).switch() + self.assertEqual(None, _test_extension.test_setparent(foo_child)) + + def test_getcurrent(self): + _test_extension.test_getcurrent() + + def test_new_greenlet(self): + self.assertEqual(-15, _test_extension.test_new_greenlet(lambda: -15)) + + def test_raise_greenlet_dead(self): + self.assertRaises( + greenlet.GreenletExit, _test_extension.test_raise_dead_greenlet) + + def test_raise_greenlet_error(self): + self.assertRaises( + greenlet.error, _test_extension.test_raise_greenlet_error) + + def test_throw(self): + seen = [] + + def foo(): # pylint:disable=disallowed-name + try: + greenlet.getcurrent().parent.switch() + except ValueError: + seen.append(sys.exc_info()[1]) + except greenlet.GreenletExit: + raise AssertionError + g = greenlet.greenlet(foo) + g.switch() + _test_extension.test_throw(g) + self.assertEqual(len(seen), 1) + self.assertTrue( + isinstance(seen[0], ValueError), + "ValueError was not raised in foo()") + self.assertEqual( + str(seen[0]), + 'take that sucka!', + "message doesn't match") + + def test_non_traceback_param(self): + with self.assertRaises(TypeError) as exc: + _test_extension.test_throw_exact( + greenlet.getcurrent(), + Exception, + Exception(), + self + ) + self.assertEqual(str(exc.exception), + "throw() third argument must be a traceback object") + + def test_instance_of_wrong_type(self): + with self.assertRaises(TypeError) as exc: + _test_extension.test_throw_exact( + greenlet.getcurrent(), + Exception(), + BaseException(), + None, + ) + + self.assertEqual(str(exc.exception), + "instance exception may not have a separate value") + + def test_not_throwable(self): + with self.assertRaises(TypeError) as exc: + _test_extension.test_throw_exact( + greenlet.getcurrent(), + "abc", + None, + None, + ) + self.assertEqual(str(exc.exception), + "exceptions must be classes, or instances, not str") + + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_gc.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_gc.py new file mode 100644 index 0000000..994addb --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_gc.py @@ -0,0 +1,86 @@ +import gc + +import weakref + +import greenlet + + +from . import TestCase +from .leakcheck import fails_leakcheck +# These only work with greenlet gc support +# which is no longer optional. +assert greenlet.GREENLET_USE_GC + +class GCTests(TestCase): + def test_dead_circular_ref(self): + o = weakref.ref(greenlet.greenlet(greenlet.getcurrent).switch()) + gc.collect() + if o() is not None: + import sys + print("O IS NOT NONE.", sys.getrefcount(o())) + self.assertIsNone(o()) + self.assertFalse(gc.garbage, gc.garbage) + + def test_circular_greenlet(self): + class circular_greenlet(greenlet.greenlet): + self = None + o = circular_greenlet() + o.self = o + o = weakref.ref(o) + gc.collect() + self.assertIsNone(o()) + self.assertFalse(gc.garbage, gc.garbage) + + def test_inactive_ref(self): + class inactive_greenlet(greenlet.greenlet): + def __init__(self): + greenlet.greenlet.__init__(self, run=self.run) + + def run(self): + pass + o = inactive_greenlet() + o = weakref.ref(o) + gc.collect() + self.assertIsNone(o()) + self.assertFalse(gc.garbage, gc.garbage) + + @fails_leakcheck + def test_finalizer_crash(self): + # This test is designed to crash when active greenlets + # are made garbage collectable, until the underlying + # problem is resolved. How does it work: + # - order of object creation is important + # - array is created first, so it is moved to unreachable first + # - we create a cycle between a greenlet and this array + # - we create an object that participates in gc, is only + # referenced by a greenlet, and would corrupt gc lists + # on destruction, the easiest is to use an object with + # a finalizer + # - because array is the first object in unreachable it is + # cleared first, which causes all references to greenlet + # to disappear and causes greenlet to be destroyed, but since + # it is still live it causes a switch during gc, which causes + # an object with finalizer to be destroyed, which causes stack + # corruption and then a crash + + class object_with_finalizer(object): + def __del__(self): + pass + array = [] + parent = greenlet.getcurrent() + def greenlet_body(): + greenlet.getcurrent().object = object_with_finalizer() + try: + parent.switch() + except greenlet.GreenletExit: + print("Got greenlet exit!") + finally: + del greenlet.getcurrent().object + g = greenlet.greenlet(greenlet_body) + g.array = array + array.append(g) + g.switch() + del array + del g + greenlet.getcurrent() + gc.collect() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_generator.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_generator.py new file mode 100644 index 0000000..ca4a644 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_generator.py @@ -0,0 +1,59 @@ + +from greenlet import greenlet + +from . import TestCase + +class genlet(greenlet): + parent = None + def __init__(self, *args, **kwds): + self.args = args + self.kwds = kwds + + def run(self): + fn, = self.fn + fn(*self.args, **self.kwds) + + def __iter__(self): + return self + + def __next__(self): + self.parent = greenlet.getcurrent() + result = self.switch() + if self: + return result + + raise StopIteration + + next = __next__ + + +def Yield(value): + g = greenlet.getcurrent() + while not isinstance(g, genlet): + if g is None: + raise RuntimeError('yield outside a genlet') + g = g.parent + g.parent.switch(value) + + +def generator(func): + class Generator(genlet): + fn = (func,) + return Generator + +# ____________________________________________________________ + + +class GeneratorTests(TestCase): + def test_generator(self): + seen = [] + + def g(n): + for i in range(n): + seen.append(i) + Yield(i) + g = generator(g) + for _ in range(3): + for j in g(5): + seen.append(j) + self.assertEqual(seen, 3 * [0, 0, 1, 1, 2, 2, 3, 3, 4, 4]) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_generator_nested.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_generator_nested.py new file mode 100644 index 0000000..8d752a6 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_generator_nested.py @@ -0,0 +1,168 @@ + +from greenlet import greenlet +from . import TestCase +from .leakcheck import fails_leakcheck + +class genlet(greenlet): + parent = None + def __init__(self, *args, **kwds): + self.args = args + self.kwds = kwds + self.child = None + + def run(self): + # Note the function is packed in a tuple + # to avoid creating a bound method for it. + fn, = self.fn + fn(*self.args, **self.kwds) + + def __iter__(self): + return self + + def set_child(self, child): + self.child = child + + def __next__(self): + if self.child: + child = self.child + while child.child: + tmp = child + child = child.child + tmp.child = None + + result = child.switch() + else: + self.parent = greenlet.getcurrent() + result = self.switch() + + if self: + return result + + raise StopIteration + + next = __next__ + +def Yield(value, level=1): + g = greenlet.getcurrent() + + while level != 0: + if not isinstance(g, genlet): + raise RuntimeError('yield outside a genlet') + if level > 1: + g.parent.set_child(g) + g = g.parent + level -= 1 + + g.switch(value) + + +def Genlet(func): + class TheGenlet(genlet): + fn = (func,) + return TheGenlet + +# ____________________________________________________________ + + +def g1(n, seen): + for i in range(n): + seen.append(i + 1) + yield i + + +def g2(n, seen): + for i in range(n): + seen.append(i + 1) + Yield(i) + +g2 = Genlet(g2) + + +def nested(i): + Yield(i) + + +def g3(n, seen): + for i in range(n): + seen.append(i + 1) + nested(i) +g3 = Genlet(g3) + + +def a(n): + if n == 0: + return + for ii in ax(n - 1): + Yield(ii) + Yield(n) +ax = Genlet(a) + + +def perms(l): + if len(l) > 1: + for e in l: + # No syntactical sugar for generator expressions + x = [Yield([e] + p) for p in perms([x for x in l if x != e])] + assert x + else: + Yield(l) +perms = Genlet(perms) + + +def gr1(n): + for ii in range(1, n): + Yield(ii) + Yield(ii * ii, 2) + +gr1 = Genlet(gr1) + + +def gr2(n, seen): + for ii in gr1(n): + seen.append(ii) + +gr2 = Genlet(gr2) + + +class NestedGeneratorTests(TestCase): + def test_layered_genlets(self): + seen = [] + for ii in gr2(5, seen): + seen.append(ii) + self.assertEqual(seen, [1, 1, 2, 4, 3, 9, 4, 16]) + + @fails_leakcheck + def test_permutations(self): + gen_perms = perms(list(range(4))) + permutations = list(gen_perms) + self.assertEqual(len(permutations), 4 * 3 * 2 * 1) + self.assertIn([0, 1, 2, 3], permutations) + self.assertIn([3, 2, 1, 0], permutations) + res = [] + for ii in zip(perms(list(range(4))), perms(list(range(3)))): + res.append(ii) + self.assertEqual( + res, + [([0, 1, 2, 3], [0, 1, 2]), ([0, 1, 3, 2], [0, 2, 1]), + ([0, 2, 1, 3], [1, 0, 2]), ([0, 2, 3, 1], [1, 2, 0]), + ([0, 3, 1, 2], [2, 0, 1]), ([0, 3, 2, 1], [2, 1, 0])]) + # XXX Test to make sure we are working as a generator expression + + def test_genlet_simple(self): + for g in g1, g2, g3: + seen = [] + for _ in range(3): + for j in g(5, seen): + seen.append(j) + self.assertEqual(seen, 3 * [1, 0, 2, 1, 3, 2, 4, 3, 5, 4]) + + def test_genlet_bad(self): + try: + Yield(10) + except RuntimeError: + pass + + def test_nested_genlets(self): + seen = [] + for ii in ax(5): + seen.append(ii) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_greenlet.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_greenlet.py new file mode 100644 index 0000000..51849cd --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_greenlet.py @@ -0,0 +1,1311 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import gc +import sys +import time +import threading + +from abc import ABCMeta, abstractmethod + +import greenlet +from greenlet import greenlet as RawGreenlet +from . import TestCase +from .leakcheck import fails_leakcheck + + +# We manually manage locks in many tests +# pylint:disable=consider-using-with +# pylint:disable=too-many-public-methods +# This module is quite large. +# TODO: Refactor into separate test files. For example, +# put all the regression tests that used to produce +# crashes in test_greenlet_no_crash; put tests that DO deliberately crash +# the interpreter into test_greenlet_crash. +# pylint:disable=too-many-lines + +class SomeError(Exception): + pass + + +def fmain(seen): + try: + greenlet.getcurrent().parent.switch() + except: + seen.append(sys.exc_info()[0]) + raise + raise SomeError + + +def send_exception(g, exc): + # note: send_exception(g, exc) can be now done with g.throw(exc). + # the purpose of this test is to explicitly check the propagation rules. + def crasher(exc): + raise exc + g1 = RawGreenlet(crasher, parent=g) + g1.switch(exc) + + +class TestGreenlet(TestCase): + + def _do_simple_test(self): + lst = [] + + def f(): + lst.append(1) + greenlet.getcurrent().parent.switch() + lst.append(3) + g = RawGreenlet(f) + lst.append(0) + g.switch() + lst.append(2) + g.switch() + lst.append(4) + self.assertEqual(lst, list(range(5))) + + def test_simple(self): + self._do_simple_test() + + def test_switch_no_run_raises_AttributeError(self): + g = RawGreenlet() + with self.assertRaises(AttributeError) as exc: + g.switch() + + self.assertIn("run", str(exc.exception)) + + def test_throw_no_run_raises_AttributeError(self): + g = RawGreenlet() + with self.assertRaises(AttributeError) as exc: + g.throw(SomeError) + + self.assertIn("run", str(exc.exception)) + + def test_parent_equals_None(self): + g = RawGreenlet(parent=None) + self.assertIsNotNone(g) + self.assertIs(g.parent, greenlet.getcurrent()) + + def test_run_equals_None(self): + g = RawGreenlet(run=None) + self.assertIsNotNone(g) + self.assertIsNone(g.run) + + def test_two_children(self): + lst = [] + + def f(): + lst.append(1) + greenlet.getcurrent().parent.switch() + lst.extend([1, 1]) + g = RawGreenlet(f) + h = RawGreenlet(f) + g.switch() + self.assertEqual(len(lst), 1) + h.switch() + self.assertEqual(len(lst), 2) + h.switch() + self.assertEqual(len(lst), 4) + self.assertEqual(h.dead, True) + g.switch() + self.assertEqual(len(lst), 6) + self.assertEqual(g.dead, True) + + def test_two_recursive_children(self): + lst = [] + + def f(): + lst.append('b') + greenlet.getcurrent().parent.switch() + + def g(): + lst.append('a') + g = RawGreenlet(f) + g.switch() + lst.append('c') + + g = RawGreenlet(g) + self.assertEqual(sys.getrefcount(g), 2) + g.switch() + self.assertEqual(lst, ['a', 'b', 'c']) + # Just the one in this frame, plus the one on the stack we pass to the function + self.assertEqual(sys.getrefcount(g), 2) + + def test_threads(self): + success = [] + + def f(): + self._do_simple_test() + success.append(True) + ths = [threading.Thread(target=f) for i in range(10)] + for th in ths: + th.start() + for th in ths: + th.join(10) + self.assertEqual(len(success), len(ths)) + + def test_exception(self): + seen = [] + g1 = RawGreenlet(fmain) + g2 = RawGreenlet(fmain) + g1.switch(seen) + g2.switch(seen) + g2.parent = g1 + + self.assertEqual(seen, []) + #with self.assertRaises(SomeError): + # p("***Switching back") + # g2.switch() + # Creating this as a bound method can reveal bugs that + # are hidden on newer versions of Python that avoid creating + # bound methods for direct expressions; IOW, don't use the `with` + # form! + self.assertRaises(SomeError, g2.switch) + self.assertEqual(seen, [SomeError]) + + value = g2.switch() + self.assertEqual(value, ()) + self.assertEqual(seen, [SomeError]) + + value = g2.switch(25) + self.assertEqual(value, 25) + self.assertEqual(seen, [SomeError]) + + + def test_send_exception(self): + seen = [] + g1 = RawGreenlet(fmain) + g1.switch(seen) + self.assertRaises(KeyError, send_exception, g1, KeyError) + self.assertEqual(seen, [KeyError]) + + def test_dealloc(self): + seen = [] + g1 = RawGreenlet(fmain) + g2 = RawGreenlet(fmain) + g1.switch(seen) + g2.switch(seen) + self.assertEqual(seen, []) + del g1 + gc.collect() + self.assertEqual(seen, [greenlet.GreenletExit]) + del g2 + gc.collect() + self.assertEqual(seen, [greenlet.GreenletExit, greenlet.GreenletExit]) + + def test_dealloc_catches_GreenletExit_throws_other(self): + def run(): + try: + greenlet.getcurrent().parent.switch() + except greenlet.GreenletExit: + raise SomeError from None + + g = RawGreenlet(run) + g.switch() + # Destroying the only reference to the greenlet causes it + # to get GreenletExit; when it in turn raises, even though we're the parent + # we don't get the exception, it just gets printed. + # When we run on 3.8 only, we can use sys.unraisablehook + oldstderr = sys.stderr + try: + from cStringIO import StringIO + except ImportError: + from io import StringIO + stderr = sys.stderr = StringIO() + try: + del g + finally: + sys.stderr = oldstderr + + v = stderr.getvalue() + self.assertIn("Exception", v) + self.assertIn('ignored', v) + self.assertIn("SomeError", v) + + + def test_dealloc_other_thread(self): + seen = [] + someref = [] + + bg_glet_created_running_and_no_longer_ref_in_bg = threading.Event() + fg_ref_released = threading.Event() + bg_should_be_clear = threading.Event() + ok_to_exit_bg_thread = threading.Event() + + def f(): + g1 = RawGreenlet(fmain) + g1.switch(seen) + someref.append(g1) + del g1 + gc.collect() + + bg_glet_created_running_and_no_longer_ref_in_bg.set() + fg_ref_released.wait(3) + + RawGreenlet() # trigger release + bg_should_be_clear.set() + ok_to_exit_bg_thread.wait(3) + RawGreenlet() # One more time + + t = threading.Thread(target=f) + t.start() + bg_glet_created_running_and_no_longer_ref_in_bg.wait(10) + + self.assertEqual(seen, []) + self.assertEqual(len(someref), 1) + del someref[:] + gc.collect() + # g1 is not released immediately because it's from another thread + self.assertEqual(seen, []) + fg_ref_released.set() + bg_should_be_clear.wait(3) + try: + self.assertEqual(seen, [greenlet.GreenletExit]) + finally: + ok_to_exit_bg_thread.set() + t.join(10) + del seen[:] + del someref[:] + + def test_frame(self): + def f1(): + f = sys._getframe(0) # pylint:disable=protected-access + self.assertEqual(f.f_back, None) + greenlet.getcurrent().parent.switch(f) + return "meaning of life" + g = RawGreenlet(f1) + frame = g.switch() + self.assertTrue(frame is g.gr_frame) + self.assertTrue(g) + + from_g = g.switch() + self.assertFalse(g) + self.assertEqual(from_g, 'meaning of life') + self.assertEqual(g.gr_frame, None) + + def test_thread_bug(self): + def runner(x): + g = RawGreenlet(lambda: time.sleep(x)) + g.switch() + t1 = threading.Thread(target=runner, args=(0.2,)) + t2 = threading.Thread(target=runner, args=(0.3,)) + t1.start() + t2.start() + t1.join(10) + t2.join(10) + + def test_switch_kwargs(self): + def run(a, b): + self.assertEqual(a, 4) + self.assertEqual(b, 2) + return 42 + x = RawGreenlet(run).switch(a=4, b=2) + self.assertEqual(x, 42) + + def test_switch_kwargs_to_parent(self): + def run(x): + greenlet.getcurrent().parent.switch(x=x) + greenlet.getcurrent().parent.switch(2, x=3) + return x, x ** 2 + g = RawGreenlet(run) + self.assertEqual({'x': 3}, g.switch(3)) + self.assertEqual(((2,), {'x': 3}), g.switch()) + self.assertEqual((3, 9), g.switch()) + + def test_switch_to_another_thread(self): + data = {} + created_event = threading.Event() + done_event = threading.Event() + + def run(): + data['g'] = RawGreenlet(lambda: None) + created_event.set() + done_event.wait(10) + thread = threading.Thread(target=run) + thread.start() + created_event.wait(10) + with self.assertRaises(greenlet.error): + data['g'].switch() + done_event.set() + thread.join(10) + # XXX: Should handle this automatically + data.clear() + + def test_exc_state(self): + def f(): + try: + raise ValueError('fun') + except: # pylint:disable=bare-except + exc_info = sys.exc_info() + RawGreenlet(h).switch() + self.assertEqual(exc_info, sys.exc_info()) + + def h(): + self.assertEqual(sys.exc_info(), (None, None, None)) + + RawGreenlet(f).switch() + + def test_instance_dict(self): + def f(): + greenlet.getcurrent().test = 42 + def deldict(g): + del g.__dict__ + def setdict(g, value): + g.__dict__ = value + g = RawGreenlet(f) + self.assertEqual(g.__dict__, {}) + g.switch() + self.assertEqual(g.test, 42) + self.assertEqual(g.__dict__, {'test': 42}) + g.__dict__ = g.__dict__ + self.assertEqual(g.__dict__, {'test': 42}) + self.assertRaises(TypeError, deldict, g) + self.assertRaises(TypeError, setdict, g, 42) + + def test_running_greenlet_has_no_run(self): + has_run = [] + def func(): + has_run.append( + hasattr(greenlet.getcurrent(), 'run') + ) + + g = RawGreenlet(func) + g.switch() + self.assertEqual(has_run, [False]) + + def test_deepcopy(self): + import copy + self.assertRaises(TypeError, copy.copy, RawGreenlet()) + self.assertRaises(TypeError, copy.deepcopy, RawGreenlet()) + + def test_parent_restored_on_kill(self): + hub = RawGreenlet(lambda: None) + main = greenlet.getcurrent() + result = [] + def worker(): + try: + # Wait to be killed by going back to the test. + main.switch() + except greenlet.GreenletExit: + # Resurrect and switch to parent + result.append(greenlet.getcurrent().parent) + result.append(greenlet.getcurrent()) + hub.switch() + g = RawGreenlet(worker, parent=hub) + g.switch() + # delete the only reference, thereby raising GreenletExit + del g + self.assertTrue(result) + self.assertIs(result[0], main) + self.assertIs(result[1].parent, hub) + # Delete them, thereby breaking the cycle between the greenlet + # and the frame, which otherwise would never be collectable + # XXX: We should be able to automatically fix this. + del result[:] + hub = None + main = None + + def test_parent_return_failure(self): + # No run causes AttributeError on switch + g1 = RawGreenlet() + # Greenlet that implicitly switches to parent + g2 = RawGreenlet(lambda: None, parent=g1) + # AttributeError should propagate to us, no fatal errors + with self.assertRaises(AttributeError): + g2.switch() + + def test_throw_exception_not_lost(self): + class mygreenlet(RawGreenlet): + def __getattribute__(self, name): + try: + raise Exception # pylint:disable=broad-exception-raised + except: # pylint:disable=bare-except + pass + return RawGreenlet.__getattribute__(self, name) + g = mygreenlet(lambda: None) + self.assertRaises(SomeError, g.throw, SomeError()) + + @fails_leakcheck + def _do_test_throw_to_dead_thread_doesnt_crash(self, wait_for_cleanup=False): + result = [] + def worker(): + greenlet.getcurrent().parent.switch() + + def creator(): + g = RawGreenlet(worker) + g.switch() + result.append(g) + if wait_for_cleanup: + # Let this greenlet eventually be cleaned up. + g.switch() + greenlet.getcurrent() + t = threading.Thread(target=creator) + t.start() + t.join(10) + del t + # But, depending on the operating system, the thread + # deallocator may not actually have run yet! So we can't be + # sure about the error message unless we wait. + if wait_for_cleanup: + self.wait_for_pending_cleanups() + with self.assertRaises(greenlet.error) as exc: + result[0].throw(SomeError) + + if not wait_for_cleanup: + self.assertIn( + str(exc.exception), [ + "cannot switch to a different thread (which happens to have exited)", + "cannot switch to a different thread" + ] + ) + else: + self.assertEqual( + str(exc.exception), + "cannot switch to a different thread (which happens to have exited)", + ) + + if hasattr(result[0].gr_frame, 'clear'): + # The frame is actually executing (it thinks), we can't clear it. + with self.assertRaises(RuntimeError): + result[0].gr_frame.clear() + # Unfortunately, this doesn't actually clear the references, they're in the + # fast local array. + if not wait_for_cleanup: + result[0].gr_frame.f_locals.clear() + else: + self.assertIsNone(result[0].gr_frame) + + del creator + worker = None + del result[:] + # XXX: we ought to be able to automatically fix this. + # See issue 252 + self.expect_greenlet_leak = True # direct us not to wait for it to go away + + @fails_leakcheck + def test_throw_to_dead_thread_doesnt_crash(self): + self._do_test_throw_to_dead_thread_doesnt_crash() + + def test_throw_to_dead_thread_doesnt_crash_wait(self): + self._do_test_throw_to_dead_thread_doesnt_crash(True) + + @fails_leakcheck + def test_recursive_startup(self): + class convoluted(RawGreenlet): + def __init__(self): + RawGreenlet.__init__(self) + self.count = 0 + def __getattribute__(self, name): + if name == 'run' and self.count == 0: + self.count = 1 + self.switch(43) + return RawGreenlet.__getattribute__(self, name) + def run(self, value): + while True: + self.parent.switch(value) + g = convoluted() + self.assertEqual(g.switch(42), 43) + # Exits the running greenlet, otherwise it leaks + # XXX: We should be able to automatically fix this + #g.throw(greenlet.GreenletExit) + #del g + self.expect_greenlet_leak = True + + def test_threaded_updatecurrent(self): + # released when main thread should execute + lock1 = threading.Lock() + lock1.acquire() + # released when another thread should execute + lock2 = threading.Lock() + lock2.acquire() + class finalized(object): + def __del__(self): + # happens while in green_updatecurrent() in main greenlet + # should be very careful not to accidentally call it again + # at the same time we must make sure another thread executes + lock2.release() + lock1.acquire() + # now ts_current belongs to another thread + def deallocator(): + greenlet.getcurrent().parent.switch() + def fthread(): + lock2.acquire() + greenlet.getcurrent() + del g[0] + lock1.release() + lock2.acquire() + greenlet.getcurrent() + lock1.release() + main = greenlet.getcurrent() + g = [RawGreenlet(deallocator)] + g[0].bomb = finalized() + g[0].switch() + t = threading.Thread(target=fthread) + t.start() + # let another thread grab ts_current and deallocate g[0] + lock2.release() + lock1.acquire() + # this is the corner stone + # getcurrent() will notice that ts_current belongs to another thread + # and start the update process, which would notice that g[0] should + # be deallocated, and that will execute an object's finalizer. Now, + # that object will let another thread run so it can grab ts_current + # again, which would likely crash the interpreter if there's no + # check for this case at the end of green_updatecurrent(). This test + # passes if getcurrent() returns correct result, but it's likely + # to randomly crash if it's not anyway. + self.assertEqual(greenlet.getcurrent(), main) + # wait for another thread to complete, just in case + t.join(10) + + def test_dealloc_switch_args_not_lost(self): + seen = [] + def worker(): + # wait for the value + value = greenlet.getcurrent().parent.switch() + # delete all references to ourself + del worker[0] + initiator.parent = greenlet.getcurrent().parent + # switch to main with the value, but because + # ts_current is the last reference to us we + # return here immediately, where we resurrect ourself. + try: + greenlet.getcurrent().parent.switch(value) + finally: + seen.append(greenlet.getcurrent()) + def initiator(): + return 42 # implicitly falls thru to parent + + worker = [RawGreenlet(worker)] + + worker[0].switch() # prime worker + initiator = RawGreenlet(initiator, worker[0]) + value = initiator.switch() + self.assertTrue(seen) + self.assertEqual(value, 42) + + def test_tuple_subclass(self): + # The point of this test is to see what happens when a custom + # tuple subclass is used as an object passed directly to the C + # function ``green_switch``; part of ``green_switch`` checks + # the ``len()`` of the ``args`` tuple, and that can call back + # into Python. Here, when it calls back into Python, we + # recursively enter ``green_switch`` again. + + # This test is really only relevant on Python 2. The builtin + # `apply` function directly passes the given args tuple object + # to the underlying function, whereas the Python 3 version + # unpacks and repacks into an actual tuple. This could still + # happen using the C API on Python 3 though. We should write a + # builtin version of apply() ourself. + def _apply(func, a, k): + func(*a, **k) + + class mytuple(tuple): + def __len__(self): + greenlet.getcurrent().switch() + return tuple.__len__(self) + args = mytuple() + kwargs = dict(a=42) + def switchapply(): + _apply(greenlet.getcurrent().parent.switch, args, kwargs) + g = RawGreenlet(switchapply) + self.assertEqual(g.switch(), kwargs) + + def test_abstract_subclasses(self): + AbstractSubclass = ABCMeta( + 'AbstractSubclass', + (RawGreenlet,), + {'run': abstractmethod(lambda self: None)}) + + class BadSubclass(AbstractSubclass): + pass + + class GoodSubclass(AbstractSubclass): + def run(self): + pass + + GoodSubclass() # should not raise + self.assertRaises(TypeError, BadSubclass) + + def test_implicit_parent_with_threads(self): + if not gc.isenabled(): + return # cannot test with disabled gc + N = gc.get_threshold()[0] + if N < 50: + return # cannot test with such a small N + def attempt(): + lock1 = threading.Lock() + lock1.acquire() + lock2 = threading.Lock() + lock2.acquire() + recycled = [False] + def another_thread(): + lock1.acquire() # wait for gc + greenlet.getcurrent() # update ts_current + lock2.release() # release gc + t = threading.Thread(target=another_thread) + t.start() + class gc_callback(object): + def __del__(self): + lock1.release() + lock2.acquire() + recycled[0] = True + class garbage(object): + def __init__(self): + self.cycle = self + self.callback = gc_callback() + l = [] + x = range(N*2) + current = greenlet.getcurrent() + g = garbage() + for _ in x: + g = None # lose reference to garbage + if recycled[0]: + # gc callback called prematurely + t.join(10) + return False + last = RawGreenlet() + if recycled[0]: + break # yes! gc called in green_new + l.append(last) # increase allocation counter + else: + # gc callback not called when expected + gc.collect() + if recycled[0]: + t.join(10) + return False + self.assertEqual(last.parent, current) + for g in l: + self.assertEqual(g.parent, current) + return True + for _ in range(5): + if attempt(): + break + + def test_issue_245_reference_counting_subclass_no_threads(self): + # https://github.com/python-greenlet/greenlet/issues/245 + # Before the fix, this crashed pretty reliably on + # Python 3.10, at least on macOS; but much less reliably on other + # interpreters (memory layout must have changed). + # The threaded test crashed more reliably on more interpreters. + from greenlet import getcurrent + from greenlet import GreenletExit + + class Greenlet(RawGreenlet): + pass + + initial_refs = sys.getrefcount(Greenlet) + # This has to be an instance variable because + # Python 2 raises a SyntaxError if we delete a local + # variable referenced in an inner scope. + self.glets = [] # pylint:disable=attribute-defined-outside-init + + def greenlet_main(): + try: + getcurrent().parent.switch() + except GreenletExit: + self.glets.append(getcurrent()) + + # Before the + for _ in range(10): + Greenlet(greenlet_main).switch() + + del self.glets + self.assertEqual(sys.getrefcount(Greenlet), initial_refs) + + def test_issue_245_reference_counting_subclass_threads(self): + # https://github.com/python-greenlet/greenlet/issues/245 + from threading import Thread + from threading import Event + + from greenlet import getcurrent + + class MyGreenlet(RawGreenlet): + pass + + glets = [] + ref_cleared = Event() + + def greenlet_main(): + getcurrent().parent.switch() + + def thread_main(greenlet_running_event): + mine = MyGreenlet(greenlet_main) + glets.append(mine) + # The greenlets being deleted must be active + mine.switch() + # Don't keep any reference to it in this thread + del mine + # Let main know we published our greenlet. + greenlet_running_event.set() + # Wait for main to let us know the references are + # gone and the greenlet objects no longer reachable + ref_cleared.wait(10) + # The creating thread must call getcurrent() (or a few other + # greenlet APIs) because that's when the thread-local list of dead + # greenlets gets cleared. + getcurrent() + + # We start with 3 references to the subclass: + # - This module + # - Its __mro__ + # - The __subclassess__ attribute of greenlet + # - (If we call gc.get_referents(), we find four entries, including + # some other tuple ``(greenlet)`` that I'm not sure about but must be part + # of the machinery.) + # + # On Python 3.10 it's often enough to just run 3 threads; on Python 2.7, + # more threads are needed, and the results are still + # non-deterministic. Presumably the memory layouts are different + initial_refs = sys.getrefcount(MyGreenlet) + thread_ready_events = [] + for _ in range( + initial_refs + 45 + ): + event = Event() + thread = Thread(target=thread_main, args=(event,)) + thread_ready_events.append(event) + thread.start() + + + for done_event in thread_ready_events: + done_event.wait(10) + + + del glets[:] + ref_cleared.set() + # Let any other thread run; it will crash the interpreter + # if not fixed (or silently corrupt memory and we possibly crash + # later). + self.wait_for_pending_cleanups() + self.assertEqual(sys.getrefcount(MyGreenlet), initial_refs) + + def test_falling_off_end_switches_to_unstarted_parent_raises_error(self): + def no_args(): + return 13 + + parent_never_started = RawGreenlet(no_args) + + def leaf(): + return 42 + + child = RawGreenlet(leaf, parent_never_started) + + # Because the run function takes to arguments + with self.assertRaises(TypeError): + child.switch() + + def test_falling_off_end_switches_to_unstarted_parent_works(self): + def one_arg(x): + return (x, 24) + + parent_never_started = RawGreenlet(one_arg) + + def leaf(): + return 42 + + child = RawGreenlet(leaf, parent_never_started) + + result = child.switch() + self.assertEqual(result, (42, 24)) + + def test_switch_to_dead_greenlet_with_unstarted_perverse_parent(self): + class Parent(RawGreenlet): + def __getattribute__(self, name): + if name == 'run': + raise SomeError + + + parent_never_started = Parent() + seen = [] + child = RawGreenlet(lambda: seen.append(42), parent_never_started) + # Because we automatically start the parent when the child is + # finished + with self.assertRaises(SomeError): + child.switch() + + self.assertEqual(seen, [42]) + + with self.assertRaises(SomeError): + child.switch() + self.assertEqual(seen, [42]) + + def test_switch_to_dead_greenlet_reparent(self): + seen = [] + parent_never_started = RawGreenlet(lambda: seen.append(24)) + child = RawGreenlet(lambda: seen.append(42)) + + child.switch() + self.assertEqual(seen, [42]) + + child.parent = parent_never_started + # This actually is the same as switching to the parent. + result = child.switch() + self.assertIsNone(result) + self.assertEqual(seen, [42, 24]) + + def test_can_access_f_back_of_suspended_greenlet(self): + # This tests our frame rewriting to work around Python 3.12+ having + # some interpreter frames on the C stack. It will crash in the absence + # of that logic. + main = greenlet.getcurrent() + + def outer(): + inner() + + def inner(): + main.switch(sys._getframe(0)) + + hub = RawGreenlet(outer) + # start it + hub.switch() + + # start another greenlet to make sure we aren't relying on + # anything in `hub` still being on the C stack + unrelated = RawGreenlet(lambda: None) + unrelated.switch() + + # now it is suspended + self.assertIsNotNone(hub.gr_frame) + self.assertEqual(hub.gr_frame.f_code.co_name, "inner") + self.assertIsNotNone(hub.gr_frame.f_back) + self.assertEqual(hub.gr_frame.f_back.f_code.co_name, "outer") + # The next line is what would crash + self.assertIsNone(hub.gr_frame.f_back.f_back) + + def test_get_stack_with_nested_c_calls(self): + from functools import partial + from . import _test_extension_cpp + + def recurse(v): + if v > 0: + return v * _test_extension_cpp.test_call(partial(recurse, v - 1)) + return greenlet.getcurrent().parent.switch() + + gr = RawGreenlet(recurse) + gr.switch(5) + frame = gr.gr_frame + for i in range(5): + self.assertEqual(frame.f_locals["v"], i) + frame = frame.f_back + self.assertEqual(frame.f_locals["v"], 5) + self.assertIsNone(frame.f_back) + self.assertEqual(gr.switch(10), 1200) # 1200 = 5! * 10 + + def test_frames_always_exposed(self): + # On Python 3.12 this will crash if we don't set the + # gr_frames_always_exposed attribute. More background: + # https://github.com/python-greenlet/greenlet/issues/388 + main = greenlet.getcurrent() + + def outer(): + inner(sys._getframe(0)) + + def inner(frame): + main.switch(frame) + + gr = RawGreenlet(outer) + frame = gr.switch() + + # Do something else to clobber the part of the C stack used by `gr`, + # so we can't skate by on "it just happened to still be there" + unrelated = RawGreenlet(lambda: None) + unrelated.switch() + + self.assertEqual(frame.f_code.co_name, "outer") + # The next line crashes on 3.12 if we haven't exposed the frames. + self.assertIsNone(frame.f_back) + + +class TestGreenletSetParentErrors(TestCase): + def test_threaded_reparent(self): + data = {} + created_event = threading.Event() + done_event = threading.Event() + + def run(): + data['g'] = RawGreenlet(lambda: None) + created_event.set() + done_event.wait(10) + + def blank(): + greenlet.getcurrent().parent.switch() + + thread = threading.Thread(target=run) + thread.start() + created_event.wait(10) + g = RawGreenlet(blank) + g.switch() + with self.assertRaises(ValueError) as exc: + g.parent = data['g'] + done_event.set() + thread.join(10) + + self.assertEqual(str(exc.exception), "parent cannot be on a different thread") + + def test_unexpected_reparenting(self): + another = [] + def worker(): + g = RawGreenlet(lambda: None) + another.append(g) + g.switch() + t = threading.Thread(target=worker) + t.start() + t.join(10) + # The first time we switch (running g_initialstub(), which is + # when we look up the run attribute) we attempt to change the + # parent to one from another thread (which also happens to be + # dead). ``g_initialstub()`` should detect this and raise a + # greenlet error. + # + # EXCEPT: With the fix for #252, this is actually detected + # sooner, when setting the parent itself. Prior to that fix, + # the main greenlet from the background thread kept a valid + # value for ``run_info``, and appeared to be a valid parent + # until we actually started the greenlet. But now that it's + # cleared, this test is catching whether ``green_setparent`` + # can detect the dead thread. + # + # Further refactoring once again changes this back to a greenlet.error + # + # We need to wait for the cleanup to happen, but we're + # deliberately leaking a main greenlet here. + self.wait_for_pending_cleanups(initial_main_greenlets=self.main_greenlets_before_test + 1) + + class convoluted(RawGreenlet): + def __getattribute__(self, name): + if name == 'run': + self.parent = another[0] # pylint:disable=attribute-defined-outside-init + return RawGreenlet.__getattribute__(self, name) + g = convoluted(lambda: None) + with self.assertRaises(greenlet.error) as exc: + g.switch() + self.assertEqual(str(exc.exception), + "cannot switch to a different thread (which happens to have exited)") + del another[:] + + def test_unexpected_reparenting_thread_running(self): + # Like ``test_unexpected_reparenting``, except the background thread is + # actually still alive. + another = [] + switched_to_greenlet = threading.Event() + keep_main_alive = threading.Event() + def worker(): + g = RawGreenlet(lambda: None) + another.append(g) + g.switch() + switched_to_greenlet.set() + keep_main_alive.wait(10) + class convoluted(RawGreenlet): + def __getattribute__(self, name): + if name == 'run': + self.parent = another[0] # pylint:disable=attribute-defined-outside-init + return RawGreenlet.__getattribute__(self, name) + + t = threading.Thread(target=worker) + t.start() + + switched_to_greenlet.wait(10) + try: + g = convoluted(lambda: None) + + with self.assertRaises(greenlet.error) as exc: + g.switch() + self.assertEqual(str(exc.exception), "cannot switch to a different thread") + finally: + keep_main_alive.set() + t.join(10) + # XXX: Should handle this automatically. + del another[:] + + def test_cannot_delete_parent(self): + worker = RawGreenlet(lambda: None) + self.assertIs(worker.parent, greenlet.getcurrent()) + + with self.assertRaises(AttributeError) as exc: + del worker.parent + self.assertEqual(str(exc.exception), "can't delete attribute") + + def test_cannot_delete_parent_of_main(self): + with self.assertRaises(AttributeError) as exc: + del greenlet.getcurrent().parent + self.assertEqual(str(exc.exception), "can't delete attribute") + + + def test_main_greenlet_parent_is_none(self): + # assuming we're in a main greenlet here. + self.assertIsNone(greenlet.getcurrent().parent) + + def test_set_parent_wrong_types(self): + def bg(): + # Go back to main. + greenlet.getcurrent().parent.switch() + + def check(glet): + for p in None, 1, self, "42": + with self.assertRaises(TypeError) as exc: + glet.parent = p + + self.assertEqual( + str(exc.exception), + "GreenletChecker: Expected any type of greenlet, not " + type(p).__name__) + + # First, not running + g = RawGreenlet(bg) + self.assertFalse(g) + check(g) + + # Then when running. + g.switch() + self.assertTrue(g) + check(g) + + # Let it finish + g.switch() + + + def test_trivial_cycle(self): + glet = RawGreenlet(lambda: None) + with self.assertRaises(ValueError) as exc: + glet.parent = glet + self.assertEqual(str(exc.exception), "cyclic parent chain") + + def test_trivial_cycle_main(self): + # This used to produce a ValueError, but we catch it earlier than that now. + with self.assertRaises(AttributeError) as exc: + greenlet.getcurrent().parent = greenlet.getcurrent() + self.assertEqual(str(exc.exception), "cannot set the parent of a main greenlet") + + def test_deeper_cycle(self): + g1 = RawGreenlet(lambda: None) + g2 = RawGreenlet(lambda: None) + g3 = RawGreenlet(lambda: None) + + g1.parent = g2 + g2.parent = g3 + with self.assertRaises(ValueError) as exc: + g3.parent = g1 + self.assertEqual(str(exc.exception), "cyclic parent chain") + + +class TestRepr(TestCase): + + def assertEndsWith(self, got, suffix): + self.assertTrue(got.endswith(suffix), (got, suffix)) + + def test_main_while_running(self): + r = repr(greenlet.getcurrent()) + self.assertEndsWith(r, " current active started main>") + + def test_main_in_background(self): + main = greenlet.getcurrent() + def run(): + return repr(main) + + g = RawGreenlet(run) + r = g.switch() + self.assertEndsWith(r, ' suspended active started main>') + + def test_initial(self): + r = repr(RawGreenlet()) + self.assertEndsWith(r, ' pending>') + + def test_main_from_other_thread(self): + main = greenlet.getcurrent() + + class T(threading.Thread): + original_main = thread_main = None + main_glet = None + def run(self): + self.original_main = repr(main) + self.main_glet = greenlet.getcurrent() + self.thread_main = repr(self.main_glet) + + t = T() + t.start() + t.join(10) + + self.assertEndsWith(t.original_main, ' suspended active started main>') + self.assertEndsWith(t.thread_main, ' current active started main>') + # give the machinery time to notice the death of the thread, + # and clean it up. Note that we don't use + # ``expect_greenlet_leak`` or wait_for_pending_cleanups, + # because at this point we know we have an extra greenlet + # still reachable. + for _ in range(3): + time.sleep(0.001) + + # In the past, main greenlets, even from dead threads, never + # really appear dead. We have fixed that, and we also report + # that the thread is dead in the repr. (Do this multiple times + # to make sure that we don't self-modify and forget our state + # in the C++ code). + for _ in range(3): + self.assertTrue(t.main_glet.dead) + r = repr(t.main_glet) + self.assertEndsWith(r, ' (thread exited) dead>') + + def test_dead(self): + g = RawGreenlet(lambda: None) + g.switch() + self.assertEndsWith(repr(g), ' dead>') + self.assertNotIn('suspended', repr(g)) + self.assertNotIn('started', repr(g)) + self.assertNotIn('active', repr(g)) + + def test_formatting_produces_native_str(self): + # https://github.com/python-greenlet/greenlet/issues/218 + # %s formatting on Python 2 was producing unicode, not str. + + g_dead = RawGreenlet(lambda: None) + g_not_started = RawGreenlet(lambda: None) + g_cur = greenlet.getcurrent() + + for g in g_dead, g_not_started, g_cur: + + self.assertIsInstance( + '%s' % (g,), + str + ) + self.assertIsInstance( + '%r' % (g,), + str, + ) + + +class TestMainGreenlet(TestCase): + # Tests some implementation details, and relies on some + # implementation details. + + def _check_current_is_main(self): + # implementation detail + assert 'main' in repr(greenlet.getcurrent()) + + t = type(greenlet.getcurrent()) + assert 'main' not in repr(t) + return t + + def test_main_greenlet_type_can_be_subclassed(self): + main_type = self._check_current_is_main() + subclass = type('subclass', (main_type,), {}) + self.assertIsNotNone(subclass) + + def test_main_greenlet_is_greenlet(self): + self._check_current_is_main() + self.assertIsInstance(greenlet.getcurrent(), RawGreenlet) + + + +class TestBrokenGreenlets(TestCase): + # Tests for things that used to, or still do, terminate the interpreter. + # This often means doing unsavory things. + + def test_failed_to_initialstub(self): + def func(): + raise AssertionError("Never get here") + + + g = greenlet._greenlet.UnswitchableGreenlet(func) + g.force_switch_error = True + + with self.assertRaisesRegex(SystemError, + "Failed to switch stacks into a greenlet for the first time."): + g.switch() + + def test_failed_to_switch_into_running(self): + runs = [] + def func(): + runs.append(1) + greenlet.getcurrent().parent.switch() + runs.append(2) + greenlet.getcurrent().parent.switch() + runs.append(3) # pragma: no cover + + g = greenlet._greenlet.UnswitchableGreenlet(func) + g.switch() + self.assertEqual(runs, [1]) + g.switch() + self.assertEqual(runs, [1, 2]) + g.force_switch_error = True + + with self.assertRaisesRegex(SystemError, + "Failed to switch stacks into a running greenlet."): + g.switch() + + # If we stopped here, we would fail the leakcheck, because we've left + # the ``inner_bootstrap()`` C frame and its descendents hanging around, + # which have a bunch of Python references. They'll never get cleaned up + # if we don't let the greenlet finish. + g.force_switch_error = False + g.switch() + self.assertEqual(runs, [1, 2, 3]) + + def test_failed_to_slp_switch_into_running(self): + ex = self.assertScriptRaises('fail_slp_switch.py') + + self.assertIn('fail_slp_switch is running', ex.output) + self.assertIn(ex.returncode, self.get_expected_returncodes_for_aborted_process()) + + def test_reentrant_switch_two_greenlets(self): + # Before we started capturing the arguments in g_switch_finish, this could crash. + output = self.run_script('fail_switch_two_greenlets.py') + self.assertIn('In g1_run', output) + self.assertIn('TRACE', output) + self.assertIn('LEAVE TRACE', output) + self.assertIn('Falling off end of main', output) + self.assertIn('Falling off end of g1_run', output) + self.assertIn('Falling off end of g2', output) + + def test_reentrant_switch_three_greenlets(self): + # On debug builds of greenlet, this used to crash with an assertion error; + # on non-debug versions, it ran fine (which it should not do!). + # Now it always crashes correctly with a TypeError + ex = self.assertScriptRaises('fail_switch_three_greenlets.py', exitcodes=(1,)) + + self.assertIn('TypeError', ex.output) + self.assertIn('positional arguments', ex.output) + + def test_reentrant_switch_three_greenlets2(self): + # This actually passed on debug and non-debug builds. It + # should probably have been triggering some debug assertions + # but it didn't. + # + # I think the fixes for the above test also kicked in here. + output = self.run_script('fail_switch_three_greenlets2.py') + self.assertIn( + "RESULTS: [('trace', 'switch'), " + "('trace', 'switch'), ('g2 arg', 'g2 from tracefunc'), " + "('trace', 'switch'), ('main g1', 'from g2_run'), ('trace', 'switch'), " + "('g1 arg', 'g1 from main'), ('trace', 'switch'), ('main g2', 'from g1_run'), " + "('trace', 'switch'), ('g1 from parent', 'g1 from main 2'), ('trace', 'switch'), " + "('main g1.2', 'g1 done'), ('trace', 'switch'), ('g2 from parent', ()), " + "('trace', 'switch'), ('main g2.2', 'g2 done')]", + output + ) + + def test_reentrant_switch_GreenletAlreadyStartedInPython(self): + output = self.run_script('fail_initialstub_already_started.py') + + self.assertIn( + "RESULTS: ['Begin C', 'Switch to b from B.__getattribute__ in C', " + "('Begin B', ()), '_B_run switching to main', ('main from c', 'From B'), " + "'B.__getattribute__ back from main in C', ('Begin A', (None,)), " + "('A dead?', True, 'B dead?', True, 'C dead?', False), " + "'C done', ('main from c.2', None)]", + output + ) + + def test_reentrant_switch_run_callable_has_del(self): + output = self.run_script('fail_clearing_run_switches.py') + self.assertIn( + "RESULTS [" + "('G.__getattribute__', 'run'), ('RunCallable', '__del__'), " + "('main: g.switch()', 'from RunCallable'), ('run_func', 'enter')" + "]", + output + ) + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_greenlet_trash.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_greenlet_trash.py new file mode 100644 index 0000000..8d9716e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_greenlet_trash.py @@ -0,0 +1,178 @@ +# -*- coding: utf-8 -*- +""" +Tests for greenlets interacting with the CPython trash can API. + +The CPython trash can API is not designed to be re-entered from a +single thread. But this can happen using greenlets, if something +during the object deallocation process switches greenlets, and this second +greenlet then causes the trash can to get entered again. Here, we do this +very explicitly, but in other cases (like gevent) it could be arbitrarily more +complicated: for example, a weakref callback might try to acquire a lock that's +already held by another greenlet; that would allow a greenlet switch to occur. + +See https://github.com/gevent/gevent/issues/1909 + +This test is fragile and relies on details of the CPython +implementation (like most of the rest of this package): + + - We enter the trashcan and deferred deallocation after + ``_PyTrash_UNWIND_LEVEL`` calls. This constant, defined in + CPython's object.c, is generally 50. That's basically how many objects are required to + get us into the deferred deallocation situation. + + - The test fails by hitting an ``assert()`` in object.c; if the + build didn't enable assert, then we don't catch this. + + - If the test fails in that way, the interpreter crashes. +""" +from __future__ import print_function, absolute_import, division + +import unittest + +class TestTrashCanReEnter(unittest.TestCase): + + def test_it(self): + # Try several times to trigger it, because it isn't 100% + # reliable. + for _ in range(10): + self.check_it() + + def check_it(self): # pylint:disable=too-many-statements + import greenlet + from greenlet._greenlet import get_tstate_trash_delete_nesting # pylint:disable=no-name-in-module + + main = greenlet.getcurrent() + + assert get_tstate_trash_delete_nesting() == 0 + + # We expect to be in deferred deallocation after this many + # deallocations have occurred. TODO: I wish we had a better way to do + # this --- that was before get_tstate_trash_delete_nesting; perhaps + # we can use that API to do better? + TRASH_UNWIND_LEVEL = 50 + # How many objects to put in a container; it's the container that + # queues objects for deferred deallocation. + OBJECTS_PER_CONTAINER = 500 + + class Dealloc: # define the class here because we alter class variables each time we run. + """ + An object with a ``__del__`` method. When it starts getting deallocated + from a deferred trash can run, it switches greenlets, allocates more objects + which then also go in the trash can. If we don't save state appropriately, + nesting gets out of order and we can crash the interpreter. + """ + + #: Has our deallocation actually run and switched greenlets? + #: When it does, this will be set to the current greenlet. This should + #: be happening in the main greenlet, so we check that down below. + SPAWNED = False + + #: Has the background greenlet run? + BG_RAN = False + + BG_GLET = None + + #: How many of these things have ever been allocated. + CREATED = 0 + + #: How many of these things have ever been deallocated. + DESTROYED = 0 + + #: How many were destroyed not in the main greenlet. There should always + #: be some. + #: If the test is broken or things change in the trashcan implementation, + #: this may not be correct. + DESTROYED_BG = 0 + + def __init__(self, sequence_number): + """ + :param sequence_number: The ordinal of this object during + one particular creation run. This is used to detect (guess, really) + when we have entered the trash can's deferred deallocation. + """ + self.i = sequence_number + Dealloc.CREATED += 1 + + def __del__(self): + if self.i == TRASH_UNWIND_LEVEL and not self.SPAWNED: + Dealloc.SPAWNED = greenlet.getcurrent() + other = Dealloc.BG_GLET = greenlet.greenlet(background_greenlet) + x = other.switch() + assert x == 42 + # It's important that we don't switch back to the greenlet, + # we leave it hanging there in an incomplete state. But we don't let it + # get collected, either. If we complete it now, while we're still + # in the scope of the initial trash can, things work out and we + # don't see the problem. We need this greenlet to complete + # at some point in the future, after we've exited this trash can invocation. + del other + elif self.i == 40 and greenlet.getcurrent() is not main: + Dealloc.BG_RAN = True + try: + main.switch(42) + except greenlet.GreenletExit as ex: + # We expect this; all references to us go away + # while we're still running, and we need to finish deleting + # ourself. + Dealloc.BG_RAN = type(ex) + del ex + + # Record the fact that we're dead last of all. This ensures that + # we actually get returned too. + Dealloc.DESTROYED += 1 + if greenlet.getcurrent() is not main: + Dealloc.DESTROYED_BG += 1 + + + def background_greenlet(): + # We direct through a second function, instead of + # directly calling ``make_some()``, so that we have complete + # control over when these objects are destroyed: we need them + # to be destroyed in the context of the background greenlet + t = make_some() + del t # Triggere deletion. + + def make_some(): + t = () + i = OBJECTS_PER_CONTAINER + while i: + # Nest the tuples; it's the recursion that gets us + # into trash. + t = (Dealloc(i), t) + i -= 1 + return t + + + some = make_some() + self.assertEqual(Dealloc.CREATED, OBJECTS_PER_CONTAINER) + self.assertEqual(Dealloc.DESTROYED, 0) + + # If we're going to crash, it should be on the following line. + # We only crash if ``assert()`` is enabled, of course. + del some + + # For non-debug builds of CPython, we won't crash. The best we can do is check + # the nesting level explicitly. + self.assertEqual(0, get_tstate_trash_delete_nesting()) + + # Discard this, raising GreenletExit into where it is waiting. + Dealloc.BG_GLET = None + # The same nesting level maintains. + self.assertEqual(0, get_tstate_trash_delete_nesting()) + + # We definitely cleaned some up in the background + self.assertGreater(Dealloc.DESTROYED_BG, 0) + + # Make sure all the cleanups happened. + self.assertIs(Dealloc.SPAWNED, main) + self.assertTrue(Dealloc.BG_RAN) + self.assertEqual(Dealloc.BG_RAN, greenlet.GreenletExit) + self.assertEqual(Dealloc.CREATED, Dealloc.DESTROYED ) + self.assertEqual(Dealloc.CREATED, OBJECTS_PER_CONTAINER * 2) + + import gc + gc.collect() + + +if __name__ == '__main__': + unittest.main() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_leaks.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_leaks.py new file mode 100644 index 0000000..ed1fa71 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_leaks.py @@ -0,0 +1,443 @@ +# -*- coding: utf-8 -*- +""" +Testing scenarios that may have leaked. +""" +from __future__ import print_function, absolute_import, division + +import sys +import gc + +import time +import weakref +import threading + + +import greenlet +from . import TestCase +from .leakcheck import fails_leakcheck +from .leakcheck import ignores_leakcheck +from .leakcheck import RUNNING_ON_MANYLINUX + +# pylint:disable=protected-access + +assert greenlet.GREENLET_USE_GC # Option to disable this was removed in 1.0 + +class HasFinalizerTracksInstances(object): + EXTANT_INSTANCES = set() + def __init__(self, msg): + self.msg = sys.intern(msg) + self.EXTANT_INSTANCES.add(id(self)) + def __del__(self): + self.EXTANT_INSTANCES.remove(id(self)) + def __repr__(self): + return "" % ( + id(self), self.msg + ) + @classmethod + def reset(cls): + cls.EXTANT_INSTANCES.clear() + + +class TestLeaks(TestCase): + + def test_arg_refs(self): + args = ('a', 'b', 'c') + refcount_before = sys.getrefcount(args) + # pylint:disable=unnecessary-lambda + g = greenlet.greenlet( + lambda *args: greenlet.getcurrent().parent.switch(*args)) + for _ in range(100): + g.switch(*args) + self.assertEqual(sys.getrefcount(args), refcount_before) + + def test_kwarg_refs(self): + kwargs = {} + # pylint:disable=unnecessary-lambda + g = greenlet.greenlet( + lambda **kwargs: greenlet.getcurrent().parent.switch(**kwargs)) + for _ in range(100): + g.switch(**kwargs) + self.assertEqual(sys.getrefcount(kwargs), 2) + + + @staticmethod + def __recycle_threads(): + # By introducing a thread that does sleep we allow other threads, + # that have triggered their __block condition, but did not have a + # chance to deallocate their thread state yet, to finally do so. + # The way it works is by requiring a GIL switch (different thread), + # which does a GIL release (sleep), which might do a GIL switch + # to finished threads and allow them to clean up. + def worker(): + time.sleep(0.001) + t = threading.Thread(target=worker) + t.start() + time.sleep(0.001) + t.join(10) + + def test_threaded_leak(self): + gg = [] + def worker(): + # only main greenlet present + gg.append(weakref.ref(greenlet.getcurrent())) + for _ in range(2): + t = threading.Thread(target=worker) + t.start() + t.join(10) + del t + greenlet.getcurrent() # update ts_current + self.__recycle_threads() + greenlet.getcurrent() # update ts_current + gc.collect() + greenlet.getcurrent() # update ts_current + for g in gg: + self.assertIsNone(g()) + + def test_threaded_adv_leak(self): + gg = [] + def worker(): + # main and additional *finished* greenlets + ll = greenlet.getcurrent().ll = [] + def additional(): + ll.append(greenlet.getcurrent()) + for _ in range(2): + greenlet.greenlet(additional).switch() + gg.append(weakref.ref(greenlet.getcurrent())) + for _ in range(2): + t = threading.Thread(target=worker) + t.start() + t.join(10) + del t + greenlet.getcurrent() # update ts_current + self.__recycle_threads() + greenlet.getcurrent() # update ts_current + gc.collect() + greenlet.getcurrent() # update ts_current + for g in gg: + self.assertIsNone(g()) + + def assertClocksUsed(self): + used = greenlet._greenlet.get_clocks_used_doing_optional_cleanup() + self.assertGreaterEqual(used, 0) + # we don't lose the value + greenlet._greenlet.enable_optional_cleanup(True) + used2 = greenlet._greenlet.get_clocks_used_doing_optional_cleanup() + self.assertEqual(used, used2) + self.assertGreater(greenlet._greenlet.CLOCKS_PER_SEC, 1) + + def _check_issue251(self, + manually_collect_background=True, + explicit_reference_to_switch=False): + # See https://github.com/python-greenlet/greenlet/issues/251 + # Killing a greenlet (probably not the main one) + # in one thread from another thread would + # result in leaking a list (the ts_delkey list). + # We no longer use lists to hold that stuff, though. + + # For the test to be valid, even empty lists have to be tracked by the + # GC + + assert gc.is_tracked([]) + HasFinalizerTracksInstances.reset() + greenlet.getcurrent() + greenlets_before = self.count_objects(greenlet.greenlet, exact_kind=False) + + background_glet_running = threading.Event() + background_glet_killed = threading.Event() + background_greenlets = [] + + # XXX: Switching this to a greenlet subclass that overrides + # run results in all callers failing the leaktest; that + # greenlet instance is leaked. There's a bound method for + # run() living on the stack of the greenlet in g_initialstub, + # and since we don't manually switch back to the background + # greenlet to let it "fall off the end" and exit the + # g_initialstub function, it never gets cleaned up. Making the + # garbage collector aware of this bound method (making it an + # attribute of the greenlet structure and traversing into it) + # doesn't help, for some reason. + def background_greenlet(): + # Throw control back to the main greenlet. + jd = HasFinalizerTracksInstances("DELETING STACK OBJECT") + greenlet._greenlet.set_thread_local( + 'test_leaks_key', + HasFinalizerTracksInstances("DELETING THREAD STATE")) + # Explicitly keeping 'switch' in a local variable + # breaks this test in all versions + if explicit_reference_to_switch: + s = greenlet.getcurrent().parent.switch + s([jd]) + else: + greenlet.getcurrent().parent.switch([jd]) + + bg_main_wrefs = [] + + def background_thread(): + glet = greenlet.greenlet(background_greenlet) + bg_main_wrefs.append(weakref.ref(glet.parent)) + + background_greenlets.append(glet) + glet.switch() # Be sure it's active. + # Control is ours again. + del glet # Delete one reference from the thread it runs in. + background_glet_running.set() + background_glet_killed.wait(10) + + # To trigger the background collection of the dead + # greenlet, thus clearing out the contents of the list, we + # need to run some APIs. See issue 252. + if manually_collect_background: + greenlet.getcurrent() + + + t = threading.Thread(target=background_thread) + t.start() + background_glet_running.wait(10) + greenlet.getcurrent() + lists_before = self.count_objects(list, exact_kind=True) + + assert len(background_greenlets) == 1 + self.assertFalse(background_greenlets[0].dead) + # Delete the last reference to the background greenlet + # from a different thread. This puts it in the background thread's + # ts_delkey list. + del background_greenlets[:] + background_glet_killed.set() + + # Now wait for the background thread to die. + t.join(10) + del t + # As part of the fix for 252, we need to cycle the ceval.c + # interpreter loop to be sure it has had a chance to process + # the pending call. + self.wait_for_pending_cleanups() + + lists_after = self.count_objects(list, exact_kind=True) + greenlets_after = self.count_objects(greenlet.greenlet, exact_kind=False) + + # On 2.7, we observe that lists_after is smaller than + # lists_before. No idea what lists got cleaned up. All the + # Python 3 versions match exactly. + self.assertLessEqual(lists_after, lists_before) + # On versions after 3.6, we've successfully cleaned up the + # greenlet references thanks to the internal "vectorcall" + # protocol; prior to that, there is a reference path through + # the ``greenlet.switch`` method still on the stack that we + # can't reach to clean up. The C code goes through terrific + # lengths to clean that up. + if not explicit_reference_to_switch \ + and greenlet._greenlet.get_clocks_used_doing_optional_cleanup() is not None: + # If cleanup was disabled, though, we may not find it. + self.assertEqual(greenlets_after, greenlets_before) + if manually_collect_background: + # TODO: Figure out how to make this work! + # The one on the stack is still leaking somehow + # in the non-manually-collect state. + self.assertEqual(HasFinalizerTracksInstances.EXTANT_INSTANCES, set()) + else: + # The explicit reference prevents us from collecting it + # and it isn't always found by the GC either for some + # reason. The entire frame is leaked somehow, on some + # platforms (e.g., MacPorts builds of Python (all + # versions!)), but not on other platforms (the linux and + # windows builds on GitHub actions and Appveyor). So we'd + # like to write a test that proves that the main greenlet + # sticks around, and we can on my machine (macOS 11.6, + # MacPorts builds of everything) but we can't write that + # same test on other platforms. However, hopefully iteration + # done by leakcheck will find it. + pass + + if greenlet._greenlet.get_clocks_used_doing_optional_cleanup() is not None: + self.assertClocksUsed() + + def test_issue251_killing_cross_thread_leaks_list(self): + self._check_issue251() + + def test_issue251_with_cleanup_disabled(self): + greenlet._greenlet.enable_optional_cleanup(False) + try: + self._check_issue251() + finally: + greenlet._greenlet.enable_optional_cleanup(True) + + @fails_leakcheck + def test_issue251_issue252_need_to_collect_in_background(self): + # Between greenlet 1.1.2 and the next version, this was still + # failing because the leak of the list still exists when we + # don't call a greenlet API before exiting the thread. The + # proximate cause is that neither of the two greenlets from + # the background thread are actually being destroyed, even + # though the GC is in fact visiting both objects. It's not + # clear where that leak is? For some reason the thread-local + # dict holding it isn't being cleaned up. + # + # The leak, I think, is in the CPYthon internal function that + # calls into green_switch(). The argument tuple is still on + # the C stack somewhere and can't be reached? That doesn't + # make sense, because the tuple should be collectable when + # this object goes away. + # + # Note that this test sometimes spuriously passes on Linux, + # for some reason, but I've never seen it pass on macOS. + self._check_issue251(manually_collect_background=False) + + @fails_leakcheck + def test_issue251_issue252_need_to_collect_in_background_cleanup_disabled(self): + self.expect_greenlet_leak = True + greenlet._greenlet.enable_optional_cleanup(False) + try: + self._check_issue251(manually_collect_background=False) + finally: + greenlet._greenlet.enable_optional_cleanup(True) + + @fails_leakcheck + def test_issue251_issue252_explicit_reference_not_collectable(self): + self._check_issue251( + manually_collect_background=False, + explicit_reference_to_switch=True) + + UNTRACK_ATTEMPTS = 100 + + def _only_test_some_versions(self): + # We're only looking for this problem specifically on 3.11, + # and this set of tests is relatively fragile, depending on + # OS and memory management details. So we want to run it on 3.11+ + # (obviously) but not every older 3.x version in order to reduce + # false negatives. At the moment, those false results seem to have + # resolved, so we are actually running this on 3.8+ + assert sys.version_info[0] >= 3 + if sys.version_info[:2] < (3, 8): + self.skipTest('Only observed on 3.11') + if RUNNING_ON_MANYLINUX: + self.skipTest("Slow and not worth repeating here") + + @ignores_leakcheck + # Because we're just trying to track raw memory, not objects, and running + # the leakcheck makes an already slow test slower. + def test_untracked_memory_doesnt_increase(self): + # See https://github.com/gevent/gevent/issues/1924 + # and https://github.com/python-greenlet/greenlet/issues/328 + self._only_test_some_versions() + def f(): + return 1 + + ITER = 10000 + def run_it(): + for _ in range(ITER): + greenlet.greenlet(f).switch() + + # Establish baseline + for _ in range(3): + run_it() + + # uss: (Linux, macOS, Windows): aka "Unique Set Size", this is + # the memory which is unique to a process and which would be + # freed if the process was terminated right now. + uss_before = self.get_process_uss() + + for count in range(self.UNTRACK_ATTEMPTS): + uss_before = max(uss_before, self.get_process_uss()) + run_it() + + uss_after = self.get_process_uss() + if uss_after <= uss_before and count > 1: + break + + self.assertLessEqual(uss_after, uss_before) + + def _check_untracked_memory_thread(self, deallocate_in_thread=True): + self._only_test_some_versions() + # Like the above test, but what if there are a bunch of + # unfinished greenlets in a thread that dies? + # Does it matter if we deallocate in the thread or not? + EXIT_COUNT = [0] + + def f(): + try: + greenlet.getcurrent().parent.switch() + except greenlet.GreenletExit: + EXIT_COUNT[0] += 1 + raise + return 1 + + ITER = 10000 + def run_it(): + glets = [] + for _ in range(ITER): + # Greenlet starts, switches back to us. + # We keep a strong reference to the greenlet though so it doesn't + # get a GreenletExit exception. + g = greenlet.greenlet(f) + glets.append(g) + g.switch() + + return glets + + test = self + + class ThreadFunc: + uss_before = uss_after = 0 + glets = () + ITER = 2 + def __call__(self): + self.uss_before = test.get_process_uss() + + for _ in range(self.ITER): + self.glets += tuple(run_it()) + + for g in self.glets: + test.assertIn('suspended active', str(g)) + # Drop them. + if deallocate_in_thread: + self.glets = () + self.uss_after = test.get_process_uss() + + # Establish baseline + uss_before = uss_after = None + for count in range(self.UNTRACK_ATTEMPTS): + EXIT_COUNT[0] = 0 + thread_func = ThreadFunc() + t = threading.Thread(target=thread_func) + t.start() + t.join(30) + self.assertFalse(t.is_alive()) + + if uss_before is None: + uss_before = thread_func.uss_before + + uss_before = max(uss_before, thread_func.uss_before) + if deallocate_in_thread: + self.assertEqual(thread_func.glets, ()) + self.assertEqual(EXIT_COUNT[0], ITER * thread_func.ITER) + + del thread_func # Deallocate the greenlets; but this won't raise into them + del t + if not deallocate_in_thread: + self.assertEqual(EXIT_COUNT[0], 0) + if deallocate_in_thread: + self.wait_for_pending_cleanups() + + uss_after = self.get_process_uss() + # See if we achieve a non-growth state at some point. Break when we do. + if uss_after <= uss_before and count > 1: + break + + self.wait_for_pending_cleanups() + uss_after = self.get_process_uss() + self.assertLessEqual(uss_after, uss_before, "after attempts %d" % (count,)) + + @ignores_leakcheck + # Because we're just trying to track raw memory, not objects, and running + # the leakcheck makes an already slow test slower. + def test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_thread(self): + self._check_untracked_memory_thread(deallocate_in_thread=True) + + @ignores_leakcheck + # Because the main greenlets from the background threads do not exit in a timely fashion, + # we fail the object-based leakchecks. + def test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_main(self): + self._check_untracked_memory_thread(deallocate_in_thread=False) + +if __name__ == '__main__': + __import__('unittest').main() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_stack_saved.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_stack_saved.py new file mode 100644 index 0000000..b362bf9 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_stack_saved.py @@ -0,0 +1,19 @@ +import greenlet +from . import TestCase + + +class Test(TestCase): + + def test_stack_saved(self): + main = greenlet.getcurrent() + self.assertEqual(main._stack_saved, 0) + + def func(): + main.switch(main._stack_saved) + + g = greenlet.greenlet(func) + x = g.switch() + self.assertGreater(x, 0) + self.assertGreater(g._stack_saved, 0) + g.switch() + self.assertEqual(g._stack_saved, 0) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_throw.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_throw.py new file mode 100644 index 0000000..f4f9a14 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_throw.py @@ -0,0 +1,128 @@ +import sys + + +from greenlet import greenlet +from . import TestCase + +def switch(*args): + return greenlet.getcurrent().parent.switch(*args) + + +class ThrowTests(TestCase): + def test_class(self): + def f(): + try: + switch("ok") + except RuntimeError: + switch("ok") + return + switch("fail") + g = greenlet(f) + res = g.switch() + self.assertEqual(res, "ok") + res = g.throw(RuntimeError) + self.assertEqual(res, "ok") + + def test_val(self): + def f(): + try: + switch("ok") + except RuntimeError: + val = sys.exc_info()[1] + if str(val) == "ciao": + switch("ok") + return + switch("fail") + + g = greenlet(f) + res = g.switch() + self.assertEqual(res, "ok") + res = g.throw(RuntimeError("ciao")) + self.assertEqual(res, "ok") + + g = greenlet(f) + res = g.switch() + self.assertEqual(res, "ok") + res = g.throw(RuntimeError, "ciao") + self.assertEqual(res, "ok") + + def test_kill(self): + def f(): + switch("ok") + switch("fail") + g = greenlet(f) + res = g.switch() + self.assertEqual(res, "ok") + res = g.throw() + self.assertTrue(isinstance(res, greenlet.GreenletExit)) + self.assertTrue(g.dead) + res = g.throw() # immediately eaten by the already-dead greenlet + self.assertTrue(isinstance(res, greenlet.GreenletExit)) + + def test_throw_goes_to_original_parent(self): + main = greenlet.getcurrent() + + def f1(): + try: + main.switch("f1 ready to catch") + except IndexError: + return "caught" + return "normal exit" + + def f2(): + main.switch("from f2") + + g1 = greenlet(f1) + g2 = greenlet(f2, parent=g1) + with self.assertRaises(IndexError): + g2.throw(IndexError) + self.assertTrue(g2.dead) + self.assertTrue(g1.dead) + + g1 = greenlet(f1) + g2 = greenlet(f2, parent=g1) + res = g1.switch() + self.assertEqual(res, "f1 ready to catch") + res = g2.throw(IndexError) + self.assertEqual(res, "caught") + self.assertTrue(g2.dead) + self.assertTrue(g1.dead) + + g1 = greenlet(f1) + g2 = greenlet(f2, parent=g1) + res = g1.switch() + self.assertEqual(res, "f1 ready to catch") + res = g2.switch() + self.assertEqual(res, "from f2") + res = g2.throw(IndexError) + self.assertEqual(res, "caught") + self.assertTrue(g2.dead) + self.assertTrue(g1.dead) + + def test_non_traceback_param(self): + with self.assertRaises(TypeError) as exc: + greenlet.getcurrent().throw( + Exception, + Exception(), + self + ) + self.assertEqual(str(exc.exception), + "throw() third argument must be a traceback object") + + def test_instance_of_wrong_type(self): + with self.assertRaises(TypeError) as exc: + greenlet.getcurrent().throw( + Exception(), + BaseException() + ) + + self.assertEqual(str(exc.exception), + "instance exception may not have a separate value") + + def test_not_throwable(self): + with self.assertRaises(TypeError) as exc: + greenlet.getcurrent().throw( + "abc" + ) + self.assertEqual(str(exc.exception), + "exceptions must be classes, or instances, not str") diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_tracing.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_tracing.py new file mode 100644 index 0000000..c044d4b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_tracing.py @@ -0,0 +1,291 @@ +from __future__ import print_function +import sys +import greenlet +import unittest + +from . import TestCase +from . import PY312 + +# https://discuss.python.org/t/cpython-3-12-greenlet-and-tracing-profiling-how-to-not-crash-and-get-correct-results/33144/2 +DEBUG_BUILD_PY312 = ( + PY312 and hasattr(sys, 'gettotalrefcount'), + "Broken on debug builds of Python 3.12" +) + +class SomeError(Exception): + pass + +class GreenletTracer(object): + oldtrace = None + + def __init__(self, error_on_trace=False): + self.actions = [] + self.error_on_trace = error_on_trace + + def __call__(self, *args): + self.actions.append(args) + if self.error_on_trace: + raise SomeError + + def __enter__(self): + self.oldtrace = greenlet.settrace(self) + return self.actions + + def __exit__(self, *args): + greenlet.settrace(self.oldtrace) + + +class TestGreenletTracing(TestCase): + """ + Tests of ``greenlet.settrace()`` + """ + + def test_a_greenlet_tracing(self): + main = greenlet.getcurrent() + def dummy(): + pass + def dummyexc(): + raise SomeError() + + with GreenletTracer() as actions: + g1 = greenlet.greenlet(dummy) + g1.switch() + g2 = greenlet.greenlet(dummyexc) + self.assertRaises(SomeError, g2.switch) + + self.assertEqual(actions, [ + ('switch', (main, g1)), + ('switch', (g1, main)), + ('switch', (main, g2)), + ('throw', (g2, main)), + ]) + + def test_b_exception_disables_tracing(self): + main = greenlet.getcurrent() + def dummy(): + main.switch() + g = greenlet.greenlet(dummy) + g.switch() + with GreenletTracer(error_on_trace=True) as actions: + self.assertRaises(SomeError, g.switch) + self.assertEqual(greenlet.gettrace(), None) + + self.assertEqual(actions, [ + ('switch', (main, g)), + ]) + + def test_set_same_tracer_twice(self): + # https://github.com/python-greenlet/greenlet/issues/332 + # Our logic in asserting that the tracefunction should + # gain a reference was incorrect if the same tracefunction was set + # twice. + tracer = GreenletTracer() + with tracer: + greenlet.settrace(tracer) + + +class PythonTracer(object): + oldtrace = None + + def __init__(self): + self.actions = [] + + def __call__(self, frame, event, arg): + # Record the co_name so we have an idea what function we're in. + self.actions.append((event, frame.f_code.co_name)) + + def __enter__(self): + self.oldtrace = sys.setprofile(self) + return self.actions + + def __exit__(self, *args): + sys.setprofile(self.oldtrace) + +def tpt_callback(): + return 42 + +class TestPythonTracing(TestCase): + """ + Tests of the interaction of ``sys.settrace()`` + with greenlet facilities. + + NOTE: Most of this is probably CPython specific. + """ + + maxDiff = None + + def test_trace_events_trivial(self): + with PythonTracer() as actions: + tpt_callback() + # If we use the sys.settrace instead of setprofile, we get + # this: + + # self.assertEqual(actions, [ + # ('call', 'tpt_callback'), + # ('call', '__exit__'), + # ]) + + self.assertEqual(actions, [ + ('return', '__enter__'), + ('call', 'tpt_callback'), + ('return', 'tpt_callback'), + ('call', '__exit__'), + ('c_call', '__exit__'), + ]) + + def _trace_switch(self, glet): + with PythonTracer() as actions: + glet.switch() + return actions + + def _check_trace_events_func_already_set(self, glet): + actions = self._trace_switch(glet) + self.assertEqual(actions, [ + ('return', '__enter__'), + ('c_call', '_trace_switch'), + ('call', 'run'), + ('call', 'tpt_callback'), + ('return', 'tpt_callback'), + ('return', 'run'), + ('c_return', '_trace_switch'), + ('call', '__exit__'), + ('c_call', '__exit__'), + ]) + + def test_trace_events_into_greenlet_func_already_set(self): + def run(): + return tpt_callback() + + self._check_trace_events_func_already_set(greenlet.greenlet(run)) + + def test_trace_events_into_greenlet_subclass_already_set(self): + class X(greenlet.greenlet): + def run(self): + return tpt_callback() + self._check_trace_events_func_already_set(X()) + + def _check_trace_events_from_greenlet_sets_profiler(self, g, tracer): + g.switch() + tpt_callback() + tracer.__exit__() + self.assertEqual(tracer.actions, [ + ('return', '__enter__'), + ('call', 'tpt_callback'), + ('return', 'tpt_callback'), + ('return', 'run'), + ('call', 'tpt_callback'), + ('return', 'tpt_callback'), + ('call', '__exit__'), + ('c_call', '__exit__'), + ]) + + + def test_trace_events_from_greenlet_func_sets_profiler(self): + tracer = PythonTracer() + def run(): + tracer.__enter__() + return tpt_callback() + + self._check_trace_events_from_greenlet_sets_profiler(greenlet.greenlet(run), + tracer) + + def test_trace_events_from_greenlet_subclass_sets_profiler(self): + tracer = PythonTracer() + class X(greenlet.greenlet): + def run(self): + tracer.__enter__() + return tpt_callback() + + self._check_trace_events_from_greenlet_sets_profiler(X(), tracer) + + @unittest.skipIf(*DEBUG_BUILD_PY312) + def test_trace_events_multiple_greenlets_switching(self): + tracer = PythonTracer() + + g1 = None + g2 = None + + def g1_run(): + tracer.__enter__() + tpt_callback() + g2.switch() + tpt_callback() + return 42 + + def g2_run(): + tpt_callback() + tracer.__exit__() + tpt_callback() + g1.switch() + + g1 = greenlet.greenlet(g1_run) + g2 = greenlet.greenlet(g2_run) + + x = g1.switch() + self.assertEqual(x, 42) + tpt_callback() # ensure not in the trace + self.assertEqual(tracer.actions, [ + ('return', '__enter__'), + ('call', 'tpt_callback'), + ('return', 'tpt_callback'), + ('c_call', 'g1_run'), + ('call', 'g2_run'), + ('call', 'tpt_callback'), + ('return', 'tpt_callback'), + ('call', '__exit__'), + ('c_call', '__exit__'), + ]) + + @unittest.skipIf(*DEBUG_BUILD_PY312) + def test_trace_events_multiple_greenlets_switching_siblings(self): + # Like the first version, but get both greenlets running first + # as "siblings" and then establish the tracing. + tracer = PythonTracer() + + g1 = None + g2 = None + + def g1_run(): + greenlet.getcurrent().parent.switch() + tracer.__enter__() + tpt_callback() + g2.switch() + tpt_callback() + return 42 + + def g2_run(): + greenlet.getcurrent().parent.switch() + + tpt_callback() + tracer.__exit__() + tpt_callback() + g1.switch() + + g1 = greenlet.greenlet(g1_run) + g2 = greenlet.greenlet(g2_run) + + # Start g1 + g1.switch() + # And it immediately returns control to us. + # Start g2 + g2.switch() + # Which also returns. Now kick of the real part of the + # test. + x = g1.switch() + self.assertEqual(x, 42) + + tpt_callback() # ensure not in the trace + self.assertEqual(tracer.actions, [ + ('return', '__enter__'), + ('call', 'tpt_callback'), + ('return', 'tpt_callback'), + ('c_call', 'g1_run'), + ('call', 'tpt_callback'), + ('return', 'tpt_callback'), + ('call', '__exit__'), + ('c_call', '__exit__'), + ]) + + +if __name__ == '__main__': + unittest.main() diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_version.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_version.py new file mode 100644 index 0000000..96c17cf --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_version.py @@ -0,0 +1,41 @@ +#! /usr/bin/env python +from __future__ import absolute_import +from __future__ import print_function + +import sys +import os +from unittest import TestCase as NonLeakingTestCase + +import greenlet + +# No reason to run this multiple times under leakchecks, +# it doesn't do anything. +class VersionTests(NonLeakingTestCase): + def test_version(self): + def find_dominating_file(name): + if os.path.exists(name): + return name + + tried = [] + here = os.path.abspath(os.path.dirname(__file__)) + for i in range(10): + up = ['..'] * i + path = [here] + up + [name] + fname = os.path.join(*path) + fname = os.path.abspath(fname) + tried.append(fname) + if os.path.exists(fname): + return fname + raise AssertionError("Could not find file " + name + "; checked " + str(tried)) + + try: + setup_py = find_dominating_file('setup.py') + except AssertionError as e: + self.skipTest("Unable to find setup.py; must be out of tree. " + str(e)) + + + invoke_setup = "%s %s --version" % (sys.executable, setup_py) + with os.popen(invoke_setup) as f: + sversion = f.read().strip() + + self.assertEqual(sversion, greenlet.__version__) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_weakref.py b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_weakref.py new file mode 100644 index 0000000..05a38a7 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/greenlet/tests/test_weakref.py @@ -0,0 +1,35 @@ +import gc +import weakref + + +import greenlet +from . import TestCase + +class WeakRefTests(TestCase): + def test_dead_weakref(self): + def _dead_greenlet(): + g = greenlet.greenlet(lambda: None) + g.switch() + return g + o = weakref.ref(_dead_greenlet()) + gc.collect() + self.assertEqual(o(), None) + + def test_inactive_weakref(self): + o = weakref.ref(greenlet.greenlet()) + gc.collect() + self.assertEqual(o(), None) + + def test_dealloc_weakref(self): + seen = [] + def worker(): + try: + greenlet.getcurrent().parent.switch() + finally: + seen.append(g()) + g = greenlet.greenlet(worker) + g.switch() + g2 = greenlet.greenlet(lambda: None, g) + g = weakref.ref(g2) + g2 = None + self.assertEqual(seen, [None]) diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/LICENSE.txt b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/LICENSE.txt new file mode 100644 index 0000000..7b190ca --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/METADATA b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/METADATA new file mode 100644 index 0000000..ddf5464 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/RECORD b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/RECORD new file mode 100644 index 0000000..255db20 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/WHEEL b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous-2.2.0.dist-info/WHEEL new file mode 100644 index 0000000..3b5e64b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/__init__.py new file mode 100644 index 0000000..ea55256 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/_json.py b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/_json.py new file mode 100644 index 0000000..fc23fea --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/encoding.py b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/encoding.py new file mode 100644 index 0000000..f5ca80f --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/exc.py b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/exc.py new file mode 100644 index 0000000..a75adcd --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/py.typed b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/serializer.py b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/serializer.py new file mode 100644 index 0000000..5ddf387 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/signer.py b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/signer.py new file mode 100644 index 0000000..e324dc0 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/timed.py b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/timed.py new file mode 100644 index 0000000..7384375 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/url_safe.py b/flask_auth_app/authenv/lib/python3.10/site-packages/itsdangerous/url_safe.py new file mode 100644 index 0000000..56a0793 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/INSTALLER b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/LICENSE.txt b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/LICENSE.txt new file mode 100644 index 0000000..c37cae4 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/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/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/METADATA b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/METADATA new file mode 100644 index 0000000..265cc32 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/METADATA @@ -0,0 +1,76 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 3.1.4 +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 +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 + +.. code-block:: jinja + + {% extends "base.html" %} + {% block title %}Members{% endblock %} + {% block content %} +

+ {% endblock %} + + +## Donate + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/RECORD b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/RECORD new file mode 100644 index 0000000..d1d87b7 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/RECORD @@ -0,0 +1,57 @@ +jinja2-3.1.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jinja2-3.1.4.dist-info/LICENSE.txt,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +jinja2-3.1.4.dist-info/METADATA,sha256=R_brzpPQVBvpGcsm-WbrtgotO7suQ1D0F-qkhTzeEfY,2640 +jinja2-3.1.4.dist-info/RECORD,, +jinja2-3.1.4.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +jinja2-3.1.4.dist-info/entry_points.txt,sha256=OL85gYU1eD8cuPlikifFngXpeBjaxl6rIJ8KkC_3r-I,58 +jinja2/__init__.py,sha256=wIl45IM20KGw-kfr7jJhaBxxX5g4-kihlBYjxopX7Pw,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=JXKWCAXmTx0iZB4-hAsF50vgjxw_RJTjiLOlGGTBso0,2477 +jinja2/bccache.py,sha256=gh0qs9rulnXo0PhX5jTJy2UHzI8wFnQ63o_vw7nhzRg,14061 +jinja2/compiler.py,sha256=dpV-n6_iQUP4uSwlXwGUavJmwjvXdyxKzJ-AonFjPBk,72271 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=iWJ432RadxJNnaMOPrjIDInz50UEgni3_HKuFXi2vuQ,6299 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=xhFkmxO0CESA76Ki5tz4XWq9yzGu-t0p93JCCVBVNps,61538 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=igsBH7c6C0byHaOtMbE-ugpt4GjLGgR-ywskyXtKgq8,31877 +jinja2/filters.py,sha256=bKeqjFjjz88TkHVLSyyMIEB75CzAN6b3Airgx0phJDg,54611 +jinja2/idtracking.py,sha256=GfNmadir4oDALVxzn3DL9YInhJDr69ebXeA2ygfuCGA,10704 +jinja2/lexer.py,sha256=xnWWXhPndHFsoqzpc5VTjheDE9JuKk9MUo9DZkrM8Os,29754 +jinja2/loaders.py,sha256=ru0GIWHo5KiHJi7_MoI_LvGDoBBvP6rd0hiC1ReaTwk,23167 +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=DV1iF1FR2Rsaj_5zl8rmx7j6Bj4S8iLHoYsvJ0bfEis,39890 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=POXT3tKNKJRENx2CymwUsOOXH2JwGPjW702njB5__cQ,33435 +jinja2/sandbox.py,sha256=TJjBNS9qRJ2ZgBMWdAgRBpyDLOHea2kT-2mk4PrjYx0,14616 +jinja2/tests.py,sha256=VLsBhVFnWg-PxSBz1MhRnNWgP1ovXk3neO1FLQMeC9Q,5926 +jinja2/utils.py,sha256=nV7IpWLvRCMyHW1irBAK8CIPAnOFfkb2ukggDBjbBEY,23952 +jinja2/visitor.py,sha256=EcnL1PIwf_4RVCOMxsRNuR8AXHbS1qfAdMOE2ngKJz4,3557 diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/WHEEL b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/WHEEL new file mode 100644 index 0000000..3b5e64b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.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/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/entry_points.txt b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/entry_points.txt new file mode 100644 index 0000000..abc3eae --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2-3.1.4.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2=jinja2.ext:babel_extract[i18n] + diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/__init__.py b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/__init__.py new file mode 100644 index 0000000..2f0b5b2 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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.4" diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/_identifier.py b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/_identifier.py new file mode 100644 index 0000000..928c150 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/async_utils.py b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/async_utils.py new file mode 100644 index 0000000..e65219e --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/async_utils.py @@ -0,0 +1,84 @@ +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 + +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 t.cast("V", value) + + +async def auto_aiter( + iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> "t.AsyncIterator[V]": + if hasattr(iterable, "__aiter__"): + async for item in t.cast("t.AsyncIterable[V]", iterable): + yield item + else: + for item in iterable: + yield item + + +async def auto_to_list( + value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> t.List["V"]: + return [x async for x in auto_aiter(value)] diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/bccache.py b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/bccache.py new file mode 100644 index 0000000..ada8b09 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/compiler.py b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/compiler.py new file mode 100644 index 0000000..2740717 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/compiler.py @@ -0,0 +1,1960 @@ +"""Compiles nodes from the parser into Python code.""" + +import typing as t +from contextlib import contextmanager +from functools import update_wrapper +from io import StringIO +from itertools import chain +from keyword import iskeyword as is_python_keyword + +from markupsafe import escape +from markupsafe import Markup + +from . import nodes +from .exceptions import TemplateAssertionError +from .idtracking import Symbols +from .idtracking import VAR_LOAD_ALIAS +from .idtracking import VAR_LOAD_PARAMETER +from .idtracking import VAR_LOAD_RESOLVE +from .idtracking import VAR_LOAD_UNDEFINED +from .nodes import EvalContext +from .optimizer import Optimizer +from .utils import _PassArg +from .utils import concat +from .visitor import NodeVisitor + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .environment import Environment + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + +operators = { + "eq": "==", + "ne": "!=", + "gt": ">", + "gteq": ">=", + "lt": "<", + "lteq": "<=", + "in": "in", + "notin": "not in", +} + + +def optimizeconst(f: F) -> F: + def new_func( + self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any + ) -> t.Any: + # Only optimize if the frame is not volatile + if self.optimizer is not None and not frame.eval_ctx.volatile: + new_node = self.optimizer.visit(node, frame.eval_ctx) + + if new_node != node: + return self.visit(new_node, frame) + + return f(self, node, frame, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + +def _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed and op in self.environment.intercepted_binops # type: ignore + ): + self.write(f"environment.call_binop(context, {op!r}, ") + self.visit(node.left, frame) + self.write(", ") + self.visit(node.right, frame) + else: + self.write("(") + self.visit(node.left, frame) + self.write(f" {op} ") + self.visit(node.right, frame) + + self.write(")") + + return visitor + + +def _make_unop( + op: str, +) -> t.Callable[["CodeGenerator", nodes.UnaryExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed and op in self.environment.intercepted_unops # type: ignore + ): + self.write(f"environment.call_unop(context, {op!r}, ") + self.visit(node.node, frame) + else: + self.write("(" + op) + self.visit(node.node, frame) + + self.write(")") + + return visitor + + +def generate( + node: nodes.Template, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, +) -> t.Optional[str]: + """Generate the python source for a node tree.""" + if not isinstance(node, nodes.Template): + raise TypeError("Can't compile non template nodes") + + generator = environment.code_generator_class( + environment, name, filename, stream, defer_init, optimized + ) + generator.visit(node) + + if stream is None: + return generator.stream.getvalue() # type: ignore + + return None + + +def has_safe_repr(value: t.Any) -> bool: + """Does the node have a safe representation?""" + if value is None or value is NotImplemented or value is Ellipsis: + return True + + if type(value) in {bool, int, float, complex, range, str, Markup}: + return True + + if type(value) in {tuple, list, set, frozenset}: + return all(has_safe_repr(v) for v in value) + + if type(value) is dict: # 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) -> "Frame": + """Create a copy of the current one.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.symbols = self.symbols.copy() + return rv + + def inner(self, isolated: bool = False) -> "Frame": + """Return an inner frame.""" + if isolated: + return Frame(self.eval_ctx, level=self.symbols.level + 1) + return Frame(self.eval_ctx, self) + + def soft(self) -> "Frame": + """Return a soft frame. A soft frame may not be modified as + standalone thing as it shares the resources with the frame it + was created of, but it's not a rootlevel frame any longer. + + This is only used to implement if-statements and conditional + expressions. + """ + rv = self.copy() + rv.rootlevel = False + rv.soft_frame = True + return rv + + __copy__ = copy + + +class VisitorExit(RuntimeError): + """Exception used by the `UndeclaredNameVisitor` to signal a stop.""" + + +class DependencyFinderVisitor(NodeVisitor): + """A visitor that collects filter and test calls.""" + + def __init__(self) -> None: + self.filters: t.Set[str] = set() + self.tests: t.Set[str] = set() + + def visit_Filter(self, node: nodes.Filter) -> None: + self.generic_visit(node) + self.filters.add(node.name) + + def visit_Test(self, node: nodes.Test) -> None: + self.generic_visit(node) + self.tests.add(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting at blocks.""" + + +class UndeclaredNameVisitor(NodeVisitor): + """A visitor that checks if a name is accessed without being + declared. This is different from the frame visitor as it will + not stop at closure frames. + """ + + def __init__(self, names: t.Iterable[str]) -> None: + self.names = set(names) + self.undeclared: t.Set[str] = set() + + def visit_Name(self, node: nodes.Name) -> None: + if node.ctx == "load" and node.name in self.names: + self.undeclared.add(node.name) + if self.undeclared == self.names: + raise VisitorExit() + else: + self.names.discard(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting a blocks.""" + + +class CompilerExit(Exception): + """Raised if the compiler encountered a situation where it just + doesn't make sense to further process the code. Any block that + raises such an exception is not further processed. + """ + + +class CodeGenerator(NodeVisitor): + def __init__( + self, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, + ) -> None: + if stream is None: + stream = StringIO() + self.environment = environment + self.name = name + self.filename = filename + self.stream = stream + self.created_block_context = False + self.defer_init = defer_init + self.optimizer: t.Optional[Optimizer] = None + + if optimized: + self.optimizer = Optimizer(environment) + + # aliases for imports + self.import_aliases: t.Dict[str, str] = {} + + # a registry for all blocks. Because blocks are moved out + # into the global python scope they are registered here + self.blocks: t.Dict[str, nodes.Block] = {} + + # the number of extends statements so far + self.extends_so_far = 0 + + # some templates have a rootlevel extends. In this case we + # can safely assume that we're a child template and do some + # more optimizations. + self.has_known_extends = False + + # the current line number + self.code_lineno = 1 + + # registry of all filters and tests (global, not block local) + self.tests: t.Dict[str, str] = {} + self.filters: t.Dict[str, str] = {} + + # the debug information + self.debug_info: t.List[t.Tuple[int, int]] = [] + self._write_debug_info: t.Optional[int] = None + + # the number of new lines before the next write() + self._new_lines = 0 + + # the line number of the last written statement + self._last_line = 0 + + # true if nothing was written so far. + self._first_write = True + + # used by the `temporary_identifier` method to get new + # unique, temporary identifier + self._last_identifier = 0 + + # the current indentation + self._indentation = 0 + + # Tracks toplevel assignments + self._assign_stack: t.List[t.Set[str]] = [] + + # Tracks parameter definition blocks + self._param_def_block: t.List[t.Set[str]] = [] + + # Tracks the current context. + self._context_reference_stack = ["context"] + + @property + def optimized(self) -> bool: + return self.optimizer is not None + + # -- Various compilation helpers + + def fail(self, msg: str, lineno: int) -> "te.NoReturn": + """Fail with a :exc:`TemplateAssertionError`.""" + raise TemplateAssertionError(msg, lineno, self.name, self.filename) + + def temporary_identifier(self) -> str: + """Get a new unique identifier.""" + self._last_identifier += 1 + return f"t_{self._last_identifier}" + + def buffer(self, frame: Frame) -> None: + """Enable buffering for the frame from that point onwards.""" + frame.buffer = self.temporary_identifier() + self.writeline(f"{frame.buffer} = []") + + def return_buffer_contents( + self, frame: Frame, force_unescaped: bool = False + ) -> None: + """Return the buffer contents of the frame.""" + if not force_unescaped: + if frame.eval_ctx.volatile: + self.writeline("if context.eval_ctx.autoescape:") + self.indent() + self.writeline(f"return Markup(concat({frame.buffer}))") + self.outdent() + self.writeline("else:") + self.indent() + self.writeline(f"return concat({frame.buffer})") + self.outdent() + return + elif frame.eval_ctx.autoescape: + self.writeline(f"return Markup(concat({frame.buffer}))") + return + self.writeline(f"return concat({frame.buffer})") + + def indent(self) -> None: + """Indent by one.""" + self._indentation += 1 + + def outdent(self, step: int = 1) -> None: + """Outdent by step.""" + self._indentation -= step + + def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None: + """Yield or write into the frame buffer.""" + if frame.buffer is None: + self.writeline("yield ", node) + else: + self.writeline(f"{frame.buffer}.append(", node) + + def end_write(self, frame: Frame) -> None: + """End the writing process started by `start_write`.""" + if frame.buffer is not None: + self.write(")") + + def simple_write( + self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None + ) -> None: + """Simple shortcut for start_write + write + end_write.""" + self.start_write(frame, node) + self.write(s) + self.end_write(frame) + + def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None: + """Visit a list of nodes as block in a frame. If the current frame + is no buffer a dummy ``if 0: yield None`` is written automatically. + """ + try: + self.writeline("pass") + for node in nodes: + self.visit(node, frame) + except CompilerExit: + pass + + def write(self, x: str) -> None: + """Write a string into the output stream.""" + if self._new_lines: + if not self._first_write: + self.stream.write("\n" * self._new_lines) + self.code_lineno += self._new_lines + if self._write_debug_info is not None: + self.debug_info.append((self._write_debug_info, self.code_lineno)) + self._write_debug_info = None + self._first_write = False + self.stream.write(" " * self._indentation) + self._new_lines = 0 + self.stream.write(x) + + def writeline( + self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0 + ) -> None: + """Combination of newline and write.""" + self.newline(node, extra) + self.write(x) + + def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None: + """Add one or more newlines before the next write.""" + self._new_lines = max(self._new_lines, 1 + extra) + if node is not None and node.lineno != self._last_line: + self._write_debug_info = node.lineno + self._last_line = node.lineno + + def signature( + self, + node: t.Union[nodes.Call, nodes.Filter, nodes.Test], + frame: Frame, + extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> None: + """Writes a function call to the stream for the current node. + A leading comma is added automatically. The extra keyword + arguments may not include python keywords otherwise a syntax + error could occur. The extra keyword arguments should be given + as python dict. + """ + # if any of the given keyword arguments is a python keyword + # we have to make sure that no invalid call is created. + kwarg_workaround = any( + is_python_keyword(t.cast(str, k)) + for k in chain((x.key for x in node.kwargs), extra_kwargs or ()) + ) + + for arg in node.args: + self.write(", ") + self.visit(arg, frame) + + if not kwarg_workaround: + for kwarg in node.kwargs: + self.write(", ") + self.visit(kwarg, frame) + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f", {key}={value}") + if node.dyn_args: + self.write(", *") + self.visit(node.dyn_args, frame) + + if kwarg_workaround: + if node.dyn_kwargs is not None: + self.write(", **dict({") + else: + self.write(", **{") + for kwarg in node.kwargs: + self.write(f"{kwarg.key!r}: ") + self.visit(kwarg.value, frame) + self.write(", ") + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f"{key!r}: {value}, ") + if node.dyn_kwargs is not None: + self.write("}, **") + self.visit(node.dyn_kwargs, frame) + self.write(")") + else: + self.write("}") + + elif node.dyn_kwargs is not None: + self.write(", **") + self.visit(node.dyn_kwargs, frame) + + def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None: + """Find all filter and test names used in the template and + assign them to variables in the compiled namespace. Checking + that the names are registered with the environment is done when + compiling the Filter and Test nodes. If the node is in an If or + CondExpr node, the check is done at runtime instead. + + .. versionchanged:: 3.0 + Filters and tests in If and CondExpr nodes are checked at + runtime instead of compile time. + """ + visitor = DependencyFinderVisitor() + + for node in nodes: + visitor.visit(node) + + for id_map, names, dependency in ( + (self.filters, visitor.filters, "filters"), + ( + self.tests, + visitor.tests, + "tests", + ), + ): + for name in sorted(names): + if name not in id_map: + id_map[name] = self.temporary_identifier() + + # add check during runtime that dependencies used inside of executed + # blocks are defined, as this step may be skipped during compile time + self.writeline("try:") + self.indent() + self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]") + self.outdent() + self.writeline("except KeyError:") + self.indent() + self.writeline("@internalcode") + self.writeline(f"def {id_map[name]}(*unused):") + self.indent() + self.writeline( + f'raise TemplateRuntimeError("No {dependency[:-1]}' + f' named {name!r} found.")' + ) + self.outdent() + self.outdent() + + def enter_frame(self, frame: Frame) -> None: + undefs = [] + for target, (action, param) in frame.symbols.loads.items(): + if action == VAR_LOAD_PARAMETER: + pass + elif action == VAR_LOAD_RESOLVE: + self.writeline(f"{target} = {self.get_resolve_func()}({param!r})") + elif action == VAR_LOAD_ALIAS: + self.writeline(f"{target} = {param}") + elif action == VAR_LOAD_UNDEFINED: + undefs.append(target) + else: + raise NotImplementedError("unknown load instruction") + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None: + if not with_python_scope: + undefs = [] + for target in frame.symbols.loads: + undefs.append(target) + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str: + return async_value if self.environment.is_async else sync_value + + def func(self, name: str) -> str: + return f"{self.choose_async()}def {name}" + + def macro_body( + self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame + ) -> t.Tuple[Frame, MacroRef]: + """Dump the function def of a macro or call block.""" + frame = frame.inner() + frame.symbols.analyze_node(node) + macro_ref = MacroRef(node) + + explicit_caller = None + skip_special_params = set() + args = [] + + for idx, arg in enumerate(node.args): + if arg.name == "caller": + explicit_caller = idx + if arg.name in ("kwargs", "varargs"): + skip_special_params.add(arg.name) + args.append(frame.symbols.ref(arg.name)) + + undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs")) + + if "caller" in undeclared: + # In older Jinja versions there was a bug that allowed caller + # to retain the special behavior even if it was mentioned in + # the argument list. However thankfully this was only really + # working if it was the last argument. So we are explicitly + # checking this now and error out if it is anywhere else in + # the argument list. + if explicit_caller is not None: + try: + node.defaults[explicit_caller - len(node.args)] + except IndexError: + self.fail( + "When defining macros or call blocks the " + 'special "caller" argument must be omitted ' + "or be given a default.", + node.lineno, + ) + else: + args.append(frame.symbols.declare_parameter("caller")) + macro_ref.accesses_caller = True + if "kwargs" in undeclared and "kwargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("kwargs")) + macro_ref.accesses_kwargs = True + if "varargs" in undeclared and "varargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("varargs")) + macro_ref.accesses_varargs = True + + # macros are delayed, they never require output checks + frame.require_output_check = False + frame.symbols.analyze_node(node) + self.writeline(f"{self.func('macro')}({', '.join(args)}):", node) + self.indent() + + self.buffer(frame) + self.enter_frame(frame) + + self.push_parameter_definitions(frame) + for idx, arg in enumerate(node.args): + ref = frame.symbols.ref(arg.name) + self.writeline(f"if {ref} is missing:") + self.indent() + try: + default = node.defaults[idx - len(node.args)] + except IndexError: + self.writeline( + f'{ref} = undefined("parameter {arg.name!r} was not provided",' + f" name={arg.name!r})" + ) + else: + self.writeline(f"{ref} = ") + self.visit(default, frame) + self.mark_parameter_stored(ref) + self.outdent() + self.pop_parameter_definitions() + + self.blockvisit(node.body, frame) + self.return_buffer_contents(frame, force_unescaped=True) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + return frame, macro_ref + + def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None: + """Dump the macro definition for the def created by macro_body.""" + arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args) + name = getattr(macro_ref.node, "name", None) + if len(macro_ref.node.args) == 1: + arg_tuple += "," + self.write( + f"Macro(environment, macro, {name!r}, ({arg_tuple})," + f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r}," + f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)" + ) + + def position(self, node: nodes.Node) -> str: + """Return a human readable position for the node.""" + rv = f"line {node.lineno}" + if self.name is not None: + rv = f"{rv} in {self.name!r}" + return rv + + def dump_local_context(self, frame: Frame) -> str: + items_kv = ", ".join( + f"{name!r}: {target}" + for name, target in frame.symbols.dump_stores().items() + ) + return f"{{{items_kv}}}" + + def write_commons(self) -> None: + """Writes a common preamble that is used by root and block functions. + Primarily this sets up common local helpers and enforces a generator + through a dead branch. + """ + self.writeline("resolve = context.resolve_or_missing") + self.writeline("undefined = environment.undefined") + self.writeline("concat = environment.concat") + # always use the standard Undefined class for the implicit else of + # conditional expressions + self.writeline("cond_expr_undefined = Undefined") + self.writeline("if 0: yield None") + + def push_parameter_definitions(self, frame: Frame) -> None: + """Pushes all parameter targets from the given frame into a local + stack that permits tracking of yet to be assigned parameters. In + particular this enables the optimization from `visit_Name` to skip + undefined expressions for parameters in macros as macros can reference + otherwise unbound parameters. + """ + self._param_def_block.append(frame.symbols.dump_param_targets()) + + def pop_parameter_definitions(self) -> None: + """Pops the current parameter definitions set.""" + self._param_def_block.pop() + + def mark_parameter_stored(self, target: str) -> None: + """Marks a parameter in the current parameter definitions as stored. + This will skip the enforced undefined checks. + """ + if self._param_def_block: + self._param_def_block[-1].discard(target) + + def push_context_reference(self, target: str) -> None: + self._context_reference_stack.append(target) + + def pop_context_reference(self) -> None: + self._context_reference_stack.pop() + + def get_context_ref(self) -> str: + return self._context_reference_stack[-1] + + def get_resolve_func(self) -> str: + target = self._context_reference_stack[-1] + if target == "context": + return "resolve" + return f"{target}.resolve" + + def derive_context(self, frame: Frame) -> str: + return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})" + + def parameter_is_undeclared(self, target: str) -> bool: + """Checks if a given target is an undeclared parameter.""" + if not self._param_def_block: + return False + return target in self._param_def_block[-1] + + def push_assign_tracking(self) -> None: + """Pushes a new layer for assignment tracking.""" + self._assign_stack.append(set()) + + def pop_assign_tracking(self, frame: Frame) -> None: + """Pops the topmost level for assignment tracking and updates the + context variables if necessary. + """ + vars = self._assign_stack.pop() + if ( + not frame.block_frame + and not frame.loop_frame + and not frame.toplevel + or not vars + ): + return + public_names = [x for x in vars if x[:1] != "_"] + if len(vars) == 1: + name = next(iter(vars)) + ref = frame.symbols.ref(name) + if frame.loop_frame: + self.writeline(f"_loop_vars[{name!r}] = {ref}") + return + if frame.block_frame: + self.writeline(f"_block_vars[{name!r}] = {ref}") + return + self.writeline(f"context.vars[{name!r}] = {ref}") + else: + if frame.loop_frame: + self.writeline("_loop_vars.update({") + elif frame.block_frame: + self.writeline("_block_vars.update({") + else: + self.writeline("context.vars.update({") + for idx, name in enumerate(vars): + if idx: + self.write(", ") + ref = frame.symbols.ref(name) + self.write(f"{name!r}: {ref}") + self.write("})") + if not frame.block_frame and not frame.loop_frame and public_names: + if len(public_names) == 1: + self.writeline(f"context.exported_vars.add({public_names[0]!r})") + else: + names_str = ", ".join(map(repr, public_names)) + self.writeline(f"context.exported_vars.update(({names_str}))") + + # -- Statement Visitors + + def visit_Template( + self, node: nodes.Template, frame: t.Optional[Frame] = None + ) -> None: + assert frame is None, "no root frame allowed" + eval_ctx = EvalContext(self.environment, self.name) + + from .runtime import 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( + "async for event in parent_template.root_render_func(context):" + ) + self.indent() + self.writeline("yield event") + self.outdent() + self.outdent(1 + (not self.has_known_extends)) + + # at this point we now have the blocks collected and can visit them too. + for name, block in self.blocks.items(): + self.writeline( + f"{self.func('block_' + name)}(context, missing=missing{envenv}):", + block, + 1, + ) + self.indent() + self.write_commons() + # It's important that we do not make this frame a child of the + # toplevel template. This would cause a variety of + # interesting issues with identifier tracking. + block_frame = Frame(eval_ctx) + block_frame.block_frame = True + undeclared = find_undeclared(block.body, ("self", "super")) + if "self" in undeclared: + ref = block_frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + if "super" in undeclared: + ref = block_frame.symbols.declare_parameter("super") + self.writeline(f"{ref} = context.super({name!r}, block_{name})") + block_frame.symbols.analyze_node(block) + block_frame.block = name + self.writeline("_block_vars = {}") + self.enter_frame(block_frame) + self.pull_dependencies(block.body) + self.blockvisit(block.body, block_frame) + self.leave_frame(block_frame, with_python_scope=True) + self.outdent() + + blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks) + self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1) + debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info) + self.writeline(f"debug_info = {debug_kv_str!r}") + + def visit_Block(self, node: nodes.Block, frame: Frame) -> None: + """Call a block and register it for the template.""" + level = 0 + if frame.toplevel: + # if we know that we are a child template, there is no need to + # check if we are one + if self.has_known_extends: + return + if self.extends_so_far > 0: + self.writeline("if parent_template is None:") + self.indent() + level += 1 + + if node.scoped: + context = self.derive_context(frame) + else: + context = self.get_context_ref() + + if node.required: + self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node) + self.indent() + self.writeline( + f'raise TemplateRuntimeError("Required block {node.name!r} not found")', + node, + ) + self.outdent() + + if not self.environment.is_async and frame.buffer is None: + self.writeline( + f"yield from context.blocks[{node.name!r}][0]({context})", node + ) + else: + self.writeline( + f"{self.choose_async()}for event in" + f" context.blocks[{node.name!r}][0]({context}):", + node, + ) + self.indent() + self.simple_write("event", frame) + self.outdent() + + self.outdent(level) + + def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None: + """Calls the extender.""" + if not frame.toplevel: + self.fail("cannot use extend from a non top-level scope", node.lineno) + + # if the number of extends statements in general is zero so + # far, we don't have to add a check if something extended + # the template before this one. + if self.extends_so_far > 0: + # if we have a known extends we just add a template runtime + # error into the generated code. We could catch that at compile + # time too, but i welcome it not to confuse users by throwing the + # same error at different times just "because we can". + if not self.has_known_extends: + self.writeline("if parent_template is not None:") + self.indent() + self.writeline('raise TemplateRuntimeError("extended multiple times")') + + # if we have a known extends already we don't need that code here + # as we know that the template execution will end here. + if self.has_known_extends: + raise CompilerExit() + else: + self.outdent() + + self.writeline("parent_template = environment.get_template(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + self.writeline("for name, parent_block in parent_template.blocks.items():") + self.indent() + self.writeline("context.blocks.setdefault(name, []).append(parent_block)") + self.outdent() + + # if this extends statement was in the root level we can take + # advantage of that information and simplify the generated code + # in the top level from this point onwards + if frame.rootlevel: + self.has_known_extends = True + + # and now we have one more + self.extends_so_far += 1 + + def visit_Include(self, node: nodes.Include, frame: Frame) -> None: + """Handles includes.""" + if node.ignore_missing: + self.writeline("try:") + self.indent() + + func_name = "get_or_select_template" + if isinstance(node.template, nodes.Const): + if isinstance(node.template.value, str): + func_name = "get_template" + elif isinstance(node.template.value, (tuple, list)): + func_name = "select_template" + elif isinstance(node.template, (nodes.Tuple, nodes.List)): + func_name = "select_template" + + self.writeline(f"template = environment.{func_name}(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + if node.ignore_missing: + self.outdent() + self.writeline("except TemplateNotFound:") + self.indent() + self.writeline("pass") + self.outdent() + self.writeline("else:") + self.indent() + + skip_event_yield = False + if node.with_context: + self.writeline( + f"{self.choose_async()}for event in template.root_render_func(" + "template.new_context(context.get_all(), True," + f" {self.dump_local_context(frame)})):" + ) + elif self.environment.is_async: + self.writeline( + "for event in (await template._get_default_module_async())" + "._body_stream:" + ) + else: + self.writeline("yield from template._get_default_module()._body_stream") + skip_event_yield = True + + if not skip_event_yield: + self.indent() + self.simple_write("event", frame) + self.outdent() + + if node.ignore_missing: + self.outdent() + + def _import_common( + self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame + ) -> None: + self.write(f"{self.choose_async('await ')}environment.get_template(") + self.visit(node.template, frame) + self.write(f", {self.name!r}).") + + if node.with_context: + f_name = f"make_module{self.choose_async('_async')}" + self.write( + f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})" + ) + else: + self.write(f"_get_default_module{self.choose_async('_async')}(context)") + + def visit_Import(self, node: nodes.Import, frame: Frame) -> None: + """Visit regular imports.""" + self.writeline(f"{frame.symbols.ref(node.target)} = ", node) + if frame.toplevel: + self.write(f"context.vars[{node.target!r}] = ") + + self._import_common(node, frame) + + if frame.toplevel and not node.target.startswith("_"): + self.writeline(f"context.exported_vars.discard({node.target!r})") + + def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None: + """Visit named imports.""" + self.newline(node) + self.write("included_template = ") + self._import_common(node, frame) + var_names = [] + discarded_names = [] + for name in node.names: + if isinstance(name, tuple): + name, alias = name + else: + alias = name + self.writeline( + f"{frame.symbols.ref(alias)} =" + f" getattr(included_template, {name!r}, missing)" + ) + self.writeline(f"if {frame.symbols.ref(alias)} is missing:") + self.indent() + message = ( + "the template {included_template.__name__!r}" + f" (imported on {self.position(node)})" + f" does not export the requested name {name!r}" + ) + self.writeline( + f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})" + ) + self.outdent() + if frame.toplevel: + var_names.append(alias) + if not alias.startswith("_"): + discarded_names.append(alias) + + if var_names: + if len(var_names) == 1: + name = var_names[0] + self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}") + else: + names_kv = ", ".join( + f"{name!r}: {frame.symbols.ref(name)}" for name in var_names + ) + self.writeline(f"context.vars.update({{{names_kv}}})") + if discarded_names: + if len(discarded_names) == 1: + self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})") + else: + names_str = ", ".join(map(repr, discarded_names)) + self.writeline( + f"context.exported_vars.difference_update(({names_str}))" + ) + + def visit_For(self, node: nodes.For, frame: Frame) -> None: + loop_frame = frame.inner() + loop_frame.loop_frame = True + test_frame = frame.inner() + else_frame = frame.inner() + + # try to figure out if we have an extended loop. An extended loop + # is necessary if the loop is in recursive mode if the special loop + # variable is accessed in the body if the body is a scoped block. + extended_loop = ( + node.recursive + or "loop" + in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",)) + or any(block.scoped for block in node.find_all(nodes.Block)) + ) + + loop_ref = None + if extended_loop: + loop_ref = loop_frame.symbols.declare_parameter("loop") + + loop_frame.symbols.analyze_node(node, for_branch="body") + if node.else_: + else_frame.symbols.analyze_node(node, for_branch="else") + + if node.test: + loop_filter_func = self.temporary_identifier() + test_frame.symbols.analyze_node(node, for_branch="test") + self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test) + self.indent() + self.enter_frame(test_frame) + self.writeline(self.choose_async("async for ", "for ")) + self.visit(node.target, loop_frame) + self.write(" in ") + self.write(self.choose_async("auto_aiter(fiter)", "fiter")) + self.write(":") + self.indent() + self.writeline("if ", node.test) + self.visit(node.test, test_frame) + self.write(":") + self.indent() + self.writeline("yield ") + self.visit(node.target, loop_frame) + self.outdent(3) + self.leave_frame(test_frame, with_python_scope=True) + + # if we don't have an recursive loop we have to find the shadowed + # variables at that point. Because loops can be nested but the loop + # variable is a special one we have to enforce aliasing for it. + if node.recursive: + self.writeline( + f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node + ) + self.indent() + self.buffer(loop_frame) + + # Use the same buffer for the else frame + else_frame.buffer = loop_frame.buffer + + # make sure the loop variable is a special one and raise a template + # assertion error if a loop tries to write to loop + if extended_loop: + self.writeline(f"{loop_ref} = missing") + + for name in node.find_all(nodes.Name): + if name.ctx == "store" and name.name == "loop": + self.fail( + "Can't assign to special loop variable in for-loop target", + name.lineno, + ) + + if node.else_: + iteration_indicator = self.temporary_identifier() + self.writeline(f"{iteration_indicator} = 1") + + self.writeline(self.choose_async("async for ", "for "), node) + self.visit(node.target, loop_frame) + if extended_loop: + self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(") + else: + self.write(" in ") + + if node.test: + self.write(f"{loop_filter_func}(") + if node.recursive: + self.write("reciter") + else: + if self.environment.is_async and not extended_loop: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async and not extended_loop: + self.write(")") + if node.test: + self.write(")") + + if node.recursive: + self.write(", undefined, loop_render_func, depth):") + else: + self.write(", undefined):" if extended_loop else ":") + + self.indent() + self.enter_frame(loop_frame) + + self.writeline("_loop_vars = {}") + self.blockvisit(node.body, loop_frame) + if node.else_: + self.writeline(f"{iteration_indicator} = 0") + self.outdent() + self.leave_frame( + loop_frame, with_python_scope=node.recursive and not node.else_ + ) + + if node.else_: + self.writeline(f"if {iteration_indicator}:") + self.indent() + self.enter_frame(else_frame) + self.blockvisit(node.else_, else_frame) + self.leave_frame(else_frame) + self.outdent() + + # if the node was recursive we have to return the buffer contents + # and start the iteration code + if node.recursive: + self.return_buffer_contents(loop_frame) + self.outdent() + self.start_write(frame, node) + self.write(f"{self.choose_async('await ')}loop(") + if self.environment.is_async: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async: + self.write(")") + self.write(", loop)") + self.end_write(frame) + + # at the end of the iteration, clear any assignments made in the + # loop from the top level + if self._assign_stack: + self._assign_stack[-1].difference_update(loop_frame.symbols.stores) + + def visit_If(self, node: nodes.If, frame: Frame) -> None: + if_frame = frame.soft() + self.writeline("if ", node) + self.visit(node.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(node.body, if_frame) + self.outdent() + for elif_ in node.elif_: + self.writeline("elif ", elif_) + self.visit(elif_.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(elif_.body, if_frame) + self.outdent() + if node.else_: + self.writeline("else:") + self.indent() + self.blockvisit(node.else_, if_frame) + self.outdent() + + def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None: + macro_frame, macro_ref = self.macro_body(node, frame) + self.newline() + if frame.toplevel: + if not node.name.startswith("_"): + self.write(f"context.exported_vars.add({node.name!r})") + self.writeline(f"context.vars[{node.name!r}] = ") + self.write(f"{frame.symbols.ref(node.name)} = ") + self.macro_def(macro_ref, macro_frame) + + def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None: + call_frame, macro_ref = self.macro_body(node, frame) + self.writeline("caller = ") + self.macro_def(macro_ref, call_frame) + self.start_write(frame, node) + self.visit_Call(node.call, frame, forward_caller=True) + self.end_write(frame) + + def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None: + filter_frame = frame.inner() + filter_frame.symbols.analyze_node(node) + self.enter_frame(filter_frame) + self.buffer(filter_frame) + self.blockvisit(node.body, filter_frame) + self.start_write(frame, node) + self.visit_Filter(node.filter, filter_frame) + self.end_write(frame) + self.leave_frame(filter_frame) + + def visit_With(self, node: nodes.With, frame: Frame) -> None: + with_frame = frame.inner() + with_frame.symbols.analyze_node(node) + self.enter_frame(with_frame) + for target, expr in zip(node.targets, node.values): + self.newline() + self.visit(target, with_frame) + self.write(" = ") + self.visit(expr, frame) + self.blockvisit(node.body, with_frame) + self.leave_frame(with_frame) + + def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None: + self.newline(node) + self.visit(node.node, frame) + + class _FinalizeInfo(t.NamedTuple): + const: t.Optional[t.Callable[..., str]] + src: t.Optional[str] + + @staticmethod + def _default_finalize(value: t.Any) -> t.Any: + """The default finalize function if the environment isn't + configured with one. Or, if the environment has one, this is + called on that function's output for constants. + """ + return str(value) + + _finalize: t.Optional[_FinalizeInfo] = None + + def _make_finalize(self) -> _FinalizeInfo: + """Build the finalize function to be used on constants and at + runtime. Cached so it's only created once for all output nodes. + + Returns a ``namedtuple`` with the following attributes: + + ``const`` + A function to finalize constant data at compile time. + + ``src`` + Source code to output around nodes to be evaluated at + runtime. + """ + if self._finalize is not None: + return self._finalize + + finalize: t.Optional[t.Callable[..., t.Any]] + finalize = default = self._default_finalize + src = None + + if self.environment.finalize: + src = "environment.finalize(" + env_finalize = self.environment.finalize + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(env_finalize) # type: ignore + ) + finalize = None + + if pass_arg is None: + + def finalize(value: t.Any) -> t.Any: # 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() + self.newline(node) + self.visit(node.target, frame) + self.write(" = ") + self.visit(node.node, frame) + self.pop_assign_tracking(frame) + + def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None: + self.push_assign_tracking() + block_frame = frame.inner() + # This is a special case. Since a set block always captures we + # will disable output checks. This way one can use set blocks + # toplevel even in extended templates. + block_frame.require_output_check = False + block_frame.symbols.analyze_node(node) + self.enter_frame(block_frame) + self.buffer(block_frame) + self.blockvisit(node.body, block_frame) + self.newline(node) + self.visit(node.target, frame) + self.write(" = (Markup if context.eval_ctx.autoescape else identity)(") + if node.filter is not None: + self.visit_Filter(node.filter, block_frame) + else: + self.write(f"concat({block_frame.buffer})") + self.write(")") + self.pop_assign_tracking(frame) + self.leave_frame(block_frame) + + # -- Expression Visitors + + def visit_Name(self, node: nodes.Name, frame: Frame) -> None: + if node.ctx == "store" and ( + frame.toplevel or frame.loop_frame or frame.block_frame + ): + if self._assign_stack: + self._assign_stack[-1].add(node.name) + ref = frame.symbols.ref(node.name) + + # If we are looking up a variable we might have to deal with the + # case where it's undefined. We can skip that case if the load + # instruction indicates a parameter which are always defined. + if node.ctx == "load": + load = frame.symbols.find_load(ref) + if not ( + load is not None + and load[0] == VAR_LOAD_PARAMETER + and not self.parameter_is_undeclared(ref) + ): + self.write( + f"(undefined(name={node.name!r}) if {ref} is missing else {ref})" + ) + return + + self.write(ref) + + def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None: + # NSRefs can only be used to store values; since they use the normal + # `foo.bar` notation they will be parsed as a normal attribute access + # when used anywhere but in a `set` context + ref = frame.symbols.ref(node.name) + self.writeline(f"if not isinstance({ref}, Namespace):") + self.indent() + self.writeline( + "raise TemplateRuntimeError" + '("cannot assign attribute on non-namespace object")' + ) + self.outdent() + self.writeline(f"{ref}[{node.attr!r}]") + + def visit_Const(self, node: nodes.Const, frame: Frame) -> None: + val = node.as_const(frame.eval_ctx) + if isinstance(val, float): + self.write(str(val)) + else: + self.write(repr(val)) + + def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None: + try: + self.write(repr(node.as_const(frame.eval_ctx))) + except nodes.Impossible: + self.write( + f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})" + ) + + def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None: + self.write("(") + idx = -1 + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write(",)" if idx == 0 else ")") + + def visit_List(self, node: nodes.List, frame: Frame) -> None: + self.write("[") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write("]") + + def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None: + self.write("{") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item.key, frame) + self.write(": ") + self.visit(item.value, frame) + self.write("}") + + visit_Add = _make_binop("+") + visit_Sub = _make_binop("-") + visit_Mul = _make_binop("*") + visit_Div = _make_binop("/") + visit_FloorDiv = _make_binop("//") + visit_Pow = _make_binop("**") + visit_Mod = _make_binop("%") + visit_And = _make_binop("and") + visit_Or = _make_binop("or") + visit_Pos = _make_unop("+") + visit_Neg = _make_unop("-") + visit_Not = _make_unop("not ") + + @optimizeconst + def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None: + if frame.eval_ctx.volatile: + func_name = "(markup_join if context.eval_ctx.volatile else str_join)" + elif frame.eval_ctx.autoescape: + func_name = "markup_join" + else: + func_name = "str_join" + self.write(f"{func_name}((") + for arg in node.nodes: + self.visit(arg, frame) + self.write(", ") + self.write("))") + + @optimizeconst + def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None: + self.write("(") + self.visit(node.expr, frame) + for op in node.ops: + self.visit(op, frame) + self.write(")") + + def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None: + self.write(f" {operators[node.op]} ") + self.visit(node.expr, frame) + + @optimizeconst + def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getattr(") + self.visit(node.node, frame) + self.write(f", {node.attr!r})") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None: + # slices bypass the environment getitem method. + if isinstance(node.arg, nodes.Slice): + self.visit(node.node, frame) + self.write("[") + self.visit(node.arg, frame) + self.write("]") + else: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getitem(") + self.visit(node.node, frame) + self.write(", ") + self.visit(node.arg, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None: + if node.start is not None: + self.visit(node.start, frame) + self.write(":") + if node.stop is not None: + self.visit(node.stop, frame) + if node.step is not None: + self.write(":") + self.visit(node.step, frame) + + @contextmanager + def _filter_test_common( + self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool + ) -> t.Iterator[None]: + if self.environment.is_async: + self.write("(await auto_await(") + + if is_filter: + self.write(f"{self.filters[node.name]}(") + func = self.environment.filters.get(node.name) + else: + self.write(f"{self.tests[node.name]}(") + func = self.environment.tests.get(node.name) + + # When inside an If or CondExpr frame, allow the filter to be + # undefined at compile time and only raise an error if it's + # actually called at runtime. See pull_dependencies. + if func is None and not frame.soft_frame: + type_name = "filter" if is_filter else "test" + self.fail(f"No {type_name} named {node.name!r}.", node.lineno) + + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(func) # type: ignore + ) + + if pass_arg is not None: + self.write(f"{pass_arg}, ") + + # Back to the visitor function to handle visiting the target of + # the filter or test. + yield + + self.signature(node, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None: + with self._filter_test_common(node, frame, True): + # if the filter node is None we are inside a filter block + # and want to write to the current buffer + if node.node is not None: + self.visit(node.node, frame) + elif frame.eval_ctx.volatile: + self.write( + f"(Markup(concat({frame.buffer}))" + f" if context.eval_ctx.autoescape else concat({frame.buffer}))" + ) + elif frame.eval_ctx.autoescape: + self.write(f"Markup(concat({frame.buffer}))") + else: + self.write(f"concat({frame.buffer})") + + @optimizeconst + def visit_Test(self, node: nodes.Test, frame: Frame) -> None: + with self._filter_test_common(node, frame, False): + self.visit(node.node, frame) + + @optimizeconst + def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None: + frame = frame.soft() + + def write_expr2() -> None: + if node.expr2 is not None: + self.visit(node.expr2, frame) + return + + self.write( + f'cond_expr_undefined("the inline if-expression on' + f" {self.position(node)} evaluated to false and no else" + f' section was defined.")' + ) + + self.write("(") + self.visit(node.expr1, frame) + self.write(" if ") + self.visit(node.test, frame) + self.write(" else ") + write_expr2() + self.write(")") + + @optimizeconst + def visit_Call( + self, node: nodes.Call, frame: Frame, forward_caller: bool = False + ) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + if self.environment.sandboxed: + self.write("environment.call(context, ") + else: + self.write("context.call(") + self.visit(node.node, frame) + extra_kwargs = {"caller": "caller"} if forward_caller else None + loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {} + block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {} + if extra_kwargs: + extra_kwargs.update(loop_kwargs, **block_kwargs) + elif loop_kwargs or block_kwargs: + extra_kwargs = dict(loop_kwargs, **block_kwargs) + self.signature(node, frame, extra_kwargs) + self.write(")") + if self.environment.is_async: + self.write("))") + + def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None: + self.write(node.key + "=") + self.visit(node.value, frame) + + # -- Unused nodes for extensions + + def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None: + self.write("Markup(") + self.visit(node.expr, frame) + self.write(")") + + def visit_MarkSafeIfAutoescape( + self, node: nodes.MarkSafeIfAutoescape, frame: Frame + ) -> None: + self.write("(Markup if context.eval_ctx.autoescape else identity)(") + self.visit(node.expr, frame) + self.write(")") + + def visit_EnvironmentAttribute( + self, node: nodes.EnvironmentAttribute, frame: Frame + ) -> None: + self.write("environment." + node.name) + + def visit_ExtensionAttribute( + self, node: nodes.ExtensionAttribute, frame: Frame + ) -> None: + self.write(f"environment.extensions[{node.identifier!r}].{node.name}") + + def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None: + self.write(self.import_aliases[node.importname]) + + def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None: + self.write(node.name) + + def visit_ContextReference( + self, node: nodes.ContextReference, frame: Frame + ) -> None: + self.write("context") + + def visit_DerivedContextReference( + self, node: nodes.DerivedContextReference, frame: Frame + ) -> None: + self.write(self.derive_context(frame)) + + def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None: + self.writeline("continue", node) + + def visit_Break(self, node: nodes.Break, frame: Frame) -> None: + self.writeline("break", node) + + def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None: + scope_frame = frame.inner() + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + + def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None: + ctx = self.temporary_identifier() + self.writeline(f"{ctx} = {self.derive_context(frame)}") + self.writeline(f"{ctx}.vars = ") + self.visit(node.context, frame) + self.push_context_reference(ctx) + + scope_frame = frame.inner(isolated=True) + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + self.pop_context_reference() + + def visit_EvalContextModifier( + self, node: nodes.EvalContextModifier, frame: Frame + ) -> None: + for keyword in node.options: + self.writeline(f"context.eval_ctx.{keyword.key} = ") + self.visit(keyword.value, frame) + try: + val = keyword.value.as_const(frame.eval_ctx) + except nodes.Impossible: + frame.eval_ctx.volatile = True + else: + setattr(frame.eval_ctx, keyword.key, val) + + def visit_ScopedEvalContextModifier( + self, node: nodes.ScopedEvalContextModifier, frame: Frame + ) -> None: + old_ctx_name = self.temporary_identifier() + saved_ctx = frame.eval_ctx.save() + self.writeline(f"{old_ctx_name} = context.eval_ctx.save()") + self.visit_EvalContextModifier(node, frame) + for child in node.body: + self.visit(child, frame) + frame.eval_ctx.revert(saved_ctx) + self.writeline(f"context.eval_ctx.revert({old_ctx_name})") diff --git a/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/constants.py b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/constants.py new file mode 100644 index 0000000..41a1c23 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/debug.py b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/debug.py new file mode 100644 index 0000000..7ed7e92 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/defaults.py b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/defaults.py new file mode 100644 index 0000000..638cad3 --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/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/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/environment.py b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/environment.py new file mode 100644 index 0000000..1d3be0b --- /dev/null +++ b/flask_auth_app/authenv/lib/python3.10/site-packages/jinja2/environment.py @@ -0,0 +1,1675 @@ +"""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: "Environment") -> "Environment": + """Perform a sanity check on the environment.""" + assert issubclass( + environment.undefined, Undefined + ), "'undefined' must be a subclass of 'jinja2.Undefined'." + assert ( + environment.block_start_string + != environment.variable_start_string + != environment.comment_start_string + ), "block, variable and comment start strings must be different." + assert environment.newline_sequence in { + "\r", + "\r\n", + "\n", + }, "'newline_sequence' must be one of '\\n', '\\r\\n', or '\\r'." + return environment + + +class Environment: + r"""The core component of Jinja is the `Environment`. It contains + important shared variables like configuration, filters, tests, + globals and others. Instances of this class may be modified if + they are not shared and if no template was loaded so far. + Modifications on environments after the first template was loaded + will lead to surprising effects and undefined behavior. + + Here are the possible initialization parameters: + + `block_start_string` + The string marking the beginning of a block. Defaults to ``'{%'``. + + `block_end_string` + The string marking the end of a block. Defaults to ``'%}'``. + + `variable_start_string` + The string marking the beginning of a print statement. + Defaults to ``'{{'``. + + `variable_end_string` + The string marking the end of a print statement. Defaults to + ``'}}'``. + + `comment_start_string` + The string marking the beginning of a comment. Defaults to ``'{#'``. + + `comment_end_string` + The string marking the end of a comment. Defaults to ``'#}'``. + + `line_statement_prefix` + If given and a string, this will be used as prefix for line based + statements. See also :ref:`line-statements`. + + `line_comment_prefix` + If given and a string, this will be used as prefix for line based + comments. See also :ref:`line-statements`. + + .. versionadded:: 2.2 + + `trim_blocks` + If this is set to ``True`` the first newline after a block is + removed (block, not variable tag!). Defaults to `False`. + + `lstrip_blocks` + If this is set to ``True`` leading spaces and tabs are stripped + from the start of a line to a block. Defaults to `False`. + + `newline_sequence` + The sequence that starts a newline. Must be one of ``'\r'``, + ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a + useful default for Linux and OS X systems as well as web + applications. + + `keep_trailing_newline` + Preserve the trailing newline when rendering templates. + The default is ``False``, which causes a single newline, + if present, to be stripped from the end of the template. + + .. versionadded:: 2.7 + + `extensions` + List of Jinja extensions to use. This can either be import paths + as strings or extension classes. For more information have a + look at :ref:`the extensions documentation `. + + `optimized` + should the optimizer be enabled? Default is ``True``. + + `undefined` + :class:`Undefined` or a subclass of it that is used to represent + undefined values in the template. + + `finalize` + A callable that can be used to process the result of a variable + expression before it is output. For example one can convert + ``None`` implicitly into an empty string here. + + `autoescape` + If set to ``True`` the XML/HTML autoescaping feature is enabled by + default. For more details about autoescaping see + :class:`~markupsafe.Markup`. As of Jinja 2.4 this can also + be a callable that is passed the template name and has to + return ``True`` or ``False`` depending on autoescape should be + enabled by default. + + .. versionchanged:: 2.4 + `autoescape` can now be a function + + `loader` + The template loader for this environment. + + `cache_size` + The size of the cache. Per default this is ``400`` which means + that if more than 400 templates are loaded the loader will clean + out the least recently used template. If the cache size is set to + ``0`` templates are recompiled all the time, if the cache size is + ``-1`` the cache will not be cleaned. + + .. versionchanged:: 2.8 + The cache size was increased to 400 from a low 50. + + `auto_reload` + Some loaders load templates from locations where the template + sources may change (ie: file system or database). If + ``auto_reload`` is set to ``True`` (default) every time a template is + requested the loader checks if the source changed and if yes, it + will reload the template. For higher performance it's possible to + disable that. + + `bytecode_cache` + If set to a bytecode cache object, this object will provide a + cache for the internal Jinja bytecode so that templates don't + have to be parsed if they were not changed. + + See :ref:`bytecode-cache` for more information. + + `enable_async` + If set to true this enables async template execution which + allows using async functions and generators. + """ + + #: if this environment is sandboxed. Modifying this variable won't make + #: the environment sandboxed though. For a real sandboxed environment + #: have a look at jinja2.sandbox. This flag alone controls the code + #: generation by the compiler. + sandboxed = False + + #: True if the environment is just an overlay + overlayed = False + + #: the environment this environment is linked to if it is an overlay + linked_to: t.Optional["Environment"] = None + + #: shared environments have this set to `True`. A shared environment + #: must not be modified + shared = False + + #: the class that is used for code generation. See + #: :class:`~jinja2.compiler.CodeGenerator` for more information. + code_generator_class: t.Type["CodeGenerator"] = CodeGenerator + + concat = "".join + + #: the context class that is used for templates. See + #: :class:`~jinja2.runtime.Context` for more information. + context_class: t.Type[Context] = Context + + template_class: t.Type["Template"] + + def __init__( + self, + block_start_string: str = BLOCK_START_STRING, + block_end_string: str = BLOCK_END_STRING, + variable_start_string: str = VARIABLE_START_STRING, + variable_end_string: str = VARIABLE_END_STRING, + comment_start_string: str = COMMENT_START_STRING, + comment_end_string: str = COMMENT_END_STRING, + line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX, + line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX, + trim_blocks: bool = TRIM_BLOCKS, + lstrip_blocks: bool = LSTRIP_BLOCKS, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE, + keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (), + optimized: bool = True, + undefined: t.Type[Undefined] = Undefined, + finalize: t.Optional[t.Callable[..., t.Any]] = None, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False, + loader: t.Optional["BaseLoader"] = None, + cache_size: int = 400, + auto_reload: bool = True, + bytecode_cache: t.Optional["BytecodeCache"] = None, + enable_async: bool = False, + ): + # !!Important notice!! + # The constructor accepts quite a few arguments that should be + # passed by keyword rather than position. However it's important to + # not change the order of arguments because it's used at least + # internally in those cases: + # - spontaneous environments (i18n extension and Template) + # - unittests + # If parameter changes are required only add parameters at the end + # and don't change the arguments (or the defaults!) of the arguments + # existing already. + + # lexer / parser information + self.block_start_string = block_start_string + self.block_end_string = block_end_string + self.variable_start_string = variable_start_string + self.variable_end_string = variable_end_string + self.comment_start_string = comment_start_string + self.comment_end_string = comment_end_string + self.line_statement_prefix = line_statement_prefix + self.line_comment_prefix = line_comment_prefix + self.trim_blocks = trim_blocks + self.lstrip_blocks = lstrip_blocks + self.newline_sequence = newline_sequence + self.keep_trailing_newline = keep_trailing_newline + + # runtime information + self.undefined: t.Type[Undefined] = undefined + self.optimized = optimized + self.finalize = finalize + self.autoescape = autoescape + + # defaults + self.filters = DEFAULT_FILTERS.copy() + self.tests = DEFAULT_TESTS.copy() + self.globals = DEFAULT_NAMESPACE.copy() + + # set the loader provided + self.loader = loader + self.cache = create_cache(cache_size) + self.bytecode_cache = bytecode_cache + self.auto_reload = auto_reload + + # configurable policies + self.policies = DEFAULT_POLICIES.copy() + + # load extensions + self.extensions = load_extensions(self, extensions) + + self.is_async = enable_async + _environment_config_check(self) + + def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None: + """Adds an extension after the environment was created. + + .. versionadded:: 2.5 + """ + self.extensions.update(load_extensions(self, [extension])) + + def extend(self, **attributes: t.Any) -> None: + """Add the items to the instance of the environment if they do not exist + yet. This is used by :ref:`extensions ` to register + callbacks and configuration values without breaking inheritance. + """ + for key, value in attributes.items(): + if not hasattr(self, key): + setattr(self, key, value) + + def overlay( + self, + block_start_string: str = missing, + block_end_string: str = missing, + variable_start_string: str = missing, + variable_end_string: str = missing, + comment_start_string: str = missing, + comment_end_string: str = missing, + line_statement_prefix: t.Optional[str] = missing, + line_comment_prefix: t.Optional[str] = missing, + trim_blocks: bool = missing, + lstrip_blocks: bool = missing, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing, + keep_trailing_newline: bool = missing, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing, + optimized: bool = missing, + undefined: t.Type[Undefined] = missing, + finalize: t.Optional[t.Callable[..., t.Any]] = missing, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing, + loader: t.Optional["BaseLoader"] = missing, + cache_size: int = missing, + auto_reload: bool = missing, + bytecode_cache: t.Optional["BytecodeCache"] = missing, + enable_async: bool = False, + ) -> "Environment": + """Create a new overlay environment that shares all the data with the + current environment except for cache and the overridden attributes. + Extensions cannot be removed for an overlayed environment. An overlayed + environment automatically gets all the extensions of the environment it + is linked to plus optional extra extensions. + + Creating overlays should happen after the initial environment was set + up completely. Not all attributes are truly linked, some are just + copied over so modifications on the original environment may not shine + through. + + .. versionchanged:: 3.1.2 + Added the ``newline_sequence``,, ``keep_trailing_newline``, + and ``enable_async`` parameters to match ``__init__``. + """ + args = dict(locals()) + del args["self"], args["cache_size"], args["extensions"], args["enable_async"] + + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.overlayed = True + rv.linked_to = self + + for key, value in args.items(): + if value is not missing: + setattr(rv, key, value) + + if cache_size is not missing: + rv.cache = create_cache(cache_size) + else: + rv.cache = copy_cache(self.cache) + + rv.extensions = {} + for key, value in self.extensions.items(): + rv.extensions[key] = value.bind(rv) + if extensions is not missing: + rv.extensions.update(load_extensions(rv, extensions)) + + if enable_async is not missing: + rv.is_async = enable_async + + return _environment_config_check(rv) + + @property + def lexer(self) -> Lexer: + """The lexer for this environment.""" + return get_lexer(self) + + def iter_extensions(self) -> t.Iterator["Extension"]: + """Iterates over the extensions by priority.""" + return iter(sorted(self.extensions.values(), key=lambda x: x.priority)) + + def getitem( + self, obj: t.Any, argument: t.Union[str, t.Any] + ) -> t.Union[t.Any, Undefined]: + """Get an item or attribute of an object but prefer the item.""" + try: + return obj[argument] + except (AttributeError, TypeError, LookupError): + if isinstance(argument, str): + try: + attr = str(argument) + except Exception: + pass + else: + try: + return getattr(obj, attr) + except AttributeError: + pass + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj: t.Any, attribute: str) -> t.Any: + """Get an item or attribute of an object but prefer the attribute. + Unlike :meth:`getitem` the attribute *must* be a string. + """ + try: + return getattr(obj, attribute) + except AttributeError: + pass + try: + return obj[attribute] + except (TypeError, LookupError, AttributeError): + return self.undefined(obj=obj, name=attribute) + + def _filter_test_common( + self, + name: t.Union[str, Undefined], + value: t.Any, + args: t.Optional[t.Sequence[t.Any]], + kwargs: t.Optional[t.Mapping[str, t.Any]], + context: t.Optional[Context], + eval_ctx: t.Optional[EvalContext], + is_filter: bool, + ) -> t.Any: + if is_filter: + env_map = self.filters + type_name = "filter" + else: + env_map = self.tests + type_name = "test" + + func = env_map.get(name) # type: ignore + + if func is None: + msg = f"No {type_name} named {name!r}." + + if isinstance(name, Undefined): + try: + name._fail_with_undefined_error() + except Exception as e: + msg = f"{msg} ({e}; did you forget to quote the callable name?)" + + raise TemplateRuntimeError(msg) + + args = [value, *(args if args is not None else ())] + kwargs = kwargs if kwargs is not None else {} + pass_arg = _PassArg.from_obj(func) + + if pass_arg is _PassArg.context: + if context is None: + raise TemplateRuntimeError( + f"Attempted to invoke a context {type_name} without context." + ) + + args.insert(0, context) + elif pass_arg is _PassArg.eval_context: + if eval_ctx is None: + if context is not None: + eval_ctx = context.eval_ctx + else: + eval_ctx = EvalContext(self) + + args.insert(0, eval_ctx) + elif pass_arg is _PassArg.environment: + args.insert(0, self) + + return func(*args, **kwargs) + + def call_filter( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a filter on a value the same way the compiler does. + + This might return a coroutine if the filter is running from an + environment in async mode and the filter supports async + execution. It's your responsibility to await this if needed. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, True + ) + + def call_test( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a test on a value the same way the compiler does. + + This might return a coroutine if the test is running from an + environment in async mode and the test supports async execution. + It's your responsibility to await this if needed. + + .. versionchanged:: 3.0 + Tests support ``@pass_context``, etc. decorators. Added + the ``context`` and ``eval_ctx`` parameters. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, False + ) + + @internalcode + def parse( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> nodes.Template: + """Parse the sourcecode and return the abstract syntax tree. This + tree of nodes is used by the compiler to convert the template into + executable source- or bytecode. This is useful for debugging or to + extract information from templates. + + If you are :ref:`developing Jinja extensions ` + this gives you a good overview of the node tree generated. + """ + try: + return self._parse(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def _parse( + self, source: str, name: t.Optional[str], filename: t.Optional[str] + ) -> nodes.Template: + """Internal parsing function used by `parse` and `compile`.""" + return Parser(self, source, name, filename).parse() + + def lex( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> t.Iterator[t.Tuple[int, str, str]]: + """Lex the given sourcecode and return a generator that yields + tokens as tuples in the form ``(lineno, token_type, value)``. + This can be useful for :ref:`extension development ` + and debugging templates. + + This does not perform preprocessing. If you want the preprocessing + of the extensions to be applied you have to filter source through + the :meth:`preprocess` method. + """ + source = str(source) + try: + return self.lexer.tokeniter(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def preprocess( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> str: + """Preprocesses the source with all extensions. This is automatically + called for all parsing and compiling methods but *not* for :meth:`lex` + because there you usually only want the actual source tokenized. + """ + return reduce( + lambda s, e: e.preprocess(s, name, filename), + self.iter_extensions(), + str(source), + ) + + def _tokenize( + self, + source: str, + name: t.Optional[str], + filename: t.Optional[str] = None, + state: t.Optional[str] = None, + ) -> TokenStream: + """Called by the parser to do the preprocessing and filtering + for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. + """ + source = self.preprocess(source, name, filename) + stream = self.lexer.tokenize(source, name, filename, state) + + for ext in self.iter_extensions(): + stream = ext.filter_stream(stream) # type: ignore + + if not isinstance(stream, TokenStream): + stream = TokenStream(stream, name, filename) + + 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( # type: ignore + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[False]" = False, + defer_init: bool = False, + ) -> CodeType: ... + + @typing.overload + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[True]" = ..., + defer_init: bool = False, + ) -> str: ... + + @internalcode + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: bool = False, + defer_init: bool = False, + ) -> t.Union[str, CodeType]: + """Compile a node or template source code. The `name` parameter is + the load name of the template after it was joined using + :meth:`join_path` if necessary, not the filename on the file system. + the `filename` parameter is the estimated filename of the template on + the file system. If the template came from a database or memory this + can be omitted. + + The return value of this method is a python code object. If the `raw` + parameter is `True` the return value will be a string with python + code equivalent to the bytecode returned otherwise. This method is + mainly used internally. + + `defer_init` is use internally to aid the module code generator. This + causes the generated code to be able to import without the global + environment variable to be set. + + .. versionadded:: 2.4 + `defer_init` parameter added. + """ + source_hint = None + try: + if isinstance(source, str): + source_hint = source + source = self._parse(source, name, filename) + source = self._generate(source, name, filename, defer_init=defer_init) + if raw: + return source + if filename is None: + filename = "

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