From 7d58ec28ac5e72f839d0ab0916e133f7ca470fec Mon Sep 17 00:00:00 2001 From: geekinsneaks <86547543+geekinsneaks@users.noreply.github.com> Date: Tue, 14 Apr 2026 05:50:40 +0000 Subject: [PATCH 1/8] updated index.html --- src/index.html | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/index.html diff --git a/src/index.html b/src/index.html new file mode 100644 index 00000000..c0a5af32 --- /dev/null +++ b/src/index.html @@ -0,0 +1,3 @@ + +

Hello from the codespace!

+ \ No newline at end of file From bff2a0d911d11a5d07d659b927127ca3b6618def Mon Sep 17 00:00:00 2001 From: geekinsneaks <86547543+geekinsneaks@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:57:16 -0700 Subject: [PATCH 2/8] Create devcontainer.json --- .devcontainer/devcontainer.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..4888c072 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,8 @@ +{ +// Name this configuration +"name": "Codespace for Skills!", +// Use the base codespace image +"image": "mcr.microsoft.com/vscode/devcontainers/universal:latest", +"remoteUser": "codespace", +"overrideCommand": false +} From 340b2d25e38093a86945b0245e4bf8ee8d3c41ba Mon Sep 17 00:00:00 2001 From: geekinsneaks <86547543+geekinsneaks@users.noreply.github.com> Date: Mon, 13 Apr 2026 23:01:37 -0700 Subject: [PATCH 3/8] Enhance dev container with VS Code customizations --- .devcontainer/devcontainer.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4888c072..860da49b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,4 +5,17 @@ "image": "mcr.microsoft.com/vscode/devcontainers/universal:latest", "remoteUser": "codespace", "overrideCommand": false + // Add the IDs of extensions you want installed when the container is created. +"customisations": { + "vscode": { + "extensions": [ + "GitHub.copilot" + ] + }, + "codespaces": { + "openFiles": [ + "codespace.md" + ] + } +} } From 203dff78eb31986823fa81f9c046ef40ed3b4302 Mon Sep 17 00:00:00 2001 From: geekinsneaks <86547543+geekinsneaks@users.noreply.github.com> Date: Mon, 13 Apr 2026 23:05:45 -0700 Subject: [PATCH 4/8] Enhance dev container with VS Code customizations --- .devcontainer/devcontainer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 860da49b..8d30d289 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,8 +4,7 @@ // Use the base codespace image "image": "mcr.microsoft.com/vscode/devcontainers/universal:latest", "remoteUser": "codespace", -"overrideCommand": false - // Add the IDs of extensions you want installed when the container is created. +"overrideCommand": false, "customisations": { "vscode": { "extensions": [ From 1487de9e4078f00af001134205536e393cff24ac Mon Sep 17 00:00:00 2001 From: geekinsneaks <86547543+geekinsneaks@users.noreply.github.com> Date: Mon, 13 Apr 2026 23:10:23 -0700 Subject: [PATCH 5/8] Update devcontainer.json --- .devcontainer/devcontainer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8d30d289..99941e14 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -16,5 +16,6 @@ "codespace.md" ] } -} +}, +"postCreateCommand": "echo '# Writing code upon codespace creation!' } From d583bccec2e6461940f502758d960578efc534de Mon Sep 17 00:00:00 2001 From: geekinsneaks <86547543+geekinsneaks@users.noreply.github.com> Date: Tue, 14 Apr 2026 06:16:40 +0000 Subject: [PATCH 6/8] Adding setup.sh from the codespace! --- setup.sh | 4 ++++ 1 file changed, 4 insertions(+) create mode 100755 setup.sh diff --git a/setup.sh b/setup.sh new file mode 100755 index 00000000..39634ffc --- /dev/null +++ b/setup.sh @@ -0,0 +1,4 @@ +#!/bin/bash +sudo apt-get update +sudo apt-get install sl +echo "export PATH=\$PATH:/usr/games" >> ~/.bashrc \ No newline at end of file From 42aea7f34f569cf55224da97c583fd2784c8604b Mon Sep 17 00:00:00 2001 From: geekinsneaks <86547543+geekinsneaks@users.noreply.github.com> Date: Tue, 14 Apr 2026 06:17:37 +0000 Subject: [PATCH 7/8] updated setup.sh --- setup.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 setup.sh diff --git a/setup.sh b/setup.sh old mode 100755 new mode 100644 From 2c66a601e3b387dae6869c677a9336927f50624d Mon Sep 17 00:00:00 2001 From: geekinsneaks <86547543+geekinsneaks@users.noreply.github.com> Date: Wed, 15 Apr 2026 05:51:47 +0000 Subject: [PATCH 8/8] All the steps are complete --- app/__init__.py | 21 + app/models.py | 17 + app/routes.py | 25 + app/static/style.css | 47 + app/templates/add_workout.html | 21 + app/templates/base.html | 17 + app/templates/index.html | 11 + requirements.txt | 5 + run.py | 6 + venv/bin/Activate.ps1 | 247 + venv/bin/activate | 76 + venv/bin/activate.csh | 27 + venv/bin/activate.fish | 69 + venv/bin/alembic | 8 + venv/bin/dotenv | 8 + venv/bin/flask | 8 + venv/bin/mako-render | 8 + venv/bin/pip | 8 + venv/bin/pip3 | 8 + venv/bin/pip3.12 | 8 + venv/bin/python | 1 + venv/bin/python3 | 1 + venv/bin/python3.12 | 1 + .../site/python3.12/greenlet/greenlet.h | 164 + .../Flask_Migrate-4.0.5.dist-info/INSTALLER | 1 + .../Flask_Migrate-4.0.5.dist-info/LICENSE | 20 + .../Flask_Migrate-4.0.5.dist-info/METADATA | 86 + .../Flask_Migrate-4.0.5.dist-info/RECORD | 31 + .../Flask_Migrate-4.0.5.dist-info/REQUESTED | 0 .../Flask_Migrate-4.0.5.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../SQLAlchemy-2.0.23.dist-info/INSTALLER | 1 + .../SQLAlchemy-2.0.23.dist-info/LICENSE | 19 + .../SQLAlchemy-2.0.23.dist-info/METADATA | 241 + .../SQLAlchemy-2.0.23.dist-info/RECORD | 530 + .../SQLAlchemy-2.0.23.dist-info/REQUESTED | 0 .../SQLAlchemy-2.0.23.dist-info/WHEEL | 5 + .../SQLAlchemy-2.0.23.dist-info/top_level.txt | 1 + .../typing_extensions.cpython-312.pyc | Bin 0 -> 163642 bytes .../alembic-1.18.4.dist-info/INSTALLER | 1 + .../alembic-1.18.4.dist-info/METADATA | 139 + .../alembic-1.18.4.dist-info/RECORD | 178 + .../alembic-1.18.4.dist-info/WHEEL | 5 + .../alembic-1.18.4.dist-info/entry_points.txt | 2 + .../alembic-1.18.4.dist-info/licenses/LICENSE | 19 + .../alembic-1.18.4.dist-info/top_level.txt | 1 + .../site-packages/alembic/__init__.py | 6 + .../site-packages/alembic/__main__.py | 4 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 316 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 309 bytes .../__pycache__/command.cpython-312.pyc | Bin 0 -> 29910 bytes .../__pycache__/config.cpython-312.pyc | Bin 0 -> 39095 bytes .../__pycache__/context.cpython-312.pyc | Bin 0 -> 374 bytes .../__pycache__/environment.cpython-312.pyc | Bin 0 -> 227 bytes .../__pycache__/migration.cpython-312.pyc | Bin 0 -> 223 bytes .../alembic/__pycache__/op.cpython-312.pyc | Bin 0 -> 356 bytes .../alembic/autogenerate/__init__.py | 10 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 622 bytes .../__pycache__/api.cpython-312.pyc | Bin 0 -> 22967 bytes .../__pycache__/render.cpython-312.pyc | Bin 0 -> 47417 bytes .../__pycache__/rewriter.cpython-312.pyc | Bin 0 -> 9497 bytes .../site-packages/alembic/autogenerate/api.py | 667 ++ .../alembic/autogenerate/compare/__init__.py | 62 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2431 bytes .../__pycache__/comments.cpython-312.pyc | Bin 0 -> 3770 bytes .../__pycache__/constraints.cpython-312.pyc | Bin 0 -> 27115 bytes .../__pycache__/schema.cpython-312.pyc | Bin 0 -> 2371 bytes .../server_defaults.cpython-312.pyc | Bin 0 -> 10535 bytes .../__pycache__/tables.cpython-312.pyc | Bin 0 -> 11610 bytes .../compare/__pycache__/types.cpython-312.pyc | Bin 0 -> 4371 bytes .../compare/__pycache__/util.cpython-312.pyc | Bin 0 -> 10945 bytes .../alembic/autogenerate/compare/comments.py | 106 + .../autogenerate/compare/constraints.py | 812 ++ .../alembic/autogenerate/compare/schema.py | 62 + .../autogenerate/compare/server_defaults.py | 344 + .../alembic/autogenerate/compare/tables.py | 316 + .../alembic/autogenerate/compare/types.py | 147 + .../alembic/autogenerate/compare/util.py | 314 + .../alembic/autogenerate/render.py | 1172 +++ .../alembic/autogenerate/rewriter.py | 240 + .../site-packages/alembic/command.py | 848 ++ .../site-packages/alembic/config.py | 1051 ++ .../site-packages/alembic/context.py | 5 + .../site-packages/alembic/context.pyi | 876 ++ .../site-packages/alembic/ddl/__init__.py | 6 + .../ddl/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 384 bytes .../ddl/__pycache__/_autogen.cpython-312.pyc | Bin 0 -> 15194 bytes .../ddl/__pycache__/base.cpython-312.pyc | Bin 0 -> 17513 bytes .../ddl/__pycache__/impl.cpython-312.pyc | Bin 0 -> 35887 bytes .../ddl/__pycache__/mssql.cpython-312.pyc | Bin 0 -> 19401 bytes .../ddl/__pycache__/mysql.cpython-312.pyc | Bin 0 -> 17009 bytes .../ddl/__pycache__/oracle.cpython-312.pyc | Bin 0 -> 8515 bytes .../__pycache__/postgresql.cpython-312.pyc | Bin 0 -> 33690 bytes .../ddl/__pycache__/sqlite.cpython-312.pyc | Bin 0 -> 8023 bytes .../site-packages/alembic/ddl/_autogen.py | 329 + .../site-packages/alembic/ddl/base.py | 406 + .../site-packages/alembic/ddl/impl.py | 921 ++ .../site-packages/alembic/ddl/mssql.py | 523 + .../site-packages/alembic/ddl/mysql.py | 526 + .../site-packages/alembic/ddl/oracle.py | 202 + .../site-packages/alembic/ddl/postgresql.py | 864 ++ .../site-packages/alembic/ddl/sqlite.py | 237 + .../site-packages/alembic/environment.py | 1 + .../site-packages/alembic/migration.py | 1 + .../python3.12/site-packages/alembic/op.py | 5 + .../python3.12/site-packages/alembic/op.pyi | 1429 +++ .../alembic/operations/__init__.py | 15 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 484 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 81753 bytes .../__pycache__/batch.cpython-312.pyc | Bin 0 -> 31395 bytes .../__pycache__/ops.cpython-312.pyc | Bin 0 -> 113714 bytes .../__pycache__/schemaobj.cpython-312.pyc | Bin 0 -> 11882 bytes .../__pycache__/toimpl.cpython-312.pyc | Bin 0 -> 12002 bytes .../site-packages/alembic/operations/base.py | 2001 ++++ .../site-packages/alembic/operations/batch.py | 720 ++ .../site-packages/alembic/operations/ops.py | 2918 ++++++ .../alembic/operations/schemaobj.py | 290 + .../alembic/operations/toimpl.py | 261 + .../python3.12/site-packages/alembic/py.typed | 0 .../site-packages/alembic/runtime/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 191 bytes .../__pycache__/environment.cpython-312.pyc | Bin 0 -> 44664 bytes .../__pycache__/migration.cpython-312.pyc | Bin 0 -> 57787 bytes .../__pycache__/plugins.cpython-312.pyc | Bin 0 -> 7802 bytes .../alembic/runtime/environment.py | 1074 ++ .../alembic/runtime/migration.py | 1346 +++ .../site-packages/alembic/runtime/plugins.py | 179 + .../site-packages/alembic/script/__init__.py | 4 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 304 bytes .../script/__pycache__/base.cpython-312.pyc | Bin 0 -> 42526 bytes .../__pycache__/revision.cpython-312.pyc | Bin 0 -> 62503 bytes .../__pycache__/write_hooks.cpython-312.pyc | Bin 0 -> 7218 bytes .../site-packages/alembic/script/base.py | 1052 ++ .../site-packages/alembic/script/revision.py | 1728 ++++ .../alembic/script/write_hooks.py | 181 + .../alembic/templates/async/README | 1 + .../async/__pycache__/env.cpython-312.pyc | Bin 0 -> 3340 bytes .../alembic/templates/async/alembic.ini.mako | 149 + .../alembic/templates/async/env.py | 89 + .../alembic/templates/async/script.py.mako | 28 + .../alembic/templates/generic/README | 1 + .../generic/__pycache__/env.cpython-312.pyc | Bin 0 -> 2554 bytes .../templates/generic/alembic.ini.mako | 149 + .../alembic/templates/generic/env.py | 78 + .../alembic/templates/generic/script.py.mako | 28 + .../alembic/templates/multidb/README | 12 + .../multidb/__pycache__/env.cpython-312.pyc | Bin 0 -> 4765 bytes .../templates/multidb/alembic.ini.mako | 157 + .../alembic/templates/multidb/env.py | 140 + .../alembic/templates/multidb/script.py.mako | 51 + .../alembic/templates/pyproject/README | 1 + .../pyproject/__pycache__/env.cpython-312.pyc | Bin 0 -> 2556 bytes .../templates/pyproject/alembic.ini.mako | 44 + .../alembic/templates/pyproject/env.py | 78 + .../templates/pyproject/pyproject.toml.mako | 84 + .../templates/pyproject/script.py.mako | 28 + .../alembic/templates/pyproject_async/README | 1 + .../__pycache__/env.cpython-312.pyc | Bin 0 -> 3350 bytes .../pyproject_async/alembic.ini.mako | 44 + .../alembic/templates/pyproject_async/env.py | 89 + .../pyproject_async/pyproject.toml.mako | 84 + .../templates/pyproject_async/script.py.mako | 28 + .../site-packages/alembic/testing/__init__.py | 32 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1418 bytes .../__pycache__/assertions.cpython-312.pyc | Bin 0 -> 7477 bytes .../testing/__pycache__/env.cpython-312.pyc | Bin 0 -> 16793 bytes .../__pycache__/fixtures.cpython-312.pyc | Bin 0 -> 18987 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 0 -> 10402 bytes .../__pycache__/schemacompare.cpython-312.pyc | Bin 0 -> 9104 bytes .../testing/__pycache__/util.cpython-312.pyc | Bin 0 -> 5111 bytes .../__pycache__/warnings.cpython-312.pyc | Bin 0 -> 1086 bytes .../alembic/testing/assertions.py | 180 + .../site-packages/alembic/testing/env.py | 581 ++ .../site-packages/alembic/testing/fixtures.py | 404 + .../alembic/testing/plugin/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 198 bytes .../__pycache__/bootstrap.cpython-312.pyc | Bin 0 -> 259 bytes .../alembic/testing/plugin/bootstrap.py | 4 + .../alembic/testing/requirements.py | 189 + .../alembic/testing/schemacompare.py | 169 + .../alembic/testing/suite/__init__.py | 7 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 429 bytes .../_autogen_fixtures.cpython-312.pyc | Bin 0 -> 17402 bytes .../test_autogen_comments.cpython-312.pyc | Bin 0 -> 6689 bytes .../test_autogen_computed.cpython-312.pyc | Bin 0 -> 8429 bytes .../test_autogen_diffs.cpython-312.pyc | Bin 0 -> 12370 bytes .../test_autogen_fks.cpython-312.pyc | Bin 0 -> 34741 bytes .../test_autogen_identity.cpython-312.pyc | Bin 0 -> 9666 bytes .../test_environment.cpython-312.pyc | Bin 0 -> 18593 bytes .../suite/__pycache__/test_op.cpython-312.pyc | Bin 0 -> 2878 bytes .../testing/suite/_autogen_fixtures.py | 479 + .../testing/suite/test_autogen_comments.py | 242 + .../testing/suite/test_autogen_computed.py | 157 + .../testing/suite/test_autogen_diffs.py | 273 + .../alembic/testing/suite/test_autogen_fks.py | 1191 +++ .../testing/suite/test_autogen_identity.py | 226 + .../alembic/testing/suite/test_environment.py | 364 + .../alembic/testing/suite/test_op.py | 42 + .../site-packages/alembic/testing/util.py | 126 + .../site-packages/alembic/testing/warnings.py | 31 + .../site-packages/alembic/util/__init__.py | 33 + .../util/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1465 bytes .../util/__pycache__/compat.cpython-312.pyc | Bin 0 -> 5221 bytes .../util/__pycache__/editor.cpython-312.pyc | Bin 0 -> 3223 bytes .../util/__pycache__/exc.cpython-312.pyc | Bin 0 -> 1795 bytes .../__pycache__/langhelpers.cpython-312.pyc | Bin 0 -> 16881 bytes .../__pycache__/messaging.cpython-312.pyc | Bin 0 -> 5144 bytes .../util/__pycache__/pyfiles.cpython-312.pyc | Bin 0 -> 6377 bytes .../__pycache__/sqla_compat.cpython-312.pyc | Bin 0 -> 20840 bytes .../site-packages/alembic/util/compat.py | 130 + .../site-packages/alembic/util/editor.py | 81 + .../site-packages/alembic/util/exc.py | 43 + .../site-packages/alembic/util/langhelpers.py | 445 + .../site-packages/alembic/util/messaging.py | 122 + .../site-packages/alembic/util/pyfiles.py | 153 + .../site-packages/alembic/util/sqla_compat.py | 510 + .../blinker-1.9.0.dist-info/INSTALLER | 1 + .../blinker-1.9.0.dist-info/LICENSE.txt | 20 + .../blinker-1.9.0.dist-info/METADATA | 60 + .../blinker-1.9.0.dist-info/RECORD | 12 + .../blinker-1.9.0.dist-info/WHEEL | 4 + .../site-packages/blinker/__init__.py | 17 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 495 bytes .../__pycache__/_utilities.cpython-312.pyc | Bin 0 -> 2722 bytes .../blinker/__pycache__/base.cpython-312.pyc | Bin 0 -> 21965 bytes .../site-packages/blinker/_utilities.py | 64 + .../python3.12/site-packages/blinker/base.py | 512 + .../python3.12/site-packages/blinker/py.typed | 0 .../click-8.3.2.dist-info/INSTALLER | 1 + .../click-8.3.2.dist-info/METADATA | 84 + .../click-8.3.2.dist-info/RECORD | 40 + .../site-packages/click-8.3.2.dist-info/WHEEL | 4 + .../licenses/LICENSE.txt | 28 + .../site-packages/click/__init__.py | 123 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4067 bytes .../click/__pycache__/_compat.cpython-312.pyc | Bin 0 -> 24186 bytes .../__pycache__/_termui_impl.cpython-312.pyc | Bin 0 -> 31555 bytes .../__pycache__/_textwrap.cpython-312.pyc | Bin 0 -> 2415 bytes .../click/__pycache__/_utils.cpython-312.pyc | Bin 0 -> 1195 bytes .../__pycache__/_winconsole.cpython-312.pyc | Bin 0 -> 11760 bytes .../click/__pycache__/core.cpython-312.pyc | Bin 0 -> 134887 bytes .../__pycache__/decorators.cpython-312.pyc | Bin 0 -> 22132 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 14772 bytes .../__pycache__/formatting.cpython-312.pyc | Bin 0 -> 13593 bytes .../click/__pycache__/globals.cpython-312.pyc | Bin 0 -> 2960 bytes .../click/__pycache__/parser.cpython-312.pyc | Bin 0 -> 20419 bytes .../shell_completion.cpython-312.pyc | Bin 0 -> 23294 bytes .../click/__pycache__/termui.cpython-312.pyc | Bin 0 -> 34647 bytes .../click/__pycache__/testing.cpython-312.pyc | Bin 0 -> 27130 bytes .../click/__pycache__/types.cpython-312.pyc | Bin 0 -> 50059 bytes .../click/__pycache__/utils.cpython-312.pyc | Bin 0 -> 24864 bytes .../python3.12/site-packages/click/_compat.py | 622 ++ .../site-packages/click/_termui_impl.py | 852 ++ .../site-packages/click/_textwrap.py | 51 + .../python3.12/site-packages/click/_utils.py | 36 + .../site-packages/click/_winconsole.py | 296 + .../python3.12/site-packages/click/core.py | 3437 +++++++ .../site-packages/click/decorators.py | 551 ++ .../site-packages/click/exceptions.py | 308 + .../site-packages/click/formatting.py | 301 + .../python3.12/site-packages/click/globals.py | 67 + .../python3.12/site-packages/click/parser.py | 532 + .../python3.12/site-packages/click/py.typed | 0 .../site-packages/click/shell_completion.py | 667 ++ .../python3.12/site-packages/click/termui.py | 883 ++ .../python3.12/site-packages/click/testing.py | 574 ++ .../python3.12/site-packages/click/types.py | 1209 +++ .../python3.12/site-packages/click/utils.py | 627 ++ .../site-packages/dotenv/__init__.py | 49 + .../site-packages/dotenv/__main__.py | 6 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1691 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 355 bytes .../dotenv/__pycache__/cli.cpython-312.pyc | Bin 0 -> 9698 bytes .../__pycache__/ipython.cpython-312.pyc | Bin 0 -> 1964 bytes .../dotenv/__pycache__/main.cpython-312.pyc | Bin 0 -> 16082 bytes .../dotenv/__pycache__/parser.cpython-312.pyc | Bin 0 -> 9997 bytes .../__pycache__/variables.cpython-312.pyc | Bin 0 -> 5023 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 207 bytes .../python3.12/site-packages/dotenv/cli.py | 199 + .../site-packages/dotenv/ipython.py | 39 + .../python3.12/site-packages/dotenv/main.py | 382 + .../python3.12/site-packages/dotenv/parser.py | 175 + .../python3.12/site-packages/dotenv/py.typed | 1 + .../site-packages/dotenv/variables.py | 86 + .../site-packages/dotenv/version.py | 1 + .../flask-2.3.3.dist-info/INSTALLER | 1 + .../flask-2.3.3.dist-info/LICENSE.rst | 28 + .../flask-2.3.3.dist-info/METADATA | 116 + .../flask-2.3.3.dist-info/RECORD | 53 + .../flask-2.3.3.dist-info/REQUESTED | 0 .../site-packages/flask-2.3.3.dist-info/WHEEL | 4 + .../flask-2.3.3.dist-info/entry_points.txt | 3 + .../site-packages/flask/__init__.py | 102 + .../site-packages/flask/__main__.py | 3 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3237 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 235 bytes .../flask/__pycache__/app.cpython-312.pyc | Bin 0 -> 83073 bytes .../__pycache__/blueprints.cpython-312.pyc | Bin 0 -> 30725 bytes .../flask/__pycache__/cli.cpython-312.pyc | Bin 0 -> 39862 bytes .../flask/__pycache__/config.cpython-312.pyc | Bin 0 -> 15383 bytes .../flask/__pycache__/ctx.cpython-312.pyc | Bin 0 -> 19540 bytes .../__pycache__/debughelpers.cpython-312.pyc | Bin 0 -> 8533 bytes .../flask/__pycache__/globals.cpython-312.pyc | Bin 0 -> 3627 bytes .../flask/__pycache__/helpers.cpython-312.pyc | Bin 0 -> 28155 bytes .../flask/__pycache__/logging.cpython-312.pyc | Bin 0 -> 3259 bytes .../__pycache__/scaffold.cpython-312.pyc | Bin 0 -> 33320 bytes .../__pycache__/sessions.cpython-312.pyc | Bin 0 -> 15905 bytes .../flask/__pycache__/signals.cpython-312.pyc | Bin 0 -> 1727 bytes .../__pycache__/templating.cpython-312.pyc | Bin 0 -> 9884 bytes .../flask/__pycache__/testing.cpython-312.pyc | Bin 0 -> 13509 bytes .../flask/__pycache__/typing.cpython-312.pyc | Bin 0 -> 3774 bytes .../flask/__pycache__/views.cpython-312.pyc | Bin 0 -> 6823 bytes .../__pycache__/wrappers.cpython-312.pyc | Bin 0 -> 6076 bytes .../lib/python3.12/site-packages/flask/app.py | 2213 +++++ .../site-packages/flask/blueprints.py | 626 ++ .../lib/python3.12/site-packages/flask/cli.py | 1068 ++ .../python3.12/site-packages/flask/config.py | 347 + .../lib/python3.12/site-packages/flask/ctx.py | 440 + .../site-packages/flask/debughelpers.py | 160 + .../python3.12/site-packages/flask/globals.py | 96 + .../python3.12/site-packages/flask/helpers.py | 701 ++ .../site-packages/flask/json/__init__.py | 170 + .../json/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 6687 bytes .../json/__pycache__/provider.cpython-312.pyc | Bin 0 -> 9238 bytes .../json/__pycache__/tag.cpython-312.pyc | Bin 0 -> 13334 bytes .../site-packages/flask/json/provider.py | 216 + .../site-packages/flask/json/tag.py | 314 + .../python3.12/site-packages/flask/logging.py | 76 + .../python3.12/site-packages/flask/py.typed | 0 .../site-packages/flask/scaffold.py | 873 ++ .../site-packages/flask/sessions.py | 367 + .../python3.12/site-packages/flask/signals.py | 33 + .../site-packages/flask/templating.py | 220 + .../python3.12/site-packages/flask/testing.py | 295 + .../python3.12/site-packages/flask/typing.py | 82 + .../python3.12/site-packages/flask/views.py | 190 + .../site-packages/flask/wrappers.py | 173 + .../site-packages/flask_migrate/__init__.py | 266 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 14932 bytes .../__pycache__/cli.cpython-312.pyc | Bin 0 -> 11066 bytes .../site-packages/flask_migrate/cli.py | 251 + .../templates/aioflask-multidb/README | 1 + .../__pycache__/env.cpython-312.pyc | Bin 0 -> 8723 bytes .../aioflask-multidb/alembic.ini.mako | 50 + .../templates/aioflask-multidb/env.py | 202 + .../templates/aioflask-multidb/script.py.mako | 53 + .../flask_migrate/templates/aioflask/README | 1 + .../aioflask/__pycache__/env.cpython-312.pyc | Bin 0 -> 5040 bytes .../templates/aioflask/alembic.ini.mako | 50 + .../flask_migrate/templates/aioflask/env.py | 118 + .../templates/aioflask/script.py.mako | 24 + .../templates/flask-multidb/README | 1 + .../__pycache__/env.cpython-312.pyc | Bin 0 -> 7853 bytes .../templates/flask-multidb/alembic.ini.mako | 50 + .../templates/flask-multidb/env.py | 191 + .../templates/flask-multidb/script.py.mako | 53 + .../flask_migrate/templates/flask/README | 1 + .../flask/__pycache__/env.cpython-312.pyc | Bin 0 -> 4549 bytes .../templates/flask/alembic.ini.mako | 50 + .../flask_migrate/templates/flask/env.py | 113 + .../templates/flask/script.py.mako | 24 + .../INSTALLER | 1 + .../LICENSE.rst | 28 + .../flask_sqlalchemy-3.0.5.dist-info/METADATA | 104 + .../flask_sqlalchemy-3.0.5.dist-info/RECORD | 27 + .../REQUESTED | 0 .../flask_sqlalchemy-3.0.5.dist-info/WHEEL | 4 + .../flask_sqlalchemy/__init__.py | 46 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1699 bytes .../__pycache__/cli.cpython-312.pyc | Bin 0 -> 1024 bytes .../__pycache__/extension.cpython-312.pyc | Bin 0 -> 39993 bytes .../__pycache__/model.cpython-312.pyc | Bin 0 -> 9228 bytes .../__pycache__/pagination.cpython-312.pyc | Bin 0 -> 14277 bytes .../__pycache__/query.cpython-312.pyc | Bin 0 -> 4512 bytes .../record_queries.cpython-312.pyc | Bin 0 -> 5809 bytes .../__pycache__/session.cpython-312.pyc | Bin 0 -> 4270 bytes .../__pycache__/table.cpython-312.pyc | Bin 0 -> 1836 bytes .../track_modifications.cpython-312.pyc | Bin 0 -> 3608 bytes .../site-packages/flask_sqlalchemy/cli.py | 16 + .../flask_sqlalchemy/extension.py | 1008 ++ .../site-packages/flask_sqlalchemy/model.py | 214 + .../flask_sqlalchemy/pagination.py | 364 + .../site-packages/flask_sqlalchemy/py.typed | 0 .../site-packages/flask_sqlalchemy/query.py | 105 + .../flask_sqlalchemy/record_queries.py | 141 + .../site-packages/flask_sqlalchemy/session.py | 102 + .../site-packages/flask_sqlalchemy/table.py | 39 + .../flask_sqlalchemy/track_modifications.py | 88 + .../greenlet-3.4.0.dist-info/INSTALLER | 1 + .../greenlet-3.4.0.dist-info/METADATA | 98 + .../greenlet-3.4.0.dist-info/RECORD | 126 + .../greenlet-3.4.0.dist-info/WHEEL | 5 + .../greenlet-3.4.0.dist-info/licenses/LICENSE | 30 + .../licenses/LICENSE.PSF | 47 + .../sboms/auditwheel.cdx.json | 1 + .../greenlet-3.4.0.dist-info/top_level.txt | 1 + .../greenlet.libs/libgcc_s-0cd532bd.so.1 | Bin 0 -> 181737 bytes .../libstdc++-5d72f927.so.6.0.33 | Bin 0 -> 3562401 bytes .../site-packages/greenlet/CObjects.cpp | 160 + .../site-packages/greenlet/PyGreenlet.cpp | 841 ++ .../site-packages/greenlet/PyGreenlet.hpp | 35 + .../greenlet/PyGreenletUnswitchable.cpp | 150 + .../site-packages/greenlet/PyModule.cpp | 309 + .../greenlet/TBrokenGreenlet.cpp | 45 + .../greenlet/TExceptionState.cpp | 62 + .../site-packages/greenlet/TGreenlet.cpp | 729 ++ .../site-packages/greenlet/TGreenlet.hpp | 849 ++ .../greenlet/TGreenletGlobals.cpp | 113 + .../site-packages/greenlet/TMainGreenlet.cpp | 163 + .../site-packages/greenlet/TPythonState.cpp | 490 + .../site-packages/greenlet/TStackState.cpp | 265 + .../site-packages/greenlet/TThreadState.hpp | 627 ++ .../greenlet/TThreadStateCreator.hpp | 105 + .../greenlet/TThreadStateDestroy.cpp | 217 + .../site-packages/greenlet/TUserGreenlet.cpp | 674 ++ .../site-packages/greenlet/__init__.py | 62 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 863 bytes .../site-packages/greenlet/greenlet.cpp | 343 + .../site-packages/greenlet/greenlet.h | 164 + .../greenlet/greenlet_allocator.hpp | 76 + .../greenlet/greenlet_compiler_compat.hpp | 98 + .../greenlet/greenlet_cpython_compat.hpp | 117 + .../greenlet/greenlet_exceptions.hpp | 171 + .../greenlet/greenlet_internal.hpp | 109 + .../greenlet/greenlet_msvc_compat.hpp | 100 + .../site-packages/greenlet/greenlet_refs.hpp | 1172 +++ .../greenlet/greenlet_slp_switch.hpp | 103 + .../greenlet/greenlet_thread_support.hpp | 31 + .../greenlet/platform/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 193 bytes .../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 | 65 + .../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 | 41 + .../greenlet/platform/switch_s390_unix.h | 87 + .../greenlet/platform/switch_sh_gcc.h | 36 + .../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 | 77 + .../site-packages/greenlet/tests/__init__.py | 248 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 9229 bytes ...fail_clearing_run_switches.cpython-312.pyc | Bin 0 -> 2078 bytes .../fail_cpp_exception.cpython-312.pyc | Bin 0 -> 1613 bytes ...nitialstub_already_started.cpython-312.pyc | Bin 0 -> 3481 bytes .../fail_slp_switch.cpython-312.pyc | Bin 0 -> 1306 bytes ...ail_switch_three_greenlets.cpython-312.pyc | Bin 0 -> 1722 bytes ...il_switch_three_greenlets2.cpython-312.pyc | Bin 0 -> 2576 bytes .../fail_switch_two_greenlets.cpython-312.pyc | Bin 0 -> 1693 bytes .../__pycache__/leakcheck.cpython-312.pyc | Bin 0 -> 11635 bytes .../test_contextvars.cpython-312.pyc | Bin 0 -> 13814 bytes .../__pycache__/test_cpp.cpython-312.pyc | Bin 0 -> 5080 bytes .../test_extension_interface.cpython-312.pyc | Bin 0 -> 9269 bytes .../tests/__pycache__/test_gc.cpython-312.pyc | Bin 0 -> 4916 bytes .../test_generator.cpython-312.pyc | Bin 0 -> 3066 bytes .../test_generator_nested.cpython-312.pyc | Bin 0 -> 7760 bytes .../__pycache__/test_greenlet.cpython-312.pyc | Bin 0 -> 79349 bytes .../test_greenlet_trash.cpython-312.pyc | Bin 0 -> 6844 bytes .../test_interpreter_shutdown.cpython-312.pyc | Bin 0 -> 33901 bytes .../__pycache__/test_leaks.cpython-312.pyc | Bin 0 -> 19952 bytes .../test_stack_saved.cpython-312.pyc | Bin 0 -> 1345 bytes .../__pycache__/test_throw.cpython-312.pyc | Bin 0 -> 7353 bytes .../__pycache__/test_tracing.cpython-312.pyc | Bin 0 -> 13789 bytes .../__pycache__/test_version.cpython-312.pyc | Bin 0 -> 2748 bytes .../__pycache__/test_weakref.cpython-312.pyc | Bin 0 -> 2736 bytes .../greenlet/tests/_test_extension.c | 261 + .../greenlet/tests/_test_extension_cpp.cpp | 230 + .../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 | 334 + .../greenlet/tests/test_contextvars.py | 282 + .../site-packages/greenlet/tests/test_cpp.py | 91 + .../tests/test_extension_interface.py | 141 + .../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 | 1426 +++ .../greenlet/tests/test_greenlet_trash.py | 195 + .../tests/test_interpreter_shutdown.py | 822 ++ .../greenlet/tests/test_leaks.py | 478 + .../greenlet/tests/test_stack_saved.py | 19 + .../greenlet/tests/test_throw.py | 128 + .../greenlet/tests/test_tracing.py | 298 + .../greenlet/tests/test_version.py | 46 + .../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 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1626 bytes .../__pycache__/_json.cpython-312.pyc | Bin 0 -> 1180 bytes .../__pycache__/encoding.cpython-312.pyc | Bin 0 -> 2680 bytes .../__pycache__/exc.cpython-312.pyc | Bin 0 -> 3940 bytes .../__pycache__/serializer.cpython-312.pyc | Bin 0 -> 15408 bytes .../__pycache__/signer.cpython-312.pyc | Bin 0 -> 11285 bytes .../__pycache__/timed.cpython-312.pyc | Bin 0 -> 8729 bytes .../__pycache__/url_safe.cpython-312.pyc | Bin 0 -> 3530 bytes .../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.6.dist-info/INSTALLER | 1 + .../jinja2-3.1.6.dist-info/METADATA | 84 + .../jinja2-3.1.6.dist-info/RECORD | 57 + .../jinja2-3.1.6.dist-info/WHEEL | 4 + .../jinja2-3.1.6.dist-info/entry_points.txt | 3 + .../licenses/LICENSE.txt | 28 + .../site-packages/jinja2/__init__.py | 38 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1651 bytes .../__pycache__/_identifier.cpython-312.pyc | Bin 0 -> 2132 bytes .../__pycache__/async_utils.cpython-312.pyc | Bin 0 -> 4972 bytes .../__pycache__/bccache.cpython-312.pyc | Bin 0 -> 19323 bytes .../__pycache__/compiler.cpython-312.pyc | Bin 0 -> 103890 bytes .../__pycache__/constants.cpython-312.pyc | Bin 0 -> 1554 bytes .../jinja2/__pycache__/debug.cpython-312.pyc | Bin 0 -> 6564 bytes .../__pycache__/defaults.cpython-312.pyc | Bin 0 -> 1604 bytes .../__pycache__/environment.cpython-312.pyc | Bin 0 -> 76633 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 7713 bytes .../jinja2/__pycache__/ext.cpython-312.pyc | Bin 0 -> 41875 bytes .../__pycache__/filters.cpython-312.pyc | Bin 0 -> 72267 bytes .../__pycache__/idtracking.cpython-312.pyc | Bin 0 -> 19118 bytes .../jinja2/__pycache__/lexer.cpython-312.pyc | Bin 0 -> 32063 bytes .../__pycache__/loaders.cpython-312.pyc | Bin 0 -> 32273 bytes .../jinja2/__pycache__/meta.cpython-312.pyc | Bin 0 -> 5468 bytes .../__pycache__/nativetypes.cpython-312.pyc | Bin 0 -> 7017 bytes .../jinja2/__pycache__/nodes.cpython-312.pyc | Bin 0 -> 58217 bytes .../__pycache__/optimizer.cpython-312.pyc | Bin 0 -> 2687 bytes .../jinja2/__pycache__/parser.cpython-312.pyc | Bin 0 -> 61178 bytes .../__pycache__/runtime.cpython-312.pyc | Bin 0 -> 48882 bytes .../__pycache__/sandbox.cpython-312.pyc | Bin 0 -> 18095 bytes .../jinja2/__pycache__/tests.cpython-312.pyc | Bin 0 -> 9048 bytes .../jinja2/__pycache__/utils.cpython-312.pyc | Bin 0 -> 34796 bytes .../__pycache__/visitor.cpython-312.pyc | Bin 0 -> 5351 bytes .../site-packages/jinja2/_identifier.py | 6 + .../site-packages/jinja2/async_utils.py | 99 + .../site-packages/jinja2/bccache.py | 408 + .../site-packages/jinja2/compiler.py | 1998 ++++ .../site-packages/jinja2/constants.py | 20 + .../python3.12/site-packages/jinja2/debug.py | 191 + .../site-packages/jinja2/defaults.py | 48 + .../site-packages/jinja2/environment.py | 1672 ++++ .../site-packages/jinja2/exceptions.py | 166 + .../python3.12/site-packages/jinja2/ext.py | 870 ++ .../site-packages/jinja2/filters.py | 1873 ++++ .../site-packages/jinja2/idtracking.py | 318 + .../python3.12/site-packages/jinja2/lexer.py | 868 ++ .../site-packages/jinja2/loaders.py | 693 ++ .../python3.12/site-packages/jinja2/meta.py | 112 + .../site-packages/jinja2/nativetypes.py | 130 + .../python3.12/site-packages/jinja2/nodes.py | 1206 +++ .../site-packages/jinja2/optimizer.py | 48 + .../python3.12/site-packages/jinja2/parser.py | 1049 ++ .../python3.12/site-packages/jinja2/py.typed | 0 .../site-packages/jinja2/runtime.py | 1062 ++ .../site-packages/jinja2/sandbox.py | 436 + .../python3.12/site-packages/jinja2/tests.py | 256 + .../python3.12/site-packages/jinja2/utils.py | 766 ++ .../site-packages/jinja2/visitor.py | 92 + .../mako-1.3.11.dist-info/INSTALLER | 1 + .../mako-1.3.11.dist-info/METADATA | 88 + .../mako-1.3.11.dist-info/RECORD | 74 + .../site-packages/mako-1.3.11.dist-info/WHEEL | 5 + .../mako-1.3.11.dist-info/entry_points.txt | 18 + .../mako-1.3.11.dist-info/licenses/LICENSE | 19 + .../mako-1.3.11.dist-info/top_level.txt | 1 + .../python3.12/site-packages/mako/__init__.py | 8 + .../mako/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 209 bytes .../__pycache__/_ast_util.cpython-312.pyc | Bin 0 -> 36286 bytes .../mako/__pycache__/ast.cpython-312.pyc | Bin 0 -> 7483 bytes .../mako/__pycache__/cache.cpython-312.pyc | Bin 0 -> 8492 bytes .../mako/__pycache__/cmd.cpython-312.pyc | Bin 0 -> 3744 bytes .../mako/__pycache__/codegen.cpython-312.pyc | Bin 0 -> 58962 bytes .../mako/__pycache__/compat.cpython-312.pyc | Bin 0 -> 3069 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 14758 bytes .../mako/__pycache__/filters.cpython-312.pyc | Bin 0 -> 6704 bytes .../mako/__pycache__/lexer.cpython-312.pyc | Bin 0 -> 20330 bytes .../mako/__pycache__/lookup.cpython-312.pyc | Bin 0 -> 13704 bytes .../__pycache__/parsetree.cpython-312.pyc | Bin 0 -> 29961 bytes .../mako/__pycache__/pygen.cpython-312.pyc | Bin 0 -> 11027 bytes .../mako/__pycache__/pyparser.cpython-312.pyc | Bin 0 -> 12243 bytes .../mako/__pycache__/runtime.cpython-312.pyc | Bin 0 -> 39071 bytes .../mako/__pycache__/template.cpython-312.pyc | Bin 0 -> 26773 bytes .../mako/__pycache__/util.cpython-312.pyc | Bin 0 -> 20352 bytes .../site-packages/mako/_ast_util.py | 713 ++ venv/lib/python3.12/site-packages/mako/ast.py | 202 + .../python3.12/site-packages/mako/cache.py | 239 + venv/lib/python3.12/site-packages/mako/cmd.py | 99 + .../python3.12/site-packages/mako/codegen.py | 1319 +++ .../python3.12/site-packages/mako/compat.py | 70 + .../site-packages/mako/exceptions.py | 417 + .../site-packages/mako/ext/__init__.py | 0 .../ext/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 184 bytes .../__pycache__/autohandler.cpython-312.pyc | Bin 0 -> 2352 bytes .../__pycache__/babelplugin.cpython-312.pyc | Bin 0 -> 2666 bytes .../__pycache__/beaker_cache.cpython-312.pyc | Bin 0 -> 3919 bytes .../ext/__pycache__/extract.cpython-312.pyc | Bin 0 -> 5137 bytes .../__pycache__/linguaplugin.cpython-312.pyc | Bin 0 -> 2617 bytes .../__pycache__/preprocessors.cpython-312.pyc | Bin 0 -> 679 bytes .../__pycache__/pygmentplugin.cpython-312.pyc | Bin 0 -> 5910 bytes .../__pycache__/turbogears.cpython-312.pyc | Bin 0 -> 2447 bytes .../site-packages/mako/ext/autohandler.py | 70 + .../site-packages/mako/ext/babelplugin.py | 57 + .../site-packages/mako/ext/beaker_cache.py | 82 + .../site-packages/mako/ext/extract.py | 129 + .../site-packages/mako/ext/linguaplugin.py | 57 + .../site-packages/mako/ext/preprocessors.py | 20 + .../site-packages/mako/ext/pygmentplugin.py | 150 + .../site-packages/mako/ext/turbogears.py | 61 + .../python3.12/site-packages/mako/filters.py | 163 + .../python3.12/site-packages/mako/lexer.py | 481 + .../python3.12/site-packages/mako/lookup.py | 361 + .../site-packages/mako/parsetree.py | 656 ++ .../python3.12/site-packages/mako/pygen.py | 309 + .../python3.12/site-packages/mako/pyparser.py | 234 + .../python3.12/site-packages/mako/runtime.py | 968 ++ .../python3.12/site-packages/mako/template.py | 709 ++ .../site-packages/mako/testing/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 188 bytes .../__pycache__/_config.cpython-312.pyc | Bin 0 -> 5823 bytes .../__pycache__/assertions.cpython-312.pyc | Bin 0 -> 5894 bytes .../__pycache__/config.cpython-312.pyc | Bin 0 -> 802 bytes .../__pycache__/exclusions.cpython-312.pyc | Bin 0 -> 2405 bytes .../__pycache__/fixtures.cpython-312.pyc | Bin 0 -> 4800 bytes .../__pycache__/helpers.cpython-312.pyc | Bin 0 -> 3496 bytes .../site-packages/mako/testing/_config.py | 128 + .../site-packages/mako/testing/assertions.py | 166 + .../site-packages/mako/testing/config.py | 17 + .../site-packages/mako/testing/exclusions.py | 80 + .../site-packages/mako/testing/fixtures.py | 119 + .../site-packages/mako/testing/helpers.py | 71 + .../lib/python3.12/site-packages/mako/util.py | 388 + .../markupsafe-3.0.3.dist-info/INSTALLER | 1 + .../markupsafe-3.0.3.dist-info/METADATA | 74 + .../markupsafe-3.0.3.dist-info/RECORD | 14 + .../markupsafe-3.0.3.dist-info/WHEEL | 5 + .../licenses/LICENSE.txt | 28 + .../markupsafe-3.0.3.dist-info/top_level.txt | 1 + .../site-packages/markupsafe/__init__.py | 396 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 20986 bytes .../__pycache__/_native.cpython-312.pyc | Bin 0 -> 613 bytes .../site-packages/markupsafe/_native.py | 8 + .../site-packages/markupsafe/_speedups.c | 200 + .../site-packages/markupsafe/_speedups.pyi | 1 + .../site-packages/markupsafe/py.typed | 0 .../pip-25.0.1.dist-info/AUTHORS.txt | 806 ++ .../pip-25.0.1.dist-info/INSTALLER | 1 + .../pip-25.0.1.dist-info/LICENSE.txt | 20 + .../pip-25.0.1.dist-info/METADATA | 90 + .../site-packages/pip-25.0.1.dist-info/RECORD | 854 ++ .../pip-25.0.1.dist-info/REQUESTED | 0 .../site-packages/pip-25.0.1.dist-info/WHEEL | 5 + .../pip-25.0.1.dist-info/entry_points.txt | 3 + .../pip-25.0.1.dist-info/top_level.txt | 1 + .../python3.12/site-packages/pip/__init__.py | 13 + .../python3.12/site-packages/pip/__main__.py | 24 + .../site-packages/pip/__pip-runner__.py | 50 + .../pip/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 690 bytes .../pip/__pycache__/__main__.cpython-312.pyc | Bin 0 -> 844 bytes .../__pip-runner__.cpython-312.pyc | Bin 0 -> 2208 bytes .../site-packages/pip/_internal/__init__.py | 18 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 790 bytes .../__pycache__/build_env.cpython-312.pyc | Bin 0 -> 14774 bytes .../__pycache__/cache.cpython-312.pyc | Bin 0 -> 12670 bytes .../__pycache__/configuration.cpython-312.pyc | Bin 0 -> 17634 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 36848 bytes .../__pycache__/main.cpython-312.pyc | Bin 0 -> 673 bytes .../__pycache__/pyproject.cpython-312.pyc | Bin 0 -> 5121 bytes .../self_outdated_check.cpython-312.pyc | Bin 0 -> 10406 bytes .../__pycache__/wheel_builder.cpython-312.pyc | Bin 0 -> 13618 bytes .../site-packages/pip/_internal/build_env.py | 322 + .../site-packages/pip/_internal/cache.py | 290 + .../pip/_internal/cli/__init__.py | 4 + .../cli/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 281 bytes .../autocompletion.cpython-312.pyc | Bin 0 -> 8609 bytes .../__pycache__/base_command.cpython-312.pyc | Bin 0 -> 10511 bytes .../__pycache__/cmdoptions.cpython-312.pyc | Bin 0 -> 30404 bytes .../command_context.cpython-312.pyc | Bin 0 -> 1777 bytes .../__pycache__/index_command.cpython-312.pyc | Bin 0 -> 7184 bytes .../cli/__pycache__/main.cpython-312.pyc | Bin 0 -> 2303 bytes .../__pycache__/main_parser.cpython-312.pyc | Bin 0 -> 4909 bytes .../cli/__pycache__/parser.cpython-312.pyc | Bin 0 -> 15043 bytes .../__pycache__/progress_bars.cpython-312.pyc | Bin 0 -> 3855 bytes .../__pycache__/req_command.cpython-312.pyc | Bin 0 -> 12244 bytes .../cli/__pycache__/spinners.cpython-312.pyc | Bin 0 -> 7836 bytes .../__pycache__/status_codes.cpython-312.pyc | Bin 0 -> 378 bytes .../pip/_internal/cli/autocompletion.py | 176 + .../pip/_internal/cli/base_command.py | 240 + .../pip/_internal/cli/cmdoptions.py | 1075 ++ .../pip/_internal/cli/command_context.py | 27 + .../pip/_internal/cli/index_command.py | 171 + .../site-packages/pip/_internal/cli/main.py | 80 + .../pip/_internal/cli/main_parser.py | 134 + .../site-packages/pip/_internal/cli/parser.py | 294 + .../pip/_internal/cli/progress_bars.py | 94 + .../pip/_internal/cli/req_command.py | 329 + .../pip/_internal/cli/spinners.py | 159 + .../pip/_internal/cli/status_codes.py | 6 + .../pip/_internal/commands/__init__.py | 132 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4005 bytes .../__pycache__/cache.cpython-312.pyc | Bin 0 -> 9931 bytes .../__pycache__/check.cpython-312.pyc | Bin 0 -> 2595 bytes .../__pycache__/completion.cpython-312.pyc | Bin 0 -> 5196 bytes .../__pycache__/configuration.cpython-312.pyc | Bin 0 -> 13166 bytes .../__pycache__/debug.cpython-312.pyc | Bin 0 -> 10071 bytes .../__pycache__/download.cpython-312.pyc | Bin 0 -> 7506 bytes .../__pycache__/freeze.cpython-312.pyc | Bin 0 -> 4388 bytes .../commands/__pycache__/hash.cpython-312.pyc | Bin 0 -> 2971 bytes .../commands/__pycache__/help.cpython-312.pyc | Bin 0 -> 1676 bytes .../__pycache__/index.cpython-312.pyc | Bin 0 -> 6674 bytes .../__pycache__/inspect.cpython-312.pyc | Bin 0 -> 3983 bytes .../__pycache__/install.cpython-312.pyc | Bin 0 -> 29095 bytes .../commands/__pycache__/list.cpython-312.pyc | Bin 0 -> 15760 bytes .../__pycache__/search.cpython-312.pyc | Bin 0 -> 7521 bytes .../commands/__pycache__/show.cpython-312.pyc | Bin 0 -> 10940 bytes .../__pycache__/uninstall.cpython-312.pyc | Bin 0 -> 4712 bytes .../__pycache__/wheel.cpython-312.pyc | Bin 0 -> 8868 bytes .../pip/_internal/commands/cache.py | 228 + .../pip/_internal/commands/check.py | 67 + .../pip/_internal/commands/completion.py | 130 + .../pip/_internal/commands/configuration.py | 280 + .../pip/_internal/commands/debug.py | 201 + .../pip/_internal/commands/download.py | 146 + .../pip/_internal/commands/freeze.py | 109 + .../pip/_internal/commands/hash.py | 59 + .../pip/_internal/commands/help.py | 41 + .../pip/_internal/commands/index.py | 139 + .../pip/_internal/commands/inspect.py | 92 + .../pip/_internal/commands/install.py | 784 ++ .../pip/_internal/commands/list.py | 375 + .../pip/_internal/commands/search.py | 172 + .../pip/_internal/commands/show.py | 224 + .../pip/_internal/commands/uninstall.py | 114 + .../pip/_internal/commands/wheel.py | 182 + .../pip/_internal/configuration.py | 383 + .../pip/_internal/distributions/__init__.py | 21 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 944 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 2896 bytes .../__pycache__/installed.cpython-312.pyc | Bin 0 -> 1703 bytes .../__pycache__/sdist.cpython-312.pyc | Bin 0 -> 8430 bytes .../__pycache__/wheel.cpython-312.pyc | Bin 0 -> 2284 bytes .../pip/_internal/distributions/base.py | 53 + .../pip/_internal/distributions/installed.py | 29 + .../pip/_internal/distributions/sdist.py | 158 + .../pip/_internal/distributions/wheel.py | 42 + .../site-packages/pip/_internal/exceptions.py | 809 ++ .../pip/_internal/index/__init__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 235 bytes .../__pycache__/collector.cpython-312.pyc | Bin 0 -> 21620 bytes .../package_finder.cpython-312.pyc | Bin 0 -> 41368 bytes .../index/__pycache__/sources.cpython-312.pyc | Bin 0 -> 12527 bytes .../pip/_internal/index/collector.py | 494 + .../pip/_internal/index/package_finder.py | 1029 ++ .../pip/_internal/index/sources.py | 284 + .../pip/_internal/locations/__init__.py | 456 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 16443 bytes .../__pycache__/_distutils.cpython-312.pyc | Bin 0 -> 6794 bytes .../__pycache__/_sysconfig.cpython-312.pyc | Bin 0 -> 8031 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 3784 bytes .../pip/_internal/locations/_distutils.py | 172 + .../pip/_internal/locations/_sysconfig.py | 214 + .../pip/_internal/locations/base.py | 81 + .../site-packages/pip/_internal/main.py | 12 + .../pip/_internal/metadata/__init__.py | 128 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 5870 bytes .../__pycache__/_json.cpython-312.pyc | Bin 0 -> 2970 bytes .../metadata/__pycache__/base.cpython-312.pyc | Bin 0 -> 35201 bytes .../__pycache__/pkg_resources.cpython-312.pyc | Bin 0 -> 16087 bytes .../pip/_internal/metadata/_json.py | 86 + .../pip/_internal/metadata/base.py | 688 ++ .../_internal/metadata/importlib/__init__.py | 6 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 361 bytes .../__pycache__/_compat.cpython-312.pyc | Bin 0 -> 4494 bytes .../__pycache__/_dists.cpython-312.pyc | Bin 0 -> 12882 bytes .../__pycache__/_envs.cpython-312.pyc | Bin 0 -> 11082 bytes .../_internal/metadata/importlib/_compat.py | 85 + .../_internal/metadata/importlib/_dists.py | 228 + .../pip/_internal/metadata/importlib/_envs.py | 189 + .../pip/_internal/metadata/pkg_resources.py | 301 + .../pip/_internal/models/__init__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 269 bytes .../__pycache__/candidate.cpython-312.pyc | Bin 0 -> 1607 bytes .../__pycache__/direct_url.cpython-312.pyc | Bin 0 -> 10847 bytes .../format_control.cpython-312.pyc | Bin 0 -> 4226 bytes .../models/__pycache__/index.cpython-312.pyc | Bin 0 -> 1697 bytes .../installation_report.cpython-312.pyc | Bin 0 -> 2280 bytes .../models/__pycache__/link.cpython-312.pyc | Bin 0 -> 26986 bytes .../models/__pycache__/scheme.cpython-312.pyc | Bin 0 -> 1026 bytes .../__pycache__/search_scope.cpython-312.pyc | Bin 0 -> 4990 bytes .../selection_prefs.cpython-312.pyc | Bin 0 -> 1854 bytes .../__pycache__/target_python.cpython-312.pyc | Bin 0 -> 4956 bytes .../models/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 6555 bytes .../pip/_internal/models/candidate.py | 25 + .../pip/_internal/models/direct_url.py | 224 + .../pip/_internal/models/format_control.py | 78 + .../pip/_internal/models/index.py | 28 + .../_internal/models/installation_report.py | 56 + .../pip/_internal/models/link.py | 604 ++ .../pip/_internal/models/scheme.py | 25 + .../pip/_internal/models/search_scope.py | 127 + .../pip/_internal/models/selection_prefs.py | 53 + .../pip/_internal/models/target_python.py | 121 + .../pip/_internal/models/wheel.py | 118 + .../pip/_internal/network/__init__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 257 bytes .../network/__pycache__/auth.cpython-312.pyc | Bin 0 -> 22102 bytes .../network/__pycache__/cache.cpython-312.pyc | Bin 0 -> 7049 bytes .../__pycache__/download.cpython-312.pyc | Bin 0 -> 8482 bytes .../__pycache__/lazy_wheel.cpython-312.pyc | Bin 0 -> 11610 bytes .../__pycache__/session.cpython-312.pyc | Bin 0 -> 18909 bytes .../network/__pycache__/utils.cpython-312.pyc | Bin 0 -> 2258 bytes .../__pycache__/xmlrpc.cpython-312.pyc | Bin 0 -> 2952 bytes .../pip/_internal/network/auth.py | 566 ++ .../pip/_internal/network/cache.py | 118 + .../pip/_internal/network/download.py | 187 + .../pip/_internal/network/lazy_wheel.py | 210 + .../pip/_internal/network/session.py | 523 + .../pip/_internal/network/utils.py | 98 + .../pip/_internal/network/xmlrpc.py | 62 + .../pip/_internal/operations/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 200 bytes .../__pycache__/check.cpython-312.pyc | Bin 0 -> 7107 bytes .../__pycache__/freeze.cpython-312.pyc | Bin 0 -> 10251 bytes .../__pycache__/prepare.cpython-312.pyc | Bin 0 -> 25775 bytes .../_internal/operations/build/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 206 bytes .../__pycache__/build_tracker.cpython-312.pyc | Bin 0 -> 7670 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 0 -> 1860 bytes .../metadata_editable.cpython-312.pyc | Bin 0 -> 1914 bytes .../metadata_legacy.cpython-312.pyc | Bin 0 -> 3014 bytes .../build/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 1680 bytes .../wheel_editable.cpython-312.pyc | Bin 0 -> 2019 bytes .../__pycache__/wheel_legacy.cpython-312.pyc | Bin 0 -> 3849 bytes .../operations/build/build_tracker.py | 138 + .../_internal/operations/build/metadata.py | 39 + .../operations/build/metadata_editable.py | 42 + .../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 | 181 + .../pip/_internal/operations/freeze.py | 256 + .../_internal/operations/install/__init__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 269 bytes .../editable_legacy.cpython-312.pyc | Bin 0 -> 1801 bytes .../install/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 34106 bytes .../operations/install/editable_legacy.py | 47 + .../pip/_internal/operations/install/wheel.py | 741 ++ .../pip/_internal/operations/prepare.py | 732 ++ .../site-packages/pip/_internal/pyproject.py | 185 + .../pip/_internal/req/__init__.py | 90 + .../req/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3448 bytes .../__pycache__/constructors.cpython-312.pyc | Bin 0 -> 21258 bytes .../req/__pycache__/req_file.cpython-312.pyc | Bin 0 -> 24241 bytes .../__pycache__/req_install.cpython-312.pyc | Bin 0 -> 38473 bytes .../req/__pycache__/req_set.cpython-312.pyc | Bin 0 -> 5486 bytes .../__pycache__/req_uninstall.cpython-312.pyc | Bin 0 -> 32097 bytes .../pip/_internal/req/constructors.py | 560 ++ .../pip/_internal/req/req_file.py | 623 ++ .../pip/_internal/req/req_install.py | 934 ++ .../pip/_internal/req/req_set.py | 82 + .../pip/_internal/req/req_uninstall.py | 633 ++ .../pip/_internal/resolution/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 200 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 1188 bytes .../pip/_internal/resolution/base.py | 20 + .../_internal/resolution/legacy/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 207 bytes .../__pycache__/resolver.cpython-312.pyc | Bin 0 -> 22582 bytes .../_internal/resolution/legacy/resolver.py | 597 ++ .../resolution/resolvelib/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 211 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 8152 bytes .../__pycache__/candidates.cpython-312.pyc | Bin 0 -> 29411 bytes .../__pycache__/factory.cpython-312.pyc | Bin 0 -> 32489 bytes .../found_candidates.cpython-312.pyc | Bin 0 -> 6796 bytes .../__pycache__/provider.cpython-312.pyc | Bin 0 -> 10526 bytes .../__pycache__/reporter.cpython-312.pyc | Bin 0 -> 5043 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 0 -> 15359 bytes .../__pycache__/resolver.cpython-312.pyc | Bin 0 -> 12317 bytes .../_internal/resolution/resolvelib/base.py | 139 + .../resolution/resolvelib/candidates.py | 574 ++ .../resolution/resolvelib/factory.py | 823 ++ .../resolution/resolvelib/found_candidates.py | 174 + .../resolution/resolvelib/provider.py | 258 + .../resolution/resolvelib/reporter.py | 81 + .../resolution/resolvelib/requirements.py | 245 + .../resolution/resolvelib/resolver.py | 317 + .../pip/_internal/self_outdated_check.py | 252 + .../pip/_internal/utils/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 195 bytes .../__pycache__/_jaraco_text.cpython-312.pyc | Bin 0 -> 4530 bytes .../utils/__pycache__/_log.cpython-312.pyc | Bin 0 -> 1866 bytes .../utils/__pycache__/appdirs.cpython-312.pyc | Bin 0 -> 2410 bytes .../utils/__pycache__/compat.cpython-312.pyc | Bin 0 -> 2907 bytes .../compatibility_tags.cpython-312.pyc | Bin 0 -> 6350 bytes .../__pycache__/datetime.cpython-312.pyc | Bin 0 -> 684 bytes .../__pycache__/deprecation.cpython-312.pyc | Bin 0 -> 4191 bytes .../direct_url_helpers.cpython-312.pyc | Bin 0 -> 3536 bytes .../__pycache__/egg_link.cpython-312.pyc | Bin 0 -> 3206 bytes .../__pycache__/entrypoints.cpython-312.pyc | Bin 0 -> 3993 bytes .../__pycache__/filesystem.cpython-312.pyc | Bin 0 -> 7329 bytes .../__pycache__/filetypes.cpython-312.pyc | Bin 0 -> 1164 bytes .../utils/__pycache__/glibc.cpython-312.pyc | Bin 0 -> 2419 bytes .../utils/__pycache__/hashes.cpython-312.pyc | Bin 0 -> 7603 bytes .../utils/__pycache__/logging.cpython-312.pyc | Bin 0 -> 13891 bytes .../utils/__pycache__/misc.cpython-312.pyc | Bin 0 -> 33226 bytes .../__pycache__/packaging.cpython-312.pyc | Bin 0 -> 2660 bytes .../utils/__pycache__/retry.cpython-312.pyc | Bin 0 -> 2108 bytes .../setuptools_build.cpython-312.pyc | Bin 0 -> 4550 bytes .../__pycache__/subprocess.cpython-312.pyc | Bin 0 -> 8639 bytes .../__pycache__/temp_dir.cpython-312.pyc | Bin 0 -> 12024 bytes .../__pycache__/unpacking.cpython-312.pyc | Bin 0 -> 13498 bytes .../utils/__pycache__/urls.cpython-312.pyc | Bin 0 -> 2077 bytes .../__pycache__/virtualenv.cpython-312.pyc | Bin 0 -> 4466 bytes .../utils/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 5903 bytes .../pip/_internal/utils/_jaraco_text.py | 109 + .../site-packages/pip/_internal/utils/_log.py | 38 + .../pip/_internal/utils/appdirs.py | 52 + .../pip/_internal/utils/compat.py | 79 + .../pip/_internal/utils/compatibility_tags.py | 188 + .../pip/_internal/utils/datetime.py | 11 + .../pip/_internal/utils/deprecation.py | 124 + .../pip/_internal/utils/direct_url_helpers.py | 87 + .../pip/_internal/utils/egg_link.py | 80 + .../pip/_internal/utils/entrypoints.py | 84 + .../pip/_internal/utils/filesystem.py | 149 + .../pip/_internal/utils/filetypes.py | 27 + .../pip/_internal/utils/glibc.py | 101 + .../pip/_internal/utils/hashes.py | 147 + .../pip/_internal/utils/logging.py | 354 + .../site-packages/pip/_internal/utils/misc.py | 773 ++ .../pip/_internal/utils/packaging.py | 58 + .../pip/_internal/utils/retry.py | 42 + .../pip/_internal/utils/setuptools_build.py | 146 + .../pip/_internal/utils/subprocess.py | 245 + .../pip/_internal/utils/temp_dir.py | 296 + .../pip/_internal/utils/unpacking.py | 337 + .../site-packages/pip/_internal/utils/urls.py | 55 + .../pip/_internal/utils/virtualenv.py | 104 + .../pip/_internal/utils/wheel.py | 134 + .../pip/_internal/vcs/__init__.py | 15 + .../vcs/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 534 bytes .../vcs/__pycache__/bazaar.cpython-312.pyc | Bin 0 -> 5055 bytes .../vcs/__pycache__/git.cpython-312.pyc | Bin 0 -> 19020 bytes .../vcs/__pycache__/mercurial.cpython-312.pyc | Bin 0 -> 7608 bytes .../__pycache__/subversion.cpython-312.pyc | Bin 0 -> 12527 bytes .../versioncontrol.cpython-312.pyc | Bin 0 -> 29000 bytes .../site-packages/pip/_internal/vcs/bazaar.py | 112 + .../site-packages/pip/_internal/vcs/git.py | 527 + .../pip/_internal/vcs/mercurial.py | 163 + .../pip/_internal/vcs/subversion.py | 324 + .../pip/_internal/vcs/versioncontrol.py | 688 ++ .../pip/_internal/wheel_builder.py | 354 + .../site-packages/pip/_vendor/__init__.py | 116 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4553 bytes .../typing_extensions.cpython-312.pyc | Bin 0 -> 139455 bytes .../pip/_vendor/cachecontrol/__init__.py | 29 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 906 bytes .../__pycache__/_cmd.cpython-312.pyc | Bin 0 -> 2650 bytes .../__pycache__/adapter.cpython-312.pyc | Bin 0 -> 6468 bytes .../__pycache__/cache.cpython-312.pyc | Bin 0 -> 3791 bytes .../__pycache__/controller.cpython-312.pyc | Bin 0 -> 16228 bytes .../__pycache__/filewrapper.cpython-312.pyc | Bin 0 -> 4351 bytes .../__pycache__/heuristics.cpython-312.pyc | Bin 0 -> 6701 bytes .../__pycache__/serialize.cpython-312.pyc | Bin 0 -> 5265 bytes .../__pycache__/wrapper.cpython-312.pyc | Bin 0 -> 1678 bytes .../pip/_vendor/cachecontrol/_cmd.py | 70 + .../pip/_vendor/cachecontrol/adapter.py | 161 + .../pip/_vendor/cachecontrol/cache.py | 75 + .../_vendor/cachecontrol/caches/__init__.py | 8 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 439 bytes .../__pycache__/file_cache.cpython-312.pyc | Bin 0 -> 7748 bytes .../__pycache__/redis_cache.cpython-312.pyc | Bin 0 -> 2737 bytes .../_vendor/cachecontrol/caches/file_cache.py | 182 + .../cachecontrol/caches/redis_cache.py | 48 + .../pip/_vendor/cachecontrol/controller.py | 500 + .../pip/_vendor/cachecontrol/filewrapper.py | 119 + .../pip/_vendor/cachecontrol/heuristics.py | 157 + .../pip/_vendor/cachecontrol/py.typed | 0 .../pip/_vendor/cachecontrol/serialize.py | 146 + .../pip/_vendor/cachecontrol/wrapper.py | 43 + .../pip/_vendor/certifi/__init__.py | 4 + .../pip/_vendor/certifi/__main__.py | 12 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 322 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 649 bytes .../certifi/__pycache__/core.cpython-312.pyc | Bin 0 -> 3215 bytes .../pip/_vendor/certifi/cacert.pem | 4929 ++++++++++ .../site-packages/pip/_vendor/certifi/core.py | 114 + .../pip/_vendor/certifi/py.typed | 0 .../pip/_vendor/distlib/__init__.py | 33 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1273 bytes .../__pycache__/compat.cpython-312.pyc | Bin 0 -> 45530 bytes .../__pycache__/database.cpython-312.pyc | Bin 0 -> 65585 bytes .../distlib/__pycache__/index.cpython-312.pyc | Bin 0 -> 24320 bytes .../__pycache__/locators.cpython-312.pyc | Bin 0 -> 59865 bytes .../__pycache__/manifest.cpython-312.pyc | Bin 0 -> 15080 bytes .../__pycache__/markers.cpython-312.pyc | Bin 0 -> 7658 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 0 -> 41564 bytes .../__pycache__/resources.cpython-312.pyc | Bin 0 -> 17316 bytes .../__pycache__/scripts.cpython-312.pyc | Bin 0 -> 19757 bytes .../distlib/__pycache__/util.cpython-312.pyc | Bin 0 -> 88039 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 30348 bytes .../distlib/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 52549 bytes .../pip/_vendor/distlib/compat.py | 1137 +++ .../pip/_vendor/distlib/database.py | 1329 +++ .../pip/_vendor/distlib/index.py | 508 + .../pip/_vendor/distlib/locators.py | 1295 +++ .../pip/_vendor/distlib/manifest.py | 384 + .../pip/_vendor/distlib/markers.py | 162 + .../pip/_vendor/distlib/metadata.py | 1031 ++ .../pip/_vendor/distlib/resources.py | 358 + .../pip/_vendor/distlib/scripts.py | 447 + .../site-packages/pip/_vendor/distlib/util.py | 1984 ++++ .../pip/_vendor/distlib/version.py | 750 ++ .../pip/_vendor/distlib/wheel.py | 1100 +++ .../pip/_vendor/distro/__init__.py | 54 + .../pip/_vendor/distro/__main__.py | 4 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 964 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 296 bytes .../distro/__pycache__/distro.cpython-312.pyc | Bin 0 -> 53796 bytes .../pip/_vendor/distro/distro.py | 1403 +++ .../site-packages/pip/_vendor/distro/py.typed | 0 .../pip/_vendor/idna/__init__.py | 45 + .../idna/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 890 bytes .../idna/__pycache__/codec.cpython-312.pyc | Bin 0 -> 4980 bytes .../idna/__pycache__/compat.cpython-312.pyc | Bin 0 -> 894 bytes .../idna/__pycache__/core.cpython-312.pyc | Bin 0 -> 16125 bytes .../idna/__pycache__/idnadata.cpython-312.pyc | Bin 0 -> 99480 bytes .../__pycache__/intranges.cpython-312.pyc | Bin 0 -> 2637 bytes .../__pycache__/package_data.cpython-312.pyc | Bin 0 -> 221 bytes .../__pycache__/uts46data.cpython-312.pyc | Bin 0 -> 158850 bytes .../site-packages/pip/_vendor/idna/codec.py | 122 + .../site-packages/pip/_vendor/idna/compat.py | 15 + .../site-packages/pip/_vendor/idna/core.py | 437 + .../pip/_vendor/idna/idnadata.py | 4243 ++++++++ .../pip/_vendor/idna/intranges.py | 57 + .../pip/_vendor/idna/package_data.py | 1 + .../site-packages/pip/_vendor/idna/py.typed | 0 .../pip/_vendor/idna/uts46data.py | 8681 +++++++++++++++++ .../pip/_vendor/msgpack/__init__.py | 55 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1743 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 2029 bytes .../msgpack/__pycache__/ext.cpython-312.pyc | Bin 0 -> 8297 bytes .../__pycache__/fallback.cpython-312.pyc | Bin 0 -> 41494 bytes .../pip/_vendor/msgpack/exceptions.py | 48 + .../site-packages/pip/_vendor/msgpack/ext.py | 170 + .../pip/_vendor/msgpack/fallback.py | 929 ++ .../pip/_vendor/packaging/__init__.py | 15 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 562 bytes .../__pycache__/_elffile.cpython-312.pyc | Bin 0 -> 5027 bytes .../__pycache__/_manylinux.cpython-312.pyc | Bin 0 -> 9715 bytes .../__pycache__/_musllinux.cpython-312.pyc | Bin 0 -> 4558 bytes .../__pycache__/_parser.cpython-312.pyc | Bin 0 -> 13989 bytes .../__pycache__/_structures.cpython-312.pyc | Bin 0 -> 3245 bytes .../__pycache__/_tokenizer.cpython-312.pyc | Bin 0 -> 7919 bytes .../__pycache__/markers.cpython-312.pyc | Bin 0 -> 11378 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 0 -> 27216 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 0 -> 4414 bytes .../__pycache__/specifiers.cpython-312.pyc | Bin 0 -> 39046 bytes .../__pycache__/tags.cpython-312.pyc | Bin 0 -> 23009 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 6639 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 20490 bytes .../pip/_vendor/packaging/_elffile.py | 110 + .../pip/_vendor/packaging/_manylinux.py | 263 + .../pip/_vendor/packaging/_musllinux.py | 85 + .../pip/_vendor/packaging/_parser.py | 354 + .../pip/_vendor/packaging/_structures.py | 61 + .../pip/_vendor/packaging/_tokenizer.py | 194 + .../_vendor/packaging/licenses/__init__.py | 145 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4124 bytes .../__pycache__/_spdx.cpython-312.pyc | Bin 0 -> 47368 bytes .../pip/_vendor/packaging/licenses/_spdx.py | 759 ++ .../pip/_vendor/packaging/markers.py | 331 + .../pip/_vendor/packaging/metadata.py | 863 ++ .../pip/_vendor/packaging/py.typed | 0 .../pip/_vendor/packaging/requirements.py | 91 + .../pip/_vendor/packaging/specifiers.py | 1020 ++ .../pip/_vendor/packaging/tags.py | 617 ++ .../pip/_vendor/packaging/utils.py | 163 + .../pip/_vendor/packaging/version.py | 582 ++ .../pip/_vendor/pkg_resources/__init__.py | 3676 +++++++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 161265 bytes .../pip/_vendor/platformdirs/__init__.py | 631 ++ .../pip/_vendor/platformdirs/__main__.py | 55 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 19851 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 1952 bytes .../__pycache__/android.cpython-312.pyc | Bin 0 -> 10701 bytes .../__pycache__/api.cpython-312.pyc | Bin 0 -> 13308 bytes .../__pycache__/macos.cpython-312.pyc | Bin 0 -> 8828 bytes .../__pycache__/unix.cpython-312.pyc | Bin 0 -> 14694 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 601 bytes .../__pycache__/windows.cpython-312.pyc | Bin 0 -> 13678 bytes .../pip/_vendor/platformdirs/android.py | 249 + .../pip/_vendor/platformdirs/api.py | 298 + .../pip/_vendor/platformdirs/macos.py | 144 + .../pip/_vendor/platformdirs/py.typed | 0 .../pip/_vendor/platformdirs/unix.py | 269 + .../pip/_vendor/platformdirs/version.py | 16 + .../pip/_vendor/platformdirs/windows.py | 272 + .../pip/_vendor/pygments/__init__.py | 82 + .../pip/_vendor/pygments/__main__.py | 17 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3493 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 739 bytes .../__pycache__/cmdline.cpython-312.pyc | Bin 0 -> 26589 bytes .../__pycache__/console.cpython-312.pyc | Bin 0 -> 2633 bytes .../__pycache__/filter.cpython-312.pyc | Bin 0 -> 3226 bytes .../__pycache__/formatter.cpython-312.pyc | Bin 0 -> 4725 bytes .../__pycache__/lexer.cpython-312.pyc | Bin 0 -> 38366 bytes .../__pycache__/modeline.cpython-312.pyc | Bin 0 -> 1564 bytes .../__pycache__/plugin.cpython-312.pyc | Bin 0 -> 2613 bytes .../__pycache__/regexopt.cpython-312.pyc | Bin 0 -> 4082 bytes .../__pycache__/scanner.cpython-312.pyc | Bin 0 -> 4761 bytes .../__pycache__/sphinxext.cpython-312.pyc | Bin 0 -> 12103 bytes .../__pycache__/style.cpython-312.pyc | Bin 0 -> 6698 bytes .../__pycache__/token.cpython-312.pyc | Bin 0 -> 8194 bytes .../__pycache__/unistring.cpython-312.pyc | Bin 0 -> 32977 bytes .../pygments/__pycache__/util.cpython-312.pyc | Bin 0 -> 14074 bytes .../pip/_vendor/pygments/cmdline.py | 668 ++ .../pip/_vendor/pygments/console.py | 70 + .../pip/_vendor/pygments/filter.py | 70 + .../pip/_vendor/pygments/filters/__init__.py | 940 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 37916 bytes .../pip/_vendor/pygments/formatter.py | 129 + .../_vendor/pygments/formatters/__init__.py | 157 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 6907 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 4220 bytes .../__pycache__/bbcode.cpython-312.pyc | Bin 0 -> 4227 bytes .../__pycache__/groff.cpython-312.pyc | Bin 0 -> 7298 bytes .../__pycache__/html.cpython-312.pyc | Bin 0 -> 41031 bytes .../__pycache__/img.cpython-312.pyc | Bin 0 -> 28553 bytes .../__pycache__/irc.cpython-312.pyc | Bin 0 -> 6060 bytes .../__pycache__/latex.cpython-312.pyc | Bin 0 -> 20130 bytes .../__pycache__/other.cpython-312.pyc | Bin 0 -> 6882 bytes .../__pycache__/pangomarkup.cpython-312.pyc | Bin 0 -> 2963 bytes .../__pycache__/rtf.cpython-312.pyc | Bin 0 -> 13778 bytes .../__pycache__/svg.cpython-312.pyc | Bin 0 -> 9144 bytes .../__pycache__/terminal.cpython-312.pyc | Bin 0 -> 5824 bytes .../__pycache__/terminal256.cpython-312.pyc | Bin 0 -> 15123 bytes .../_vendor/pygments/formatters/_mapping.py | 23 + .../pip/_vendor/pygments/formatters/bbcode.py | 108 + .../pip/_vendor/pygments/formatters/groff.py | 170 + .../pip/_vendor/pygments/formatters/html.py | 987 ++ .../pip/_vendor/pygments/formatters/img.py | 685 ++ .../pip/_vendor/pygments/formatters/irc.py | 154 + .../pip/_vendor/pygments/formatters/latex.py | 518 + .../pip/_vendor/pygments/formatters/other.py | 160 + .../pygments/formatters/pangomarkup.py | 83 + .../pip/_vendor/pygments/formatters/rtf.py | 349 + .../pip/_vendor/pygments/formatters/svg.py | 185 + .../_vendor/pygments/formatters/terminal.py | 127 + .../pygments/formatters/terminal256.py | 338 + .../pip/_vendor/pygments/lexer.py | 963 ++ .../pip/_vendor/pygments/lexers/__init__.py | 362 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 14626 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 68268 bytes .../lexers/__pycache__/python.cpython-312.pyc | Bin 0 -> 42972 bytes .../pip/_vendor/pygments/lexers/_mapping.py | 589 ++ .../pip/_vendor/pygments/lexers/python.py | 1198 +++ .../pip/_vendor/pygments/modeline.py | 43 + .../pip/_vendor/pygments/plugin.py | 72 + .../pip/_vendor/pygments/regexopt.py | 91 + .../pip/_vendor/pygments/scanner.py | 104 + .../pip/_vendor/pygments/sphinxext.py | 247 + .../pip/_vendor/pygments/style.py | 203 + .../pip/_vendor/pygments/styles/__init__.py | 61 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2669 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 3653 bytes .../pip/_vendor/pygments/styles/_mapping.py | 54 + .../pip/_vendor/pygments/token.py | 214 + .../pip/_vendor/pygments/unistring.py | 153 + .../pip/_vendor/pygments/util.py | 324 + .../pip/_vendor/pyproject_hooks/__init__.py | 31 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 752 bytes .../__pycache__/_impl.cpython-312.pyc | Bin 0 -> 18051 bytes .../pip/_vendor/pyproject_hooks/_impl.py | 410 + .../pyproject_hooks/_in_process/__init__.py | 21 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1081 bytes .../__pycache__/_in_process.cpython-312.pyc | Bin 0 -> 15319 bytes .../_in_process/_in_process.py | 389 + .../pip/_vendor/pyproject_hooks/py.typed | 0 .../pip/_vendor/requests/__init__.py | 179 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 5258 bytes .../__pycache__/__version__.cpython-312.pyc | Bin 0 -> 589 bytes .../_internal_utils.cpython-312.pyc | Bin 0 -> 2029 bytes .../__pycache__/adapters.cpython-312.pyc | Bin 0 -> 28436 bytes .../requests/__pycache__/api.cpython-312.pyc | Bin 0 -> 7196 bytes .../requests/__pycache__/auth.cpython-312.pyc | Bin 0 -> 13926 bytes .../__pycache__/certs.cpython-312.pyc | Bin 0 -> 683 bytes .../__pycache__/compat.cpython-312.pyc | Bin 0 -> 1682 bytes .../__pycache__/cookies.cpython-312.pyc | Bin 0 -> 25203 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 7603 bytes .../requests/__pycache__/help.cpython-312.pyc | Bin 0 -> 4233 bytes .../__pycache__/hooks.cpython-312.pyc | Bin 0 -> 1056 bytes .../__pycache__/models.cpython-312.pyc | Bin 0 -> 35433 bytes .../__pycache__/packages.cpython-312.pyc | Bin 0 -> 1271 bytes .../__pycache__/sessions.cpython-312.pyc | Bin 0 -> 27851 bytes .../__pycache__/status_codes.cpython-312.pyc | Bin 0 -> 6028 bytes .../__pycache__/structures.cpython-312.pyc | Bin 0 -> 5628 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 36371 bytes .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 50 + .../pip/_vendor/requests/adapters.py | 719 ++ .../site-packages/pip/_vendor/requests/api.py | 157 + .../pip/_vendor/requests/auth.py | 314 + .../pip/_vendor/requests/certs.py | 17 + .../pip/_vendor/requests/compat.py | 78 + .../pip/_vendor/requests/cookies.py | 561 ++ .../pip/_vendor/requests/exceptions.py | 151 + .../pip/_vendor/requests/help.py | 127 + .../pip/_vendor/requests/hooks.py | 33 + .../pip/_vendor/requests/models.py | 1037 ++ .../pip/_vendor/requests/packages.py | 25 + .../pip/_vendor/requests/sessions.py | 831 ++ .../pip/_vendor/requests/status_codes.py | 128 + .../pip/_vendor/requests/structures.py | 99 + .../pip/_vendor/requests/utils.py | 1096 +++ .../pip/_vendor/resolvelib/__init__.py | 26 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 639 bytes .../__pycache__/providers.cpython-312.pyc | Bin 0 -> 6856 bytes .../__pycache__/reporters.cpython-312.pyc | Bin 0 -> 2659 bytes .../__pycache__/resolvers.cpython-312.pyc | Bin 0 -> 25885 bytes .../__pycache__/structs.cpython-312.pyc | Bin 0 -> 10505 bytes .../pip/_vendor/resolvelib/compat/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 205 bytes .../collections_abc.cpython-312.pyc | Bin 0 -> 425 bytes .../resolvelib/compat/collections_abc.py | 6 + .../pip/_vendor/resolvelib/providers.py | 133 + .../pip/_vendor/resolvelib/py.typed | 0 .../pip/_vendor/resolvelib/reporters.py | 43 + .../pip/_vendor/resolvelib/resolvers.py | 547 ++ .../pip/_vendor/resolvelib/structs.py | 170 + .../pip/_vendor/rich/__init__.py | 177 + .../pip/_vendor/rich/__main__.py | 273 + .../rich/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 7020 bytes .../rich/__pycache__/__main__.cpython-312.pyc | Bin 0 -> 10297 bytes .../__pycache__/_cell_widths.cpython-312.pyc | Bin 0 -> 7877 bytes .../__pycache__/_emoji_codes.cpython-312.pyc | Bin 0 -> 205981 bytes .../_emoji_replace.cpython-312.pyc | Bin 0 -> 1734 bytes .../_export_format.cpython-312.pyc | Bin 0 -> 2354 bytes .../__pycache__/_extension.cpython-312.pyc | Bin 0 -> 542 bytes .../rich/__pycache__/_fileno.cpython-312.pyc | Bin 0 -> 860 bytes .../rich/__pycache__/_inspect.cpython-312.pyc | Bin 0 -> 12027 bytes .../__pycache__/_log_render.cpython-312.pyc | Bin 0 -> 4152 bytes .../rich/__pycache__/_loop.cpython-312.pyc | Bin 0 -> 1875 bytes .../__pycache__/_null_file.cpython-312.pyc | Bin 0 -> 3634 bytes .../__pycache__/_palettes.cpython-312.pyc | Bin 0 -> 5165 bytes .../rich/__pycache__/_pick.cpython-312.pyc | Bin 0 -> 726 bytes .../rich/__pycache__/_ratio.cpython-312.pyc | Bin 0 -> 6575 bytes .../__pycache__/_spinners.cpython-312.pyc | Bin 0 -> 13184 bytes .../rich/__pycache__/_stack.cpython-312.pyc | Bin 0 -> 970 bytes .../rich/__pycache__/_timer.cpython-312.pyc | Bin 0 -> 870 bytes .../_win32_console.cpython-312.pyc | Bin 0 -> 28816 bytes .../rich/__pycache__/_windows.cpython-312.pyc | Bin 0 -> 2495 bytes .../_windows_renderer.cpython-312.pyc | Bin 0 -> 3568 bytes .../rich/__pycache__/_wrap.cpython-312.pyc | Bin 0 -> 3331 bytes .../rich/__pycache__/abc.cpython-312.pyc | Bin 0 -> 1613 bytes .../rich/__pycache__/align.cpython-312.pyc | Bin 0 -> 12396 bytes .../rich/__pycache__/ansi.cpython-312.pyc | Bin 0 -> 9086 bytes .../rich/__pycache__/bar.cpython-312.pyc | Bin 0 -> 4277 bytes .../rich/__pycache__/box.cpython-312.pyc | Bin 0 -> 11843 bytes .../rich/__pycache__/cells.cpython-312.pyc | Bin 0 -> 5565 bytes .../rich/__pycache__/color.cpython-312.pyc | Bin 0 -> 26557 bytes .../__pycache__/color_triplet.cpython-312.pyc | Bin 0 -> 1706 bytes .../rich/__pycache__/columns.cpython-312.pyc | Bin 0 -> 8589 bytes .../rich/__pycache__/console.cpython-312.pyc | Bin 0 -> 114550 bytes .../__pycache__/constrain.cpython-312.pyc | Bin 0 -> 2263 bytes .../__pycache__/containers.cpython-312.pyc | Bin 0 -> 9215 bytes .../rich/__pycache__/control.cpython-312.pyc | Bin 0 -> 10946 bytes .../default_styles.cpython-312.pyc | Bin 0 -> 10443 bytes .../rich/__pycache__/diagnose.cpython-312.pyc | Bin 0 -> 1493 bytes .../rich/__pycache__/emoji.cpython-312.pyc | Bin 0 -> 4217 bytes .../rich/__pycache__/errors.cpython-312.pyc | Bin 0 -> 1850 bytes .../__pycache__/file_proxy.cpython-312.pyc | Bin 0 -> 3576 bytes .../rich/__pycache__/filesize.cpython-312.pyc | Bin 0 -> 3052 bytes .../__pycache__/highlighter.cpython-312.pyc | Bin 0 -> 9893 bytes .../rich/__pycache__/json.cpython-312.pyc | Bin 0 -> 6040 bytes .../rich/__pycache__/jupyter.cpython-312.pyc | Bin 0 -> 5213 bytes .../rich/__pycache__/layout.cpython-312.pyc | Bin 0 -> 20163 bytes .../rich/__pycache__/live.cpython-312.pyc | Bin 0 -> 19013 bytes .../__pycache__/live_render.cpython-312.pyc | Bin 0 -> 4894 bytes .../rich/__pycache__/logging.cpython-312.pyc | Bin 0 -> 14069 bytes .../rich/__pycache__/markup.cpython-312.pyc | Bin 0 -> 9572 bytes .../rich/__pycache__/measure.cpython-312.pyc | Bin 0 -> 6383 bytes .../rich/__pycache__/padding.cpython-312.pyc | Bin 0 -> 6938 bytes .../rich/__pycache__/pager.cpython-312.pyc | Bin 0 -> 1816 bytes .../rich/__pycache__/palette.cpython-312.pyc | Bin 0 -> 5302 bytes .../rich/__pycache__/panel.cpython-312.pyc | Bin 0 -> 12775 bytes .../rich/__pycache__/pretty.cpython-312.pyc | Bin 0 -> 40616 bytes .../rich/__pycache__/progress.cpython-312.pyc | Bin 0 -> 75739 bytes .../__pycache__/progress_bar.cpython-312.pyc | Bin 0 -> 10382 bytes .../rich/__pycache__/prompt.cpython-312.pyc | Bin 0 -> 16010 bytes .../rich/__pycache__/protocol.cpython-312.pyc | Bin 0 -> 1797 bytes .../rich/__pycache__/region.cpython-312.pyc | Bin 0 -> 572 bytes .../rich/__pycache__/repr.cpython-312.pyc | Bin 0 -> 6618 bytes .../rich/__pycache__/rule.cpython-312.pyc | Bin 0 -> 6573 bytes .../rich/__pycache__/scope.cpython-312.pyc | Bin 0 -> 3830 bytes .../rich/__pycache__/screen.cpython-312.pyc | Bin 0 -> 2484 bytes .../rich/__pycache__/segment.cpython-312.pyc | Bin 0 -> 28542 bytes .../rich/__pycache__/spinner.cpython-312.pyc | Bin 0 -> 6095 bytes .../rich/__pycache__/status.cpython-312.pyc | Bin 0 -> 6066 bytes .../rich/__pycache__/style.cpython-312.pyc | Bin 0 -> 33481 bytes .../rich/__pycache__/styled.cpython-312.pyc | Bin 0 -> 2144 bytes .../rich/__pycache__/syntax.cpython-312.pyc | Bin 0 -> 40189 bytes .../rich/__pycache__/table.cpython-312.pyc | Bin 0 -> 43888 bytes .../terminal_theme.cpython-312.pyc | Bin 0 -> 3353 bytes .../rich/__pycache__/text.cpython-312.pyc | Bin 0 -> 61215 bytes .../rich/__pycache__/theme.cpython-312.pyc | Bin 0 -> 6332 bytes .../rich/__pycache__/themes.cpython-312.pyc | Bin 0 -> 319 bytes .../__pycache__/traceback.cpython-312.pyc | Bin 0 -> 32897 bytes .../rich/__pycache__/tree.cpython-312.pyc | Bin 0 -> 11798 bytes .../pip/_vendor/rich/_cell_widths.py | 454 + .../pip/_vendor/rich/_emoji_codes.py | 3610 +++++++ .../pip/_vendor/rich/_emoji_replace.py | 32 + .../pip/_vendor/rich/_export_format.py | 76 + .../pip/_vendor/rich/_extension.py | 10 + .../site-packages/pip/_vendor/rich/_fileno.py | 24 + .../pip/_vendor/rich/_inspect.py | 268 + .../pip/_vendor/rich/_log_render.py | 94 + .../site-packages/pip/_vendor/rich/_loop.py | 43 + .../pip/_vendor/rich/_null_file.py | 69 + .../pip/_vendor/rich/_palettes.py | 309 + .../site-packages/pip/_vendor/rich/_pick.py | 17 + .../site-packages/pip/_vendor/rich/_ratio.py | 159 + .../pip/_vendor/rich/_spinners.py | 482 + .../site-packages/pip/_vendor/rich/_stack.py | 16 + .../site-packages/pip/_vendor/rich/_timer.py | 19 + .../pip/_vendor/rich/_win32_console.py | 661 ++ .../pip/_vendor/rich/_windows.py | 71 + .../pip/_vendor/rich/_windows_renderer.py | 56 + .../site-packages/pip/_vendor/rich/_wrap.py | 93 + .../site-packages/pip/_vendor/rich/abc.py | 33 + .../site-packages/pip/_vendor/rich/align.py | 312 + .../site-packages/pip/_vendor/rich/ansi.py | 241 + .../site-packages/pip/_vendor/rich/bar.py | 93 + .../site-packages/pip/_vendor/rich/box.py | 480 + .../site-packages/pip/_vendor/rich/cells.py | 174 + .../site-packages/pip/_vendor/rich/color.py | 621 ++ .../pip/_vendor/rich/color_triplet.py | 38 + .../site-packages/pip/_vendor/rich/columns.py | 187 + .../site-packages/pip/_vendor/rich/console.py | 2661 +++++ .../pip/_vendor/rich/constrain.py | 37 + .../pip/_vendor/rich/containers.py | 167 + .../site-packages/pip/_vendor/rich/control.py | 225 + .../pip/_vendor/rich/default_styles.py | 191 + .../pip/_vendor/rich/diagnose.py | 37 + .../site-packages/pip/_vendor/rich/emoji.py | 96 + .../site-packages/pip/_vendor/rich/errors.py | 34 + .../pip/_vendor/rich/file_proxy.py | 57 + .../pip/_vendor/rich/filesize.py | 88 + .../pip/_vendor/rich/highlighter.py | 232 + .../site-packages/pip/_vendor/rich/json.py | 139 + .../site-packages/pip/_vendor/rich/jupyter.py | 101 + .../site-packages/pip/_vendor/rich/layout.py | 442 + .../site-packages/pip/_vendor/rich/live.py | 375 + .../pip/_vendor/rich/live_render.py | 112 + .../site-packages/pip/_vendor/rich/logging.py | 297 + .../site-packages/pip/_vendor/rich/markup.py | 251 + .../site-packages/pip/_vendor/rich/measure.py | 151 + .../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 | 318 + .../site-packages/pip/_vendor/rich/pretty.py | 1016 ++ .../pip/_vendor/rich/progress.py | 1715 ++++ .../pip/_vendor/rich/progress_bar.py | 223 + .../site-packages/pip/_vendor/rich/prompt.py | 400 + .../pip/_vendor/rich/protocol.py | 42 + .../site-packages/pip/_vendor/rich/py.typed | 0 .../site-packages/pip/_vendor/rich/region.py | 10 + .../site-packages/pip/_vendor/rich/repr.py | 149 + .../site-packages/pip/_vendor/rich/rule.py | 130 + .../site-packages/pip/_vendor/rich/scope.py | 86 + .../site-packages/pip/_vendor/rich/screen.py | 54 + .../site-packages/pip/_vendor/rich/segment.py | 752 ++ .../site-packages/pip/_vendor/rich/spinner.py | 138 + .../site-packages/pip/_vendor/rich/status.py | 131 + .../site-packages/pip/_vendor/rich/style.py | 796 ++ .../site-packages/pip/_vendor/rich/styled.py | 42 + .../site-packages/pip/_vendor/rich/syntax.py | 966 ++ .../site-packages/pip/_vendor/rich/table.py | 1007 ++ .../pip/_vendor/rich/terminal_theme.py | 153 + .../site-packages/pip/_vendor/rich/text.py | 1361 +++ .../site-packages/pip/_vendor/rich/theme.py | 115 + .../site-packages/pip/_vendor/rich/themes.py | 5 + .../pip/_vendor/rich/traceback.py | 797 ++ .../site-packages/pip/_vendor/rich/tree.py | 257 + .../pip/_vendor/tomli/__init__.py | 8 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 343 bytes .../tomli/__pycache__/_parser.cpython-312.pyc | Bin 0 -> 29357 bytes .../tomli/__pycache__/_re.cpython-312.pyc | Bin 0 -> 4044 bytes .../tomli/__pycache__/_types.cpython-312.pyc | Bin 0 -> 371 bytes .../pip/_vendor/tomli/_parser.py | 770 ++ .../site-packages/pip/_vendor/tomli/_re.py | 112 + .../site-packages/pip/_vendor/tomli/_types.py | 10 + .../site-packages/pip/_vendor/tomli/py.typed | 1 + .../pip/_vendor/truststore/__init__.py | 36 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1349 bytes .../__pycache__/_api.cpython-312.pyc | Bin 0 -> 16778 bytes .../__pycache__/_macos.cpython-312.pyc | Bin 0 -> 18992 bytes .../__pycache__/_openssl.cpython-312.pyc | Bin 0 -> 2210 bytes .../_ssl_constants.cpython-312.pyc | Bin 0 -> 1104 bytes .../__pycache__/_windows.cpython-312.pyc | Bin 0 -> 15770 bytes .../pip/_vendor/truststore/_api.py | 316 + .../pip/_vendor/truststore/_macos.py | 571 ++ .../pip/_vendor/truststore/_openssl.py | 66 + .../pip/_vendor/truststore/_ssl_constants.py | 31 + .../pip/_vendor/truststore/_windows.py | 567 ++ .../pip/_vendor/truststore/py.typed | 0 .../pip/_vendor/typing_extensions.py | 3641 +++++++ .../pip/_vendor/urllib3/__init__.py | 102 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3410 bytes .../__pycache__/_collections.cpython-312.pyc | Bin 0 -> 16369 bytes .../__pycache__/_version.cpython-312.pyc | Bin 0 -> 223 bytes .../__pycache__/connection.cpython-312.pyc | Bin 0 -> 20408 bytes .../connectionpool.cpython-312.pyc | Bin 0 -> 36543 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 13498 bytes .../__pycache__/fields.cpython-312.pyc | Bin 0 -> 10407 bytes .../__pycache__/filepost.cpython-312.pyc | Bin 0 -> 4017 bytes .../__pycache__/poolmanager.cpython-312.pyc | Bin 0 -> 20434 bytes .../__pycache__/request.cpython-312.pyc | Bin 0 -> 7299 bytes .../__pycache__/response.cpython-312.pyc | Bin 0 -> 33948 bytes .../pip/_vendor/urllib3/_collections.py | 355 + .../pip/_vendor/urllib3/_version.py | 2 + .../pip/_vendor/urllib3/connection.py | 572 ++ .../pip/_vendor/urllib3/connectionpool.py | 1140 +++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 203 bytes .../_appengine_environ.cpython-312.pyc | Bin 0 -> 1853 bytes .../__pycache__/appengine.cpython-312.pyc | Bin 0 -> 11569 bytes .../__pycache__/ntlmpool.cpython-312.pyc | Bin 0 -> 5719 bytes .../__pycache__/pyopenssl.cpython-312.pyc | Bin 0 -> 24453 bytes .../securetransport.cpython-312.pyc | Bin 0 -> 35506 bytes .../contrib/__pycache__/socks.cpython-312.pyc | Bin 0 -> 7516 bytes .../urllib3/contrib/_appengine_environ.py | 36 + .../contrib/_securetransport/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 220 bytes .../__pycache__/bindings.cpython-312.pyc | Bin 0 -> 17432 bytes .../__pycache__/low_level.cpython-312.pyc | Bin 0 -> 14768 bytes .../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 | 518 + .../urllib3/contrib/securetransport.py | 920 ++ .../pip/_vendor/urllib3/contrib/socks.py | 216 + .../pip/_vendor/urllib3/exceptions.py | 323 + .../pip/_vendor/urllib3/fields.py | 274 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 204 bytes .../packages/__pycache__/six.cpython-312.pyc | Bin 0 -> 41260 bytes .../urllib3/packages/backports/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 214 bytes .../__pycache__/makefile.cpython-312.pyc | Bin 0 -> 1830 bytes .../weakref_finalize.cpython-312.pyc | Bin 0 -> 7341 bytes .../urllib3/packages/backports/makefile.py | 51 + .../packages/backports/weakref_finalize.py | 155 + .../pip/_vendor/urllib3/packages/six.py | 1076 ++ .../pip/_vendor/urllib3/poolmanager.py | 540 + .../pip/_vendor/urllib3/request.py | 191 + .../pip/_vendor/urllib3/response.py | 879 ++ .../pip/_vendor/urllib3/util/__init__.py | 49 + .../util/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1151 bytes .../__pycache__/connection.cpython-312.pyc | Bin 0 -> 4752 bytes .../util/__pycache__/proxy.cpython-312.pyc | Bin 0 -> 1557 bytes .../util/__pycache__/queue.cpython-312.pyc | Bin 0 -> 1357 bytes .../util/__pycache__/request.cpython-312.pyc | Bin 0 -> 4188 bytes .../util/__pycache__/response.cpython-312.pyc | Bin 0 -> 2997 bytes .../util/__pycache__/retry.cpython-312.pyc | Bin 0 -> 21727 bytes .../util/__pycache__/ssl_.cpython-312.pyc | Bin 0 -> 15369 bytes .../ssl_match_hostname.cpython-312.pyc | Bin 0 -> 5056 bytes .../__pycache__/ssltransport.cpython-312.pyc | Bin 0 -> 10758 bytes .../util/__pycache__/timeout.cpython-312.pyc | Bin 0 -> 11144 bytes .../util/__pycache__/url.cpython-312.pyc | Bin 0 -> 15790 bytes .../util/__pycache__/wait.cpython-312.pyc | Bin 0 -> 4408 bytes .../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 | 137 + .../pip/_vendor/urllib3/util/response.py | 107 + .../pip/_vendor/urllib3/util/retry.py | 622 ++ .../pip/_vendor/urllib3/util/ssl_.py | 504 + .../urllib3/util/ssl_match_hostname.py | 159 + .../pip/_vendor/urllib3/util/ssltransport.py | 221 + .../pip/_vendor/urllib3/util/timeout.py | 271 + .../pip/_vendor/urllib3/util/url.py | 435 + .../pip/_vendor/urllib3/util/wait.py | 152 + .../site-packages/pip/_vendor/vendor.txt | 18 + .../lib/python3.12/site-packages/pip/py.typed | 4 + .../python_dotenv-1.0.0.dist-info/INSTALLER | 1 + .../python_dotenv-1.0.0.dist-info/LICENSE | 27 + .../python_dotenv-1.0.0.dist-info/METADATA | 667 ++ .../python_dotenv-1.0.0.dist-info/RECORD | 26 + .../python_dotenv-1.0.0.dist-info/REQUESTED | 0 .../python_dotenv-1.0.0.dist-info/WHEEL | 5 + .../entry_points.txt | 2 + .../top_level.txt | 1 + .../site-packages/sqlalchemy/__init__.py | 285 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 9471 bytes .../__pycache__/events.cpython-312.pyc | Bin 0 -> 559 bytes .../__pycache__/exc.cpython-312.pyc | Bin 0 -> 31221 bytes .../__pycache__/inspection.cpython-312.pyc | Bin 0 -> 6612 bytes .../__pycache__/log.cpython-312.pyc | Bin 0 -> 11616 bytes .../__pycache__/schema.cpython-312.pyc | Bin 0 -> 2338 bytes .../__pycache__/types.cpython-312.pyc | Bin 0 -> 2286 bytes .../sqlalchemy/connectors/__init__.py | 18 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 598 bytes .../__pycache__/aioodbc.cpython-312.pyc | Bin 0 -> 7937 bytes .../__pycache__/asyncio.cpython-312.pyc | Bin 0 -> 11712 bytes .../__pycache__/pyodbc.cpython-312.pyc | Bin 0 -> 9378 bytes .../sqlalchemy/connectors/aioodbc.py | 187 + .../sqlalchemy/connectors/asyncio.py | 209 + .../sqlalchemy/connectors/pyodbc.py | 247 + .../sqlalchemy/cyextension/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 198 bytes .../sqlalchemy/cyextension/collections.pyx | 403 + .../sqlalchemy/cyextension/immutabledict.pxd | 2 + .../sqlalchemy/cyextension/immutabledict.pyx | 127 + .../sqlalchemy/cyextension/processors.pyx | 62 + .../sqlalchemy/cyextension/resultproxy.pyx | 96 + .../sqlalchemy/cyextension/util.pyx | 85 + .../sqlalchemy/dialects/__init__.py | 61 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1885 bytes .../__pycache__/_typing.cpython-312.pyc | Bin 0 -> 896 bytes .../sqlalchemy/dialects/_typing.py | 19 + .../sqlalchemy/dialects/mssql/__init__.py | 88 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1666 bytes .../mssql/__pycache__/aioodbc.cpython-312.pyc | Bin 0 -> 2408 bytes .../mssql/__pycache__/base.cpython-312.pyc | Bin 0 -> 153568 bytes .../information_schema.cpython-312.pyc | Bin 0 -> 8078 bytes .../mssql/__pycache__/json.cpython-312.pyc | Bin 0 -> 5288 bytes .../__pycache__/provision.cpython-312.pyc | Bin 0 -> 6885 bytes .../mssql/__pycache__/pymssql.cpython-312.pyc | Bin 0 -> 5913 bytes .../mssql/__pycache__/pyodbc.cpython-312.pyc | Bin 0 -> 30519 bytes .../sqlalchemy/dialects/mssql/aioodbc.py | 64 + .../sqlalchemy/dialects/mssql/base.py | 4048 ++++++++ .../dialects/mssql/information_schema.py | 253 + .../sqlalchemy/dialects/mssql/json.py | 127 + .../sqlalchemy/dialects/mssql/provision.py | 146 + .../sqlalchemy/dialects/mssql/pymssql.py | 125 + .../sqlalchemy/dialects/mssql/pyodbc.py | 746 ++ .../sqlalchemy/dialects/mysql/__init__.py | 101 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1959 bytes .../__pycache__/aiomysql.cpython-312.pyc | Bin 0 -> 16117 bytes .../mysql/__pycache__/asyncmy.cpython-312.pyc | Bin 0 -> 16576 bytes .../mysql/__pycache__/base.cpython-312.pyc | Bin 0 -> 136827 bytes .../mysql/__pycache__/cymysql.cpython-312.pyc | Bin 0 -> 3136 bytes .../mysql/__pycache__/dml.cpython-312.pyc | Bin 0 -> 8218 bytes .../__pycache__/enumerated.cpython-312.pyc | Bin 0 -> 10196 bytes .../__pycache__/expression.cpython-312.pyc | Bin 0 -> 5020 bytes .../mysql/__pycache__/json.cpython-312.pyc | Bin 0 -> 3450 bytes .../mysql/__pycache__/mariadb.cpython-312.pyc | Bin 0 -> 1071 bytes .../mariadbconnector.cpython-312.pyc | Bin 0 -> 11903 bytes .../mysqlconnector.cpython-312.pyc | Bin 0 -> 9090 bytes .../mysql/__pycache__/mysqldb.cpython-312.pyc | Bin 0 -> 12074 bytes .../__pycache__/provision.cpython-312.pyc | Bin 0 -> 4120 bytes .../mysql/__pycache__/pymysql.cpython-312.pyc | Bin 0 -> 5267 bytes .../mysql/__pycache__/pyodbc.cpython-312.pyc | Bin 0 -> 5263 bytes .../__pycache__/reflection.cpython-312.pyc | Bin 0 -> 23901 bytes .../reserved_words.cpython-312.pyc | Bin 0 -> 4371 bytes .../mysql/__pycache__/types.cpython-312.pyc | Bin 0 -> 30390 bytes .../sqlalchemy/dialects/mysql/aiomysql.py | 325 + .../sqlalchemy/dialects/mysql/asyncmy.py | 330 + .../sqlalchemy/dialects/mysql/base.py | 3443 +++++++ .../sqlalchemy/dialects/mysql/cymysql.py | 84 + .../sqlalchemy/dialects/mysql/dml.py | 219 + .../sqlalchemy/dialects/mysql/enumerated.py | 244 + .../sqlalchemy/dialects/mysql/expression.py | 140 + .../sqlalchemy/dialects/mysql/json.py | 81 + .../sqlalchemy/dialects/mysql/mariadb.py | 32 + .../dialects/mysql/mariadbconnector.py | 282 + .../dialects/mysql/mysqlconnector.py | 179 + .../sqlalchemy/dialects/mysql/mysqldb.py | 308 + .../sqlalchemy/dialects/mysql/provision.py | 101 + .../sqlalchemy/dialects/mysql/pymysql.py | 135 + .../sqlalchemy/dialects/mysql/pyodbc.py | 138 + .../sqlalchemy/dialects/mysql/reflection.py | 672 ++ .../dialects/mysql/reserved_words.py | 567 ++ .../sqlalchemy/dialects/mysql/types.py | 773 ++ .../sqlalchemy/dialects/oracle/__init__.py | 63 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1181 bytes .../oracle/__pycache__/base.cpython-312.pyc | Bin 0 -> 131890 bytes .../__pycache__/cx_oracle.cpython-312.pyc | Bin 0 -> 59456 bytes .../__pycache__/dictionary.cpython-312.pyc | Bin 0 -> 24602 bytes .../__pycache__/oracledb.cpython-312.pyc | Bin 0 -> 4394 bytes .../__pycache__/provision.cpython-312.pyc | Bin 0 -> 10769 bytes .../oracle/__pycache__/types.cpython-312.pyc | Bin 0 -> 12381 bytes .../sqlalchemy/dialects/oracle/base.py | 3232 ++++++ .../sqlalchemy/dialects/oracle/cx_oracle.py | 1484 +++ .../sqlalchemy/dialects/oracle/dictionary.py | 506 + .../sqlalchemy/dialects/oracle/oracledb.py | 109 + .../sqlalchemy/dialects/oracle/provision.py | 214 + .../sqlalchemy/dialects/oracle/types.py | 286 + .../dialects/postgresql/__init__.py | 163 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3334 bytes .../_psycopg_common.cpython-312.pyc | Bin 0 -> 7703 bytes .../__pycache__/array.cpython-312.pyc | Bin 0 -> 16587 bytes .../__pycache__/asyncpg.cpython-312.pyc | Bin 0 -> 56576 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 200727 bytes .../__pycache__/dml.cpython-312.pyc | Bin 0 -> 11615 bytes .../__pycache__/ext.cpython-312.pyc | Bin 0 -> 19256 bytes .../__pycache__/hstore.cpython-312.pyc | Bin 0 -> 15360 bytes .../__pycache__/json.cpython-312.pyc | Bin 0 -> 13554 bytes .../__pycache__/named_types.cpython-312.pyc | Bin 0 -> 22229 bytes .../__pycache__/operators.cpython-312.pyc | Bin 0 -> 2159 bytes .../__pycache__/pg8000.cpython-312.pyc | Bin 0 -> 30058 bytes .../__pycache__/pg_catalog.cpython-312.pyc | Bin 0 -> 10465 bytes .../__pycache__/provision.cpython-312.pyc | Bin 0 -> 7716 bytes .../__pycache__/psycopg.cpython-312.pyc | Bin 0 -> 35918 bytes .../__pycache__/psycopg2.cpython-312.pyc | Bin 0 -> 35671 bytes .../__pycache__/psycopg2cffi.cpython-312.pyc | Bin 0 -> 2166 bytes .../__pycache__/ranges.cpython-312.pyc | Bin 0 -> 31579 bytes .../__pycache__/types.cpython-312.pyc | Bin 0 -> 11048 bytes .../dialects/postgresql/_psycopg_common.py | 186 + .../sqlalchemy/dialects/postgresql/array.py | 426 + .../sqlalchemy/dialects/postgresql/asyncpg.py | 1261 +++ .../sqlalchemy/dialects/postgresql/base.py | 4890 ++++++++++ .../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 | 496 + .../dialects/postgresql/operators.py | 129 + .../sqlalchemy/dialects/postgresql/pg8000.py | 664 ++ .../dialects/postgresql/pg_catalog.py | 294 + .../dialects/postgresql/provision.py | 169 + .../sqlalchemy/dialects/postgresql/psycopg.py | 746 ++ .../dialects/postgresql/psycopg2.py | 876 ++ .../dialects/postgresql/psycopg2cffi.py | 61 + .../sqlalchemy/dialects/postgresql/ranges.py | 946 ++ .../sqlalchemy/dialects/postgresql/types.py | 312 + .../sqlalchemy/dialects/sqlite/__init__.py | 57 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1062 bytes .../__pycache__/aiosqlite.cpython-312.pyc | Bin 0 -> 17639 bytes .../sqlite/__pycache__/base.cpython-312.pyc | Bin 0 -> 100254 bytes .../sqlite/__pycache__/dml.cpython-312.pyc | Bin 0 -> 9243 bytes .../sqlite/__pycache__/json.cpython-312.pyc | Bin 0 -> 3779 bytes .../__pycache__/provision.cpython-312.pyc | Bin 0 -> 7280 bytes .../__pycache__/pysqlcipher.cpython-312.pyc | Bin 0 -> 6199 bytes .../__pycache__/pysqlite.cpython-312.pyc | Bin 0 -> 31256 bytes .../sqlalchemy/dialects/sqlite/aiosqlite.py | 396 + .../sqlalchemy/dialects/sqlite/base.py | 2782 ++++++ .../sqlalchemy/dialects/sqlite/dml.py | 240 + .../sqlalchemy/dialects/sqlite/json.py | 86 + .../sqlalchemy/dialects/sqlite/provision.py | 192 + .../sqlalchemy/dialects/sqlite/pysqlcipher.py | 155 + .../sqlalchemy/dialects/sqlite/pysqlite.py | 753 ++ .../dialects/type_migration_guidelines.txt | 145 + .../sqlalchemy/engine/__init__.py | 62 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2277 bytes .../_py_processors.cpython-312.pyc | Bin 0 -> 4500 bytes .../__pycache__/_py_row.cpython-312.pyc | Bin 0 -> 5767 bytes .../__pycache__/_py_util.cpython-312.pyc | Bin 0 -> 2179 bytes .../engine/__pycache__/base.cpython-312.pyc | Bin 0 -> 129569 bytes .../characteristics.cpython-312.pyc | Bin 0 -> 3744 bytes .../engine/__pycache__/create.cpython-312.pyc | Bin 0 -> 33649 bytes .../engine/__pycache__/cursor.cpython-312.pyc | Bin 0 -> 77423 bytes .../__pycache__/default.cpython-312.pyc | Bin 0 -> 86775 bytes .../engine/__pycache__/events.cpython-312.pyc | Bin 0 -> 39904 bytes .../__pycache__/interfaces.cpython-312.pyc | Bin 0 -> 99379 bytes .../engine/__pycache__/mock.cpython-312.pyc | Bin 0 -> 5691 bytes .../__pycache__/processors.cpython-312.pyc | Bin 0 -> 1291 bytes .../__pycache__/reflection.cpython-312.pyc | Bin 0 -> 79995 bytes .../engine/__pycache__/result.cpython-312.pyc | Bin 0 -> 91318 bytes .../engine/__pycache__/row.cpython-312.pyc | Bin 0 -> 17446 bytes .../__pycache__/strategies.cpython-312.pyc | Bin 0 -> 560 bytes .../engine/__pycache__/url.cpython-312.pyc | Bin 0 -> 34300 bytes .../engine/__pycache__/util.cpython-312.pyc | Bin 0 -> 6608 bytes .../sqlalchemy/engine/_py_processors.py | 136 + .../sqlalchemy/engine/_py_row.py | 122 + .../sqlalchemy/engine/_py_util.py | 68 + .../site-packages/sqlalchemy/engine/base.py | 3365 +++++++ .../sqlalchemy/engine/characteristics.py | 75 + .../site-packages/sqlalchemy/engine/create.py | 860 ++ .../site-packages/sqlalchemy/engine/cursor.py | 2149 ++++ .../sqlalchemy/engine/default.py | 2323 +++++ .../site-packages/sqlalchemy/engine/events.py | 951 ++ .../sqlalchemy/engine/interfaces.py | 3406 +++++++ .../site-packages/sqlalchemy/engine/mock.py | 131 + .../sqlalchemy/engine/processors.py | 61 + .../sqlalchemy/engine/reflection.py | 2089 ++++ .../site-packages/sqlalchemy/engine/result.py | 2405 +++++ .../site-packages/sqlalchemy/engine/row.py | 405 + .../sqlalchemy/engine/strategies.py | 19 + .../site-packages/sqlalchemy/engine/url.py | 913 ++ .../site-packages/sqlalchemy/engine/util.py | 166 + .../sqlalchemy/event/__init__.py | 25 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 840 bytes .../event/__pycache__/api.cpython-312.pyc | Bin 0 -> 9193 bytes .../event/__pycache__/attr.cpython-312.pyc | Bin 0 -> 29838 bytes .../event/__pycache__/base.cpython-312.pyc | Bin 0 -> 19568 bytes .../event/__pycache__/legacy.cpython-312.pyc | Bin 0 -> 9388 bytes .../__pycache__/registry.cpython-312.pyc | Bin 0 -> 12594 bytes .../site-packages/sqlalchemy/event/api.py | 225 + .../site-packages/sqlalchemy/event/attr.py | 641 ++ .../site-packages/sqlalchemy/event/base.py | 465 + .../site-packages/sqlalchemy/event/legacy.py | 246 + .../sqlalchemy/event/registry.py | 386 + .../site-packages/sqlalchemy/events.py | 17 + .../site-packages/sqlalchemy/exc.py | 833 ++ .../site-packages/sqlalchemy/ext/__init__.py | 11 + .../ext/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 353 bytes .../associationproxy.cpython-312.pyc | Bin 0 -> 86334 bytes .../ext/__pycache__/automap.cpython-312.pyc | Bin 0 -> 57240 bytes .../ext/__pycache__/baked.cpython-312.pyc | Bin 0 -> 23456 bytes .../ext/__pycache__/compiler.cpython-312.pyc | Bin 0 -> 20636 bytes .../horizontal_shard.cpython-312.pyc | Bin 0 -> 17694 bytes .../ext/__pycache__/hybrid.cpython-312.pyc | Bin 0 -> 59661 bytes .../ext/__pycache__/indexable.cpython-312.pyc | Bin 0 -> 12159 bytes .../instrumentation.cpython-312.pyc | Bin 0 -> 19875 bytes .../ext/__pycache__/mutable.cpython-312.pyc | Bin 0 -> 46039 bytes .../__pycache__/orderinglist.cpython-312.pyc | Bin 0 -> 17271 bytes .../__pycache__/serializer.cpython-312.pyc | Bin 0 -> 7655 bytes .../sqlalchemy/ext/associationproxy.py | 2028 ++++ .../sqlalchemy/ext/asyncio/__init__.py | 25 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 976 bytes .../asyncio/__pycache__/base.cpython-312.pyc | Bin 0 -> 11206 bytes .../__pycache__/engine.cpython-312.pyc | Bin 0 -> 56803 bytes .../asyncio/__pycache__/exc.cpython-312.pyc | Bin 0 -> 1036 bytes .../__pycache__/result.cpython-312.pyc | Bin 0 -> 37336 bytes .../__pycache__/scoping.cpython-312.pyc | Bin 0 -> 56926 bytes .../__pycache__/session.cpython-312.pyc | Bin 0 -> 71103 bytes .../sqlalchemy/ext/asyncio/base.py | 283 + .../sqlalchemy/ext/asyncio/engine.py | 1468 +++ .../sqlalchemy/ext/asyncio/exc.py | 21 + .../sqlalchemy/ext/asyncio/result.py | 976 ++ .../sqlalchemy/ext/asyncio/scoping.py | 1625 +++ .../sqlalchemy/ext/asyncio/session.py | 1940 ++++ .../site-packages/sqlalchemy/ext/automap.py | 1658 ++++ .../site-packages/sqlalchemy/ext/baked.py | 574 ++ .../site-packages/sqlalchemy/ext/compiler.py | 555 ++ .../sqlalchemy/ext/declarative/__init__.py | 65 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2012 bytes .../__pycache__/extensions.cpython-312.pyc | Bin 0 -> 21250 bytes .../sqlalchemy/ext/declarative/extensions.py | 548 ++ .../sqlalchemy/ext/horizontal_shard.py | 483 + .../site-packages/sqlalchemy/ext/hybrid.py | 1524 +++ .../site-packages/sqlalchemy/ext/indexable.py | 341 + .../sqlalchemy/ext/instrumentation.py | 452 + .../site-packages/sqlalchemy/ext/mutable.py | 1078 ++ .../sqlalchemy/ext/mypy/__init__.py | 0 .../mypy/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 195 bytes .../mypy/__pycache__/apply.cpython-312.pyc | Bin 0 -> 10464 bytes .../__pycache__/decl_class.cpython-312.pyc | Bin 0 -> 15819 bytes .../mypy/__pycache__/infer.cpython-312.pyc | Bin 0 -> 15627 bytes .../mypy/__pycache__/names.cpython-312.pyc | Bin 0 -> 10944 bytes .../mypy/__pycache__/plugin.cpython-312.pyc | Bin 0 -> 12514 bytes .../ext/mypy/__pycache__/util.cpython-312.pyc | Bin 0 -> 13114 bytes .../sqlalchemy/ext/mypy/apply.py | 318 + .../sqlalchemy/ext/mypy/decl_class.py | 515 + .../sqlalchemy/ext/mypy/infer.py | 590 ++ .../sqlalchemy/ext/mypy/names.py | 342 + .../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 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 463 bytes .../future/__pycache__/engine.cpython-312.pyc | Bin 0 -> 396 bytes .../site-packages/sqlalchemy/future/engine.py | 15 + .../site-packages/sqlalchemy/inspection.py | 181 + .../site-packages/sqlalchemy/log.py | 290 + .../site-packages/sqlalchemy/orm/__init__.py | 170 + .../orm/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 6344 bytes .../_orm_constructors.cpython-312.pyc | Bin 0 -> 101645 bytes .../orm/__pycache__/_typing.cpython-312.pyc | Bin 0 -> 6715 bytes .../__pycache__/attributes.cpython-312.pyc | Bin 0 -> 99783 bytes .../orm/__pycache__/base.cpython-312.pyc | Bin 0 -> 30165 bytes .../bulk_persistence.cpython-312.pyc | Bin 0 -> 63306 bytes .../__pycache__/clsregistry.cpython-312.pyc | Bin 0 -> 23796 bytes .../__pycache__/collections.cpython-312.pyc | Bin 0 -> 61704 bytes .../orm/__pycache__/context.cpython-312.pyc | Bin 0 -> 100289 bytes .../orm/__pycache__/decl_api.cpython-312.pyc | Bin 0 -> 66270 bytes .../orm/__pycache__/decl_base.cpython-312.pyc | Bin 0 -> 68046 bytes .../__pycache__/dependency.cpython-312.pyc | Bin 0 -> 43120 bytes .../descriptor_props.cpython-312.pyc | Bin 0 -> 48896 bytes .../orm/__pycache__/dynamic.cpython-312.pyc | Bin 0 -> 12909 bytes .../orm/__pycache__/evaluator.cpython-312.pyc | Bin 0 -> 16165 bytes .../orm/__pycache__/events.cpython-312.pyc | Bin 0 -> 136665 bytes .../orm/__pycache__/exc.cpython-312.pyc | Bin 0 -> 9846 bytes .../orm/__pycache__/identity.cpython-312.pyc | Bin 0 -> 12616 bytes .../instrumentation.cpython-312.pyc | Bin 0 -> 31200 bytes .../__pycache__/interfaces.cpython-312.pyc | Bin 0 -> 53645 bytes .../orm/__pycache__/loading.cpython-312.pyc | Bin 0 -> 46480 bytes .../mapped_collection.cpython-312.pyc | Bin 0 -> 21908 bytes .../orm/__pycache__/mapper.cpython-312.pyc | Bin 0 -> 168153 bytes .../__pycache__/path_registry.cpython-312.pyc | Bin 0 -> 31455 bytes .../__pycache__/persistence.cpython-312.pyc | Bin 0 -> 48130 bytes .../__pycache__/properties.cpython-312.pyc | Bin 0 -> 32854 bytes .../orm/__pycache__/query.cpython-312.pyc | Bin 0 -> 127527 bytes .../__pycache__/relationships.cpython-312.pyc | Bin 0 -> 128806 bytes .../orm/__pycache__/scoping.cpython-312.pyc | Bin 0 -> 83563 bytes .../orm/__pycache__/session.cpython-312.pyc | Bin 0 -> 200712 bytes .../orm/__pycache__/state.cpython-312.pyc | Bin 0 -> 45104 bytes .../__pycache__/state_changes.cpython-312.pyc | Bin 0 -> 7025 bytes .../__pycache__/strategies.cpython-312.pyc | Bin 0 -> 103011 bytes .../strategy_options.cpython-312.pyc | Bin 0 -> 86393 bytes .../orm/__pycache__/sync.cpython-312.pyc | Bin 0 -> 6530 bytes .../__pycache__/unitofwork.cpython-312.pyc | Bin 0 -> 34033 bytes .../orm/__pycache__/util.cpython-312.pyc | Bin 0 -> 85093 bytes .../orm/__pycache__/writeonly.cpython-312.pyc | Bin 0 -> 28807 bytes .../sqlalchemy/orm/_orm_constructors.py | 2483 +++++ .../site-packages/sqlalchemy/orm/_typing.py | 185 + .../sqlalchemy/orm/attributes.py | 2842 ++++++ .../site-packages/sqlalchemy/orm/base.py | 995 ++ .../sqlalchemy/orm/bulk_persistence.py | 2048 ++++ .../sqlalchemy/orm/clsregistry.py | 572 ++ .../sqlalchemy/orm/collections.py | 1619 +++ .../site-packages/sqlalchemy/orm/context.py | 3227 ++++++ .../site-packages/sqlalchemy/orm/decl_api.py | 1886 ++++ .../site-packages/sqlalchemy/orm/decl_base.py | 2155 ++++ .../sqlalchemy/orm/dependency.py | 1302 +++ .../sqlalchemy/orm/descriptor_props.py | 1074 ++ .../site-packages/sqlalchemy/orm/dynamic.py | 299 + .../site-packages/sqlalchemy/orm/evaluator.py | 368 + .../site-packages/sqlalchemy/orm/events.py | 3248 ++++++ .../site-packages/sqlalchemy/orm/exc.py | 227 + .../site-packages/sqlalchemy/orm/identity.py | 302 + .../sqlalchemy/orm/instrumentation.py | 756 ++ .../sqlalchemy/orm/interfaces.py | 1465 +++ .../site-packages/sqlalchemy/orm/loading.py | 1661 ++++ .../sqlalchemy/orm/mapped_collection.py | 562 ++ .../site-packages/sqlalchemy/orm/mapper.py | 4416 +++++++++ .../sqlalchemy/orm/path_registry.py | 819 ++ .../sqlalchemy/orm/persistence.py | 1755 ++++ .../sqlalchemy/orm/properties.py | 879 ++ .../site-packages/sqlalchemy/orm/query.py | 3414 +++++++ .../sqlalchemy/orm/relationships.py | 3466 +++++++ .../site-packages/sqlalchemy/orm/scoping.py | 2183 +++++ .../site-packages/sqlalchemy/orm/session.py | 5256 ++++++++++ .../site-packages/sqlalchemy/orm/state.py | 1138 +++ .../sqlalchemy/orm/state_changes.py | 198 + .../sqlalchemy/orm/strategies.py | 3338 +++++++ .../sqlalchemy/orm/strategy_options.py | 2528 +++++ .../site-packages/sqlalchemy/orm/sync.py | 163 + .../sqlalchemy/orm/unitofwork.py | 796 ++ .../site-packages/sqlalchemy/orm/util.py | 2407 +++++ .../site-packages/sqlalchemy/orm/writeonly.py | 681 ++ .../site-packages/sqlalchemy/pool/__init__.py | 44 + .../pool/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1514 bytes .../pool/__pycache__/base.cpython-312.pyc | Bin 0 -> 56227 bytes .../pool/__pycache__/events.cpython-312.pyc | Bin 0 -> 14269 bytes .../pool/__pycache__/impl.cpython-312.pyc | Bin 0 -> 24669 bytes .../site-packages/sqlalchemy/pool/base.py | 1521 +++ .../site-packages/sqlalchemy/pool/events.py | 370 + .../site-packages/sqlalchemy/pool/impl.py | 552 ++ .../site-packages/sqlalchemy/py.typed | 0 .../site-packages/sqlalchemy/schema.py | 70 + .../site-packages/sqlalchemy/sql/__init__.py | 145 + .../sql/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4687 bytes .../_dml_constructors.cpython-312.pyc | Bin 0 -> 4095 bytes .../_elements_constructors.cpython-312.pyc | Bin 0 -> 65667 bytes .../__pycache__/_orm_types.cpython-312.pyc | Bin 0 -> 627 bytes .../sql/__pycache__/_py_util.cpython-312.pyc | Bin 0 -> 2966 bytes .../_selectable_constructors.cpython-312.pyc | Bin 0 -> 21376 bytes .../sql/__pycache__/_typing.cpython-312.pyc | Bin 0 -> 13824 bytes .../__pycache__/annotation.cpython-312.pyc | Bin 0 -> 21322 bytes .../sql/__pycache__/base.cpython-312.pyc | Bin 0 -> 97485 bytes .../sql/__pycache__/cache_key.cpython-312.pyc | Bin 0 -> 35393 bytes .../sql/__pycache__/coercions.cpython-312.pyc | Bin 0 -> 48506 bytes .../sql/__pycache__/compiler.cpython-312.pyc | Bin 0 -> 268913 bytes .../sql/__pycache__/crud.cpython-312.pyc | Bin 0 -> 45455 bytes .../sql/__pycache__/ddl.cpython-312.pyc | Bin 0 -> 55972 bytes .../default_comparator.cpython-312.pyc | Bin 0 -> 19478 bytes .../sql/__pycache__/dml.cpython-312.pyc | Bin 0 -> 73437 bytes .../sql/__pycache__/elements.cpython-312.pyc | Bin 0 -> 203148 bytes .../sql/__pycache__/events.cpython-312.pyc | Bin 0 -> 19214 bytes .../__pycache__/expression.cpython-312.pyc | Bin 0 -> 5149 bytes .../sql/__pycache__/functions.cpython-312.pyc | Bin 0 -> 68607 bytes .../sql/__pycache__/lambdas.cpython-312.pyc | Bin 0 -> 54940 bytes .../sql/__pycache__/naming.cpython-312.pyc | Bin 0 -> 8464 bytes .../sql/__pycache__/operators.cpython-312.pyc | Bin 0 -> 88945 bytes .../sql/__pycache__/roles.cpython-312.pyc | Bin 0 -> 12252 bytes .../sql/__pycache__/schema.cpython-312.pyc | Bin 0 -> 243962 bytes .../__pycache__/selectable.cpython-312.pyc | Bin 0 -> 256055 bytes .../sql/__pycache__/sqltypes.cpython-312.pyc | Bin 0 -> 149170 bytes .../__pycache__/traversals.cpython-312.pyc | Bin 0 -> 42338 bytes .../sql/__pycache__/type_api.cpython-312.pyc | Bin 0 -> 85034 bytes .../sql/__pycache__/util.cpython-312.pyc | Bin 0 -> 54333 bytes .../sql/__pycache__/visitors.cpython-312.pyc | Bin 0 -> 35968 bytes .../sqlalchemy/sql/_dml_constructors.py | 140 + .../sqlalchemy/sql/_elements_constructors.py | 1850 ++++ .../sqlalchemy/sql/_orm_types.py | 20 + .../site-packages/sqlalchemy/sql/_py_util.py | 75 + .../sql/_selectable_constructors.py | 642 ++ .../site-packages/sqlalchemy/sql/_typing.py | 456 + .../sqlalchemy/sql/annotation.py | 594 ++ .../site-packages/sqlalchemy/sql/base.py | 2196 +++++ .../site-packages/sqlalchemy/sql/cache_key.py | 1029 ++ .../site-packages/sqlalchemy/sql/coercions.py | 1406 +++ .../site-packages/sqlalchemy/sql/compiler.py | 7655 +++++++++++++++ .../site-packages/sqlalchemy/sql/crud.py | 1671 ++++ .../site-packages/sqlalchemy/sql/ddl.py | 1377 +++ .../sqlalchemy/sql/default_comparator.py | 549 ++ .../site-packages/sqlalchemy/sql/dml.py | 1835 ++++ .../site-packages/sqlalchemy/sql/elements.py | 5405 ++++++++++ .../site-packages/sqlalchemy/sql/events.py | 455 + .../sqlalchemy/sql/expression.py | 162 + .../site-packages/sqlalchemy/sql/functions.py | 1828 ++++ .../site-packages/sqlalchemy/sql/lambdas.py | 1450 +++ .../site-packages/sqlalchemy/sql/naming.py | 212 + .../site-packages/sqlalchemy/sql/operators.py | 2596 +++++ .../site-packages/sqlalchemy/sql/roles.py | 325 + .../site-packages/sqlalchemy/sql/schema.py | 6118 ++++++++++++ .../sqlalchemy/sql/selectable.py | 6934 +++++++++++++ .../site-packages/sqlalchemy/sql/sqltypes.py | 3850 ++++++++ .../sqlalchemy/sql/traversals.py | 1021 ++ .../site-packages/sqlalchemy/sql/type_api.py | 2326 +++++ .../site-packages/sqlalchemy/sql/util.py | 1499 +++ .../site-packages/sqlalchemy/sql/visitors.py | 1181 +++ .../sqlalchemy/testing/__init__.py | 95 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3292 bytes .../__pycache__/assertions.cpython-312.pyc | Bin 0 -> 41761 bytes .../__pycache__/assertsql.cpython-312.pyc | Bin 0 -> 20200 bytes .../__pycache__/asyncio.cpython-312.pyc | Bin 0 -> 3852 bytes .../__pycache__/config.cpython-312.pyc | Bin 0 -> 17668 bytes .../__pycache__/engines.cpython-312.pyc | Bin 0 -> 21475 bytes .../__pycache__/entities.cpython-312.pyc | Bin 0 -> 4985 bytes .../__pycache__/exclusions.cpython-312.pyc | Bin 0 -> 21249 bytes .../__pycache__/pickleable.cpython-312.pyc | Bin 0 -> 6690 bytes .../__pycache__/profiling.cpython-312.pyc | Bin 0 -> 12986 bytes .../__pycache__/provision.cpython-312.pyc | Bin 0 -> 20939 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 0 -> 83942 bytes .../__pycache__/schema.cpython-312.pyc | Bin 0 -> 8962 bytes .../testing/__pycache__/util.cpython-312.pyc | Bin 0 -> 20887 bytes .../__pycache__/warnings.cpython-312.pyc | Bin 0 -> 2005 bytes .../sqlalchemy/testing/assertions.py | 989 ++ .../sqlalchemy/testing/assertsql.py | 516 + .../sqlalchemy/testing/asyncio.py | 130 + .../sqlalchemy/testing/config.py | 424 + .../sqlalchemy/testing/engines.py | 469 + .../sqlalchemy/testing/entities.py | 117 + .../sqlalchemy/testing/exclusions.py | 435 + .../sqlalchemy/testing/fixtures/__init__.py | 28 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 912 bytes .../fixtures/__pycache__/base.cpython-312.pyc | Bin 0 -> 13255 bytes .../fixtures/__pycache__/mypy.cpython-312.pyc | Bin 0 -> 12801 bytes .../fixtures/__pycache__/orm.cpython-312.pyc | Bin 0 -> 11451 bytes .../fixtures/__pycache__/sql.cpython-312.pyc | Bin 0 -> 22136 bytes .../sqlalchemy/testing/fixtures/base.py | 366 + .../sqlalchemy/testing/fixtures/mypy.py | 308 + .../sqlalchemy/testing/fixtures/orm.py | 227 + .../sqlalchemy/testing/fixtures/sql.py | 492 + .../sqlalchemy/testing/pickleable.py | 155 + .../sqlalchemy/testing/plugin/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 201 bytes .../__pycache__/bootstrap.cpython-312.pyc | Bin 0 -> 2152 bytes .../__pycache__/plugin_base.cpython-312.pyc | Bin 0 -> 27740 bytes .../__pycache__/pytestplugin.cpython-312.pyc | Bin 0 -> 32621 bytes .../sqlalchemy/testing/plugin/bootstrap.py | 45 + .../sqlalchemy/testing/plugin/plugin_base.py | 779 ++ .../sqlalchemy/testing/plugin/pytestplugin.py | 856 ++ .../sqlalchemy/testing/profiling.py | 324 + .../sqlalchemy/testing/provision.py | 486 + .../sqlalchemy/testing/requirements.py | 1765 ++++ .../sqlalchemy/testing/schema.py | 224 + .../sqlalchemy/testing/suite/__init__.py | 13 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 566 bytes .../__pycache__/test_cte.cpython-312.pyc | Bin 0 -> 9902 bytes .../__pycache__/test_ddl.cpython-312.pyc | Bin 0 -> 18751 bytes .../test_deprecations.cpython-312.pyc | Bin 0 -> 9009 bytes .../__pycache__/test_dialect.cpython-312.pyc | Bin 0 -> 34397 bytes .../__pycache__/test_insert.cpython-312.pyc | Bin 0 -> 25090 bytes .../test_reflection.cpython-312.pyc | Bin 0 -> 136931 bytes .../__pycache__/test_results.cpython-312.pyc | Bin 0 -> 23328 bytes .../__pycache__/test_rowcount.cpython-312.pyc | Bin 0 -> 10352 bytes .../__pycache__/test_select.cpython-312.pyc | Bin 0 -> 107780 bytes .../__pycache__/test_sequence.cpython-312.pyc | Bin 0 -> 14984 bytes .../__pycache__/test_types.cpython-312.pyc | Bin 0 -> 95554 bytes .../test_unicode_ddl.cpython-312.pyc | Bin 0 -> 7622 bytes .../test_update_delete.cpython-312.pyc | Bin 0 -> 7381 bytes .../sqlalchemy/testing/suite/test_cte.py | 205 + .../sqlalchemy/testing/suite/test_ddl.py | 383 + .../testing/suite/test_deprecations.py | 147 + .../sqlalchemy/testing/suite/test_dialect.py | 734 ++ .../sqlalchemy/testing/suite/test_insert.py | 614 ++ .../testing/suite/test_reflection.py | 3122 ++++++ .../sqlalchemy/testing/suite/test_results.py | 462 + .../sqlalchemy/testing/suite/test_rowcount.py | 252 + .../sqlalchemy/testing/suite/test_select.py | 1882 ++++ .../sqlalchemy/testing/suite/test_sequence.py | 311 + .../sqlalchemy/testing/suite/test_types.py | 2065 ++++ .../testing/suite/test_unicode_ddl.py | 183 + .../testing/suite/test_update_delete.py | 129 + .../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 + .../util/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 5634 bytes .../__pycache__/_collections.cpython-312.pyc | Bin 0 -> 31603 bytes .../_concurrency_py3k.cpython-312.pyc | Bin 0 -> 8990 bytes .../util/__pycache__/_has_cy.cpython-312.pyc | Bin 0 -> 1092 bytes .../_py_collections.cpython-312.pyc | Bin 0 -> 29226 bytes .../util/__pycache__/compat.cpython-312.pyc | Bin 0 -> 12995 bytes .../__pycache__/concurrency.cpython-312.pyc | Bin 0 -> 2076 bytes .../__pycache__/deprecations.cpython-312.pyc | Bin 0 -> 13604 bytes .../__pycache__/langhelpers.cpython-312.pyc | Bin 0 -> 84063 bytes .../__pycache__/preloaded.cpython-312.pyc | Bin 0 -> 5884 bytes .../util/__pycache__/queue.cpython-312.pyc | Bin 0 -> 14579 bytes .../__pycache__/tool_support.cpython-312.pyc | Bin 0 -> 8502 bytes .../__pycache__/topological.cpython-312.pyc | Bin 0 -> 3910 bytes .../util/__pycache__/typing.cpython-312.pyc | Bin 0 -> 18991 bytes .../sqlalchemy/util/_collections.py | 723 ++ .../sqlalchemy/util/_concurrency_py3k.py | 260 + .../site-packages/sqlalchemy/util/_has_cy.py | 39 + .../sqlalchemy/util/_py_collections.py | 539 + .../site-packages/sqlalchemy/util/compat.py | 320 + .../sqlalchemy/util/concurrency.py | 69 + .../sqlalchemy/util/deprecations.py | 401 + .../sqlalchemy/util/langhelpers.py | 2218 +++++ .../sqlalchemy/util/preloaded.py | 150 + .../site-packages/sqlalchemy/util/queue.py | 324 + .../sqlalchemy/util/tool_support.py | 198 + .../sqlalchemy/util/topological.py | 120 + .../site-packages/sqlalchemy/util/typing.py | 574 ++ .../INSTALLER | 1 + .../METADATA | 72 + .../typing_extensions-4.15.0.dist-info/RECORD | 7 + .../typing_extensions-4.15.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 279 + .../site-packages/typing_extensions.py | 4317 ++++++++ .../werkzeug-3.1.8.dist-info/INSTALLER | 1 + .../werkzeug-3.1.8.dist-info/METADATA | 109 + .../werkzeug-3.1.8.dist-info/RECORD | 116 + .../werkzeug-3.1.8.dist-info/WHEEL | 4 + .../licenses/LICENSE.txt | 28 + .../site-packages/werkzeug/__init__.py | 4 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 338 bytes .../__pycache__/_internal.cpython-312.pyc | Bin 0 -> 9754 bytes .../__pycache__/_reloader.cpython-312.pyc | Bin 0 -> 20864 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 34015 bytes .../__pycache__/formparser.cpython-312.pyc | Bin 0 -> 17019 bytes .../werkzeug/__pycache__/http.cpython-312.pyc | Bin 0 -> 50979 bytes .../__pycache__/local.cpython-312.pyc | Bin 0 -> 28462 bytes .../__pycache__/security.cpython-312.pyc | Bin 0 -> 8688 bytes .../__pycache__/serving.cpython-312.pyc | Bin 0 -> 46040 bytes .../werkzeug/__pycache__/test.cpython-312.pyc | Bin 0 -> 59810 bytes .../__pycache__/testapp.cpython-312.pyc | Bin 0 -> 8861 bytes .../werkzeug/__pycache__/urls.cpython-312.pyc | Bin 0 -> 8262 bytes .../__pycache__/user_agent.cpython-312.pyc | Bin 0 -> 2151 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 27688 bytes .../werkzeug/__pycache__/wsgi.cpython-312.pyc | Bin 0 -> 25963 bytes .../site-packages/werkzeug/_internal.py | 211 + .../site-packages/werkzeug/_reloader.py | 465 + .../werkzeug/datastructures/__init__.py | 64 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 2415 bytes .../__pycache__/accept.cpython-312.pyc | Bin 0 -> 15915 bytes .../__pycache__/auth.cpython-312.pyc | Bin 0 -> 14548 bytes .../__pycache__/cache_control.cpython-312.pyc | Bin 0 -> 12221 bytes .../__pycache__/csp.cpython-312.pyc | Bin 0 -> 6184 bytes .../__pycache__/etag.cpython-312.pyc | Bin 0 -> 5408 bytes .../__pycache__/file_storage.cpython-312.pyc | Bin 0 -> 8822 bytes .../__pycache__/headers.cpython-312.pyc | Bin 0 -> 30455 bytes .../__pycache__/mixins.cpython-312.pyc | Bin 0 -> 16398 bytes .../__pycache__/range.cpython-312.pyc | Bin 0 -> 10041 bytes .../__pycache__/structures.cpython-312.pyc | Bin 0 -> 58959 bytes .../werkzeug/datastructures/accept.py | 350 + .../werkzeug/datastructures/auth.py | 320 + .../werkzeug/datastructures/cache_control.py | 273 + .../werkzeug/datastructures/csp.py | 100 + .../werkzeug/datastructures/etag.py | 106 + .../werkzeug/datastructures/file_storage.py | 209 + .../werkzeug/datastructures/headers.py | 662 ++ .../werkzeug/datastructures/mixins.py | 317 + .../werkzeug/datastructures/range.py | 214 + .../werkzeug/datastructures/structures.py | 1239 +++ .../site-packages/werkzeug/debug/__init__.py | 574 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 23535 bytes .../debug/__pycache__/console.cpython-312.pyc | Bin 0 -> 11630 bytes .../debug/__pycache__/repr.cpython-312.pyc | Bin 0 -> 13772 bytes .../debug/__pycache__/tbtools.cpython-312.pyc | Bin 0 -> 17006 bytes .../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 | 344 + .../werkzeug/debug/shared/less.png | Bin 0 -> 191 bytes .../werkzeug/debug/shared/more.png | Bin 0 -> 200 bytes .../werkzeug/debug/shared/style.css | 150 + .../site-packages/werkzeug/debug/tbtools.py | 449 + .../site-packages/werkzeug/exceptions.py | 905 ++ .../site-packages/werkzeug/formparser.py | 430 + .../python3.12/site-packages/werkzeug/http.py | 1443 +++ .../site-packages/werkzeug/local.py | 653 ++ .../werkzeug/middleware/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 195 bytes .../__pycache__/dispatcher.cpython-312.pyc | Bin 0 -> 3313 bytes .../__pycache__/http_proxy.cpython-312.pyc | Bin 0 -> 9409 bytes .../__pycache__/lint.cpython-312.pyc | Bin 0 -> 17755 bytes .../__pycache__/profiler.cpython-312.pyc | Bin 0 -> 7199 bytes .../__pycache__/proxy_fix.cpython-312.pyc | Bin 0 -> 7196 bytes .../__pycache__/shared_data.cpython-312.pyc | Bin 0 -> 12731 bytes .../werkzeug/middleware/dispatcher.py | 81 + .../werkzeug/middleware/http_proxy.py | 236 + .../site-packages/werkzeug/middleware/lint.py | 439 + .../werkzeug/middleware/profiler.py | 155 + .../werkzeug/middleware/proxy_fix.py | 183 + .../werkzeug/middleware/shared_data.py | 283 + .../site-packages/werkzeug/py.typed | 0 .../werkzeug/routing/__init__.py | 134 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4668 bytes .../__pycache__/converters.cpython-312.pyc | Bin 0 -> 10915 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 7911 bytes .../routing/__pycache__/map.cpython-312.pyc | Bin 0 -> 39144 bytes .../__pycache__/matcher.cpython-312.pyc | Bin 0 -> 8233 bytes .../routing/__pycache__/rules.cpython-312.pyc | Bin 0 -> 39079 bytes .../werkzeug/routing/converters.py | 261 + .../werkzeug/routing/exceptions.py | 152 + .../site-packages/werkzeug/routing/map.py | 928 ++ .../site-packages/werkzeug/routing/matcher.py | 202 + .../site-packages/werkzeug/routing/rules.py | 927 ++ .../site-packages/werkzeug/sansio/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 191 bytes .../sansio/__pycache__/http.cpython-312.pyc | Bin 0 -> 5637 bytes .../__pycache__/multipart.cpython-312.pyc | Bin 0 -> 14317 bytes .../__pycache__/request.cpython-312.pyc | Bin 0 -> 21943 bytes .../__pycache__/response.cpython-312.pyc | Bin 0 -> 31725 bytes .../sansio/__pycache__/utils.cpython-312.pyc | Bin 0 -> 8240 bytes .../site-packages/werkzeug/sansio/http.py | 170 + .../werkzeug/sansio/multipart.py | 331 + .../site-packages/werkzeug/sansio/request.py | 536 + .../site-packages/werkzeug/sansio/response.py | 763 ++ .../site-packages/werkzeug/sansio/utils.py | 224 + .../site-packages/werkzeug/security.py | 201 + .../site-packages/werkzeug/serving.py | 1126 +++ .../python3.12/site-packages/werkzeug/test.py | 1464 +++ .../site-packages/werkzeug/testapp.py | 194 + .../python3.12/site-packages/werkzeug/urls.py | 203 + .../site-packages/werkzeug/user_agent.py | 47 + .../site-packages/werkzeug/utils.py | 684 ++ .../werkzeug/wrappers/__init__.py | 3 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 315 bytes .../__pycache__/request.cpython-312.pyc | Bin 0 -> 26124 bytes .../__pycache__/response.cpython-312.pyc | Bin 0 -> 34793 bytes .../werkzeug/wrappers/request.py | 650 ++ .../werkzeug/wrappers/response.py | 838 ++ .../python3.12/site-packages/werkzeug/wsgi.py | 609 ++ venv/lib64 | 1 + venv/pyvenv.cfg | 5 + 2170 files changed, 498740 insertions(+) create mode 100644 app/__init__.py create mode 100644 app/models.py create mode 100644 app/routes.py create mode 100644 app/static/style.css create mode 100644 app/templates/add_workout.html create mode 100644 app/templates/base.html create mode 100644 app/templates/index.html create mode 100644 requirements.txt create mode 100644 run.py create mode 100644 venv/bin/Activate.ps1 create mode 100644 venv/bin/activate create mode 100644 venv/bin/activate.csh create mode 100644 venv/bin/activate.fish create mode 100755 venv/bin/alembic create mode 100755 venv/bin/dotenv create mode 100755 venv/bin/flask create mode 100755 venv/bin/mako-render create mode 100755 venv/bin/pip create mode 100755 venv/bin/pip3 create mode 100755 venv/bin/pip3.12 create mode 120000 venv/bin/python create mode 120000 venv/bin/python3 create mode 120000 venv/bin/python3.12 create mode 100644 venv/include/site/python3.12/greenlet/greenlet.h create mode 100644 venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/LICENSE create mode 100644 venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/REQUESTED create mode 100644 venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/LICENSE create mode 100644 venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/REQUESTED create mode 100644 venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/entry_points.txt create mode 100644 venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/licenses/LICENSE create mode 100644 venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/alembic/__init__.py create mode 100644 venv/lib/python3.12/site-packages/alembic/__main__.py create mode 100644 venv/lib/python3.12/site-packages/alembic/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/__pycache__/command.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/__pycache__/config.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/__pycache__/context.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/__pycache__/environment.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/__pycache__/migration.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/__pycache__/op.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/__init__.py create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/api.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/render.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/rewriter.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/api.py create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__init__.py create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__pycache__/comments.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__pycache__/constraints.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__pycache__/schema.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__pycache__/server_defaults.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__pycache__/tables.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__pycache__/types.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__pycache__/util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/comments.py create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/constraints.py create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/schema.py create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/server_defaults.py create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/tables.py create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/types.py create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/compare/util.py create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/render.py create mode 100644 venv/lib/python3.12/site-packages/alembic/autogenerate/rewriter.py create mode 100644 venv/lib/python3.12/site-packages/alembic/command.py create mode 100644 venv/lib/python3.12/site-packages/alembic/config.py create mode 100644 venv/lib/python3.12/site-packages/alembic/context.py create mode 100644 venv/lib/python3.12/site-packages/alembic/context.pyi create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/__init__.py create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/_autogen.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/impl.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/mssql.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/mysql.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/oracle.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/postgresql.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/sqlite.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/_autogen.py create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/base.py create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/impl.py create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/mssql.py create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/mysql.py create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/oracle.py create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/postgresql.py create mode 100644 venv/lib/python3.12/site-packages/alembic/ddl/sqlite.py create mode 100644 venv/lib/python3.12/site-packages/alembic/environment.py create mode 100644 venv/lib/python3.12/site-packages/alembic/migration.py create mode 100644 venv/lib/python3.12/site-packages/alembic/op.py create mode 100644 venv/lib/python3.12/site-packages/alembic/op.pyi create mode 100644 venv/lib/python3.12/site-packages/alembic/operations/__init__.py create mode 100644 venv/lib/python3.12/site-packages/alembic/operations/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/operations/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/operations/__pycache__/batch.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/operations/__pycache__/ops.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/operations/__pycache__/schemaobj.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/operations/__pycache__/toimpl.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/operations/base.py create mode 100644 venv/lib/python3.12/site-packages/alembic/operations/batch.py create mode 100644 venv/lib/python3.12/site-packages/alembic/operations/ops.py create mode 100644 venv/lib/python3.12/site-packages/alembic/operations/schemaobj.py create mode 100644 venv/lib/python3.12/site-packages/alembic/operations/toimpl.py create mode 100644 venv/lib/python3.12/site-packages/alembic/py.typed create mode 100644 venv/lib/python3.12/site-packages/alembic/runtime/__init__.py create mode 100644 venv/lib/python3.12/site-packages/alembic/runtime/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/runtime/__pycache__/environment.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/runtime/__pycache__/migration.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/runtime/__pycache__/plugins.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/runtime/environment.py create mode 100644 venv/lib/python3.12/site-packages/alembic/runtime/migration.py create mode 100644 venv/lib/python3.12/site-packages/alembic/runtime/plugins.py create mode 100644 venv/lib/python3.12/site-packages/alembic/script/__init__.py create mode 100644 venv/lib/python3.12/site-packages/alembic/script/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/script/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/script/__pycache__/revision.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/script/__pycache__/write_hooks.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/script/base.py create mode 100644 venv/lib/python3.12/site-packages/alembic/script/revision.py create mode 100644 venv/lib/python3.12/site-packages/alembic/script/write_hooks.py create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/async/README create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/async/__pycache__/env.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/async/alembic.ini.mako create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/async/env.py create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/async/script.py.mako create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/generic/README create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/generic/__pycache__/env.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/generic/alembic.ini.mako create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/generic/env.py create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/generic/script.py.mako create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/multidb/README create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/multidb/__pycache__/env.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/multidb/alembic.ini.mako create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/multidb/env.py create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/multidb/script.py.mako create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/pyproject/README create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/pyproject/__pycache__/env.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/pyproject/alembic.ini.mako create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/pyproject/env.py create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/pyproject/pyproject.toml.mako create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/pyproject/script.py.mako create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/README create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/__pycache__/env.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/alembic.ini.mako create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/env.py create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/pyproject.toml.mako create mode 100644 venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/script.py.mako create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/__init__.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/__pycache__/assertions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/__pycache__/env.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/__pycache__/fixtures.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/__pycache__/requirements.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/__pycache__/schemacompare.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/__pycache__/util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/__pycache__/warnings.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/assertions.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/env.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/fixtures.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/plugin/__init__.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/plugin/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/plugin/__pycache__/bootstrap.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/plugin/bootstrap.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/requirements.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/schemacompare.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/__init__.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/_autogen_fixtures.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_comments.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_computed.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_diffs.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_fks.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_identity.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_environment.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_op.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/_autogen_fixtures.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_comments.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_computed.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_diffs.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_fks.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_identity.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/test_environment.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/suite/test_op.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/util.py create mode 100644 venv/lib/python3.12/site-packages/alembic/testing/warnings.py create mode 100644 venv/lib/python3.12/site-packages/alembic/util/__init__.py create mode 100644 venv/lib/python3.12/site-packages/alembic/util/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/util/__pycache__/compat.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/util/__pycache__/editor.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/util/__pycache__/exc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/util/__pycache__/langhelpers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/util/__pycache__/messaging.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/util/__pycache__/pyfiles.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/util/__pycache__/sqla_compat.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/alembic/util/compat.py create mode 100644 venv/lib/python3.12/site-packages/alembic/util/editor.py create mode 100644 venv/lib/python3.12/site-packages/alembic/util/exc.py create mode 100644 venv/lib/python3.12/site-packages/alembic/util/langhelpers.py create mode 100644 venv/lib/python3.12/site-packages/alembic/util/messaging.py create mode 100644 venv/lib/python3.12/site-packages/alembic/util/pyfiles.py create mode 100644 venv/lib/python3.12/site-packages/alembic/util/sqla_compat.py create mode 100644 venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/blinker/__init__.py create mode 100644 venv/lib/python3.12/site-packages/blinker/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/blinker/__pycache__/_utilities.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/blinker/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/blinker/_utilities.py create mode 100644 venv/lib/python3.12/site-packages/blinker/base.py create mode 100644 venv/lib/python3.12/site-packages/blinker/py.typed create mode 100644 venv/lib/python3.12/site-packages/click-8.3.2.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/click-8.3.2.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/click-8.3.2.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/click-8.3.2.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/click-8.3.2.dist-info/licenses/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/click/__init__.py create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/_compat.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/_termui_impl.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/_textwrap.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/_utils.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/_winconsole.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/core.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/decorators.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/formatting.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/globals.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/parser.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/shell_completion.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/termui.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/testing.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/types.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/__pycache__/utils.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/click/_compat.py create mode 100644 venv/lib/python3.12/site-packages/click/_termui_impl.py create mode 100644 venv/lib/python3.12/site-packages/click/_textwrap.py create mode 100644 venv/lib/python3.12/site-packages/click/_utils.py create mode 100644 venv/lib/python3.12/site-packages/click/_winconsole.py create mode 100644 venv/lib/python3.12/site-packages/click/core.py create mode 100644 venv/lib/python3.12/site-packages/click/decorators.py create mode 100644 venv/lib/python3.12/site-packages/click/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/click/formatting.py create mode 100644 venv/lib/python3.12/site-packages/click/globals.py create mode 100644 venv/lib/python3.12/site-packages/click/parser.py create mode 100644 venv/lib/python3.12/site-packages/click/py.typed create mode 100644 venv/lib/python3.12/site-packages/click/shell_completion.py create mode 100644 venv/lib/python3.12/site-packages/click/termui.py create mode 100644 venv/lib/python3.12/site-packages/click/testing.py create mode 100644 venv/lib/python3.12/site-packages/click/types.py create mode 100644 venv/lib/python3.12/site-packages/click/utils.py create mode 100644 venv/lib/python3.12/site-packages/dotenv/__init__.py create mode 100644 venv/lib/python3.12/site-packages/dotenv/__main__.py create mode 100644 venv/lib/python3.12/site-packages/dotenv/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/dotenv/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/dotenv/__pycache__/cli.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/dotenv/__pycache__/ipython.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/dotenv/__pycache__/main.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/dotenv/__pycache__/parser.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/dotenv/__pycache__/variables.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/dotenv/__pycache__/version.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/dotenv/cli.py create mode 100644 venv/lib/python3.12/site-packages/dotenv/ipython.py create mode 100644 venv/lib/python3.12/site-packages/dotenv/main.py create mode 100644 venv/lib/python3.12/site-packages/dotenv/parser.py create mode 100644 venv/lib/python3.12/site-packages/dotenv/py.typed create mode 100644 venv/lib/python3.12/site-packages/dotenv/variables.py create mode 100644 venv/lib/python3.12/site-packages/dotenv/version.py create mode 100644 venv/lib/python3.12/site-packages/flask-2.3.3.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/flask-2.3.3.dist-info/LICENSE.rst create mode 100644 venv/lib/python3.12/site-packages/flask-2.3.3.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/flask-2.3.3.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/flask-2.3.3.dist-info/REQUESTED create mode 100644 venv/lib/python3.12/site-packages/flask-2.3.3.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/flask-2.3.3.dist-info/entry_points.txt create mode 100644 venv/lib/python3.12/site-packages/flask/__init__.py create mode 100644 venv/lib/python3.12/site-packages/flask/__main__.py create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/app.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/blueprints.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/cli.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/config.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/ctx.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/debughelpers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/globals.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/helpers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/logging.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/scaffold.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/sessions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/signals.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/templating.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/testing.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/typing.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/views.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/__pycache__/wrappers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/app.py create mode 100644 venv/lib/python3.12/site-packages/flask/blueprints.py create mode 100644 venv/lib/python3.12/site-packages/flask/cli.py create mode 100644 venv/lib/python3.12/site-packages/flask/config.py create mode 100644 venv/lib/python3.12/site-packages/flask/ctx.py create mode 100644 venv/lib/python3.12/site-packages/flask/debughelpers.py create mode 100644 venv/lib/python3.12/site-packages/flask/globals.py create mode 100644 venv/lib/python3.12/site-packages/flask/helpers.py create mode 100644 venv/lib/python3.12/site-packages/flask/json/__init__.py create mode 100644 venv/lib/python3.12/site-packages/flask/json/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/json/__pycache__/provider.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/json/__pycache__/tag.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask/json/provider.py create mode 100644 venv/lib/python3.12/site-packages/flask/json/tag.py create mode 100644 venv/lib/python3.12/site-packages/flask/logging.py create mode 100644 venv/lib/python3.12/site-packages/flask/py.typed create mode 100644 venv/lib/python3.12/site-packages/flask/scaffold.py create mode 100644 venv/lib/python3.12/site-packages/flask/sessions.py create mode 100644 venv/lib/python3.12/site-packages/flask/signals.py create mode 100644 venv/lib/python3.12/site-packages/flask/templating.py create mode 100644 venv/lib/python3.12/site-packages/flask/testing.py create mode 100644 venv/lib/python3.12/site-packages/flask/typing.py create mode 100644 venv/lib/python3.12/site-packages/flask/views.py create mode 100644 venv/lib/python3.12/site-packages/flask/wrappers.py create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/__init__.py create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/__pycache__/cli.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/cli.py create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/aioflask-multidb/README create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/aioflask-multidb/__pycache__/env.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/aioflask-multidb/alembic.ini.mako create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/aioflask-multidb/env.py create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/aioflask-multidb/script.py.mako create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/aioflask/README create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/aioflask/__pycache__/env.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/aioflask/alembic.ini.mako create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/aioflask/env.py create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/aioflask/script.py.mako create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/flask-multidb/README create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/flask-multidb/__pycache__/env.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/flask-multidb/alembic.ini.mako create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/flask-multidb/env.py create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/flask-multidb/script.py.mako create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/flask/README create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/flask/__pycache__/env.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/flask/alembic.ini.mako create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/flask/env.py create mode 100644 venv/lib/python3.12/site-packages/flask_migrate/templates/flask/script.py.mako create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy-3.0.5.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy-3.0.5.dist-info/LICENSE.rst create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy-3.0.5.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy-3.0.5.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy-3.0.5.dist-info/REQUESTED create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy-3.0.5.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/__init__.py create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/__pycache__/cli.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/__pycache__/extension.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/__pycache__/model.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/__pycache__/pagination.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/__pycache__/query.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/__pycache__/record_queries.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/__pycache__/session.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/__pycache__/table.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/__pycache__/track_modifications.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/cli.py create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/extension.py create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/model.py create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/pagination.py create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/py.typed create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/query.py create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/record_queries.py create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/session.py create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/table.py create mode 100644 venv/lib/python3.12/site-packages/flask_sqlalchemy/track_modifications.py create mode 100644 venv/lib/python3.12/site-packages/greenlet-3.4.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/greenlet-3.4.0.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/greenlet-3.4.0.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/greenlet-3.4.0.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/greenlet-3.4.0.dist-info/licenses/LICENSE create mode 100644 venv/lib/python3.12/site-packages/greenlet-3.4.0.dist-info/licenses/LICENSE.PSF create mode 100644 venv/lib/python3.12/site-packages/greenlet-3.4.0.dist-info/sboms/auditwheel.cdx.json create mode 100644 venv/lib/python3.12/site-packages/greenlet-3.4.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/greenlet.libs/libgcc_s-0cd532bd.so.1 create mode 100755 venv/lib/python3.12/site-packages/greenlet.libs/libstdc++-5d72f927.so.6.0.33 create mode 100644 venv/lib/python3.12/site-packages/greenlet/CObjects.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/PyGreenlet.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/PyGreenlet.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/PyGreenletUnswitchable.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/PyModule.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/TBrokenGreenlet.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/TExceptionState.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/TGreenlet.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/TGreenlet.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/TGreenletGlobals.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/TMainGreenlet.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/TPythonState.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/TStackState.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/TThreadState.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/TThreadStateCreator.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/TThreadStateDestroy.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/TUserGreenlet.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/__init__.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/greenlet.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/greenlet.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/greenlet_allocator.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/greenlet_compiler_compat.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/greenlet_cpython_compat.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/greenlet_exceptions.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/greenlet_internal.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/greenlet_msvc_compat.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/greenlet_refs.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/greenlet_slp_switch.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/greenlet_thread_support.hpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/__init__.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/setup_switch_x64_masm.cmd create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_aarch64_gcc.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_alpha_unix.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_amd64_unix.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_arm32_gcc.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_arm32_ios.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_arm64_masm.asm create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_arm64_masm.obj create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_arm64_msvc.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_csky_gcc.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_loongarch64_linux.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_m68k_gcc.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_mips_unix.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_ppc64_aix.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_ppc64_linux.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_ppc_aix.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_ppc_linux.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_ppc_macosx.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_ppc_unix.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_riscv_unix.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_s390_unix.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_sh_gcc.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_sparc_sun_gcc.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_x32_unix.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_x64_masm.asm create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_x64_masm.obj create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_x64_msvc.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_x86_msvc.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/platform/switch_x86_unix.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/slp_platformselect.h create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__init__.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/fail_clearing_run_switches.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/fail_cpp_exception.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/fail_initialstub_already_started.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/fail_slp_switch.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/fail_switch_three_greenlets.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/fail_switch_three_greenlets2.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/fail_switch_two_greenlets.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/leakcheck.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_contextvars.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_cpp.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_extension_interface.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_gc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_generator.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_generator_nested.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_greenlet.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_greenlet_trash.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_interpreter_shutdown.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_leaks.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_stack_saved.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_throw.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_tracing.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_version.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/__pycache__/test_weakref.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/_test_extension.c create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/_test_extension_cpp.cpp create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/fail_clearing_run_switches.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/fail_cpp_exception.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/fail_initialstub_already_started.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/fail_slp_switch.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/fail_switch_three_greenlets.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/fail_switch_three_greenlets2.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/fail_switch_two_greenlets.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/leakcheck.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_contextvars.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_cpp.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_extension_interface.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_gc.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_generator.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_generator_nested.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_greenlet.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_greenlet_trash.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_interpreter_shutdown.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_leaks.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_stack_saved.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_throw.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_tracing.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_version.py create mode 100644 venv/lib/python3.12/site-packages/greenlet/tests/test_weakref.py create mode 100644 venv/lib/python3.12/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/itsdangerous-2.2.0.dist-info/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/itsdangerous-2.2.0.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/itsdangerous-2.2.0.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/itsdangerous-2.2.0.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/__init__.py create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/__pycache__/_json.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/__pycache__/encoding.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/__pycache__/exc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/__pycache__/serializer.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/__pycache__/signer.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/__pycache__/timed.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/__pycache__/url_safe.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/_json.py create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/encoding.py create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/exc.py create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/py.typed create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/serializer.py create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/signer.py create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/timed.py create mode 100644 venv/lib/python3.12/site-packages/itsdangerous/url_safe.py create mode 100644 venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/entry_points.txt create mode 100644 venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/jinja2/__init__.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/_identifier.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/async_utils.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/bccache.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/compiler.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/constants.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/debug.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/defaults.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/environment.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/ext.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/filters.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/idtracking.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/lexer.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/loaders.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/meta.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/nativetypes.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/nodes.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/optimizer.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/parser.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/runtime.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/sandbox.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/tests.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/utils.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/__pycache__/visitor.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/jinja2/_identifier.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/async_utils.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/bccache.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/compiler.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/constants.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/debug.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/defaults.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/environment.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/ext.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/filters.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/idtracking.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/lexer.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/loaders.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/meta.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/nativetypes.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/nodes.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/optimizer.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/parser.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/py.typed create mode 100644 venv/lib/python3.12/site-packages/jinja2/runtime.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/sandbox.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/tests.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/utils.py create mode 100644 venv/lib/python3.12/site-packages/jinja2/visitor.py create mode 100644 venv/lib/python3.12/site-packages/mako-1.3.11.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/mako-1.3.11.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/mako-1.3.11.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/mako-1.3.11.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/mako-1.3.11.dist-info/entry_points.txt create mode 100644 venv/lib/python3.12/site-packages/mako-1.3.11.dist-info/licenses/LICENSE create mode 100644 venv/lib/python3.12/site-packages/mako-1.3.11.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/mako/__init__.py create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/_ast_util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/ast.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/cache.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/cmd.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/codegen.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/compat.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/filters.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/lexer.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/lookup.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/parsetree.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/pygen.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/pyparser.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/runtime.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/template.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/__pycache__/util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/_ast_util.py create mode 100644 venv/lib/python3.12/site-packages/mako/ast.py create mode 100644 venv/lib/python3.12/site-packages/mako/cache.py create mode 100644 venv/lib/python3.12/site-packages/mako/cmd.py create mode 100644 venv/lib/python3.12/site-packages/mako/codegen.py create mode 100644 venv/lib/python3.12/site-packages/mako/compat.py create mode 100644 venv/lib/python3.12/site-packages/mako/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/mako/ext/__init__.py create mode 100644 venv/lib/python3.12/site-packages/mako/ext/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/ext/__pycache__/autohandler.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/ext/__pycache__/babelplugin.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/ext/__pycache__/beaker_cache.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/ext/__pycache__/extract.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/ext/__pycache__/linguaplugin.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/ext/__pycache__/preprocessors.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/ext/__pycache__/pygmentplugin.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/ext/__pycache__/turbogears.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/ext/autohandler.py create mode 100644 venv/lib/python3.12/site-packages/mako/ext/babelplugin.py create mode 100644 venv/lib/python3.12/site-packages/mako/ext/beaker_cache.py create mode 100644 venv/lib/python3.12/site-packages/mako/ext/extract.py create mode 100644 venv/lib/python3.12/site-packages/mako/ext/linguaplugin.py create mode 100644 venv/lib/python3.12/site-packages/mako/ext/preprocessors.py create mode 100644 venv/lib/python3.12/site-packages/mako/ext/pygmentplugin.py create mode 100644 venv/lib/python3.12/site-packages/mako/ext/turbogears.py create mode 100644 venv/lib/python3.12/site-packages/mako/filters.py create mode 100644 venv/lib/python3.12/site-packages/mako/lexer.py create mode 100644 venv/lib/python3.12/site-packages/mako/lookup.py create mode 100644 venv/lib/python3.12/site-packages/mako/parsetree.py create mode 100644 venv/lib/python3.12/site-packages/mako/pygen.py create mode 100644 venv/lib/python3.12/site-packages/mako/pyparser.py create mode 100644 venv/lib/python3.12/site-packages/mako/runtime.py create mode 100644 venv/lib/python3.12/site-packages/mako/template.py create mode 100644 venv/lib/python3.12/site-packages/mako/testing/__init__.py create mode 100644 venv/lib/python3.12/site-packages/mako/testing/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/testing/__pycache__/_config.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/testing/__pycache__/assertions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/testing/__pycache__/config.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/testing/__pycache__/exclusions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/testing/__pycache__/fixtures.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/testing/__pycache__/helpers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/mako/testing/_config.py create mode 100644 venv/lib/python3.12/site-packages/mako/testing/assertions.py create mode 100644 venv/lib/python3.12/site-packages/mako/testing/config.py create mode 100644 venv/lib/python3.12/site-packages/mako/testing/exclusions.py create mode 100644 venv/lib/python3.12/site-packages/mako/testing/fixtures.py create mode 100644 venv/lib/python3.12/site-packages/mako/testing/helpers.py create mode 100644 venv/lib/python3.12/site-packages/mako/util.py create mode 100644 venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/licenses/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/markupsafe/__init__.py create mode 100644 venv/lib/python3.12/site-packages/markupsafe/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/markupsafe/__pycache__/_native.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/markupsafe/_native.py create mode 100644 venv/lib/python3.12/site-packages/markupsafe/_speedups.c create mode 100644 venv/lib/python3.12/site-packages/markupsafe/_speedups.pyi create mode 100644 venv/lib/python3.12/site-packages/markupsafe/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip-25.0.1.dist-info/AUTHORS.txt create mode 100644 venv/lib/python3.12/site-packages/pip-25.0.1.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/pip-25.0.1.dist-info/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/pip-25.0.1.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/pip-25.0.1.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/pip-25.0.1.dist-info/REQUESTED create mode 100644 venv/lib/python3.12/site-packages/pip-25.0.1.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/pip-25.0.1.dist-info/entry_points.txt create mode 100644 venv/lib/python3.12/site-packages/pip-25.0.1.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/pip/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/__main__.py create mode 100644 venv/lib/python3.12/site-packages/pip/__pip-runner__.py create mode 100644 venv/lib/python3.12/site-packages/pip/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/__pycache__/__pip-runner__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/__pycache__/build_env.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/__pycache__/cache.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/__pycache__/configuration.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/__pycache__/main.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/__pycache__/pyproject.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/build_env.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cache.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/index_command.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/parser.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/base_command.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/command_context.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/index_command.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/main.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/main_parser.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/parser.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/req_command.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/spinners.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/cli/status_codes.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/cache.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/check.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/completion.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/debug.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/download.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/hash.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/help.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/index.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/inspect.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/install.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/list.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/search.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/show.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/cache.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/check.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/completion.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/configuration.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/debug.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/download.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/freeze.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/hash.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/help.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/index.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/inspect.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/install.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/list.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/search.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/show.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/uninstall.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/commands/wheel.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/configuration.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/distributions/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/distributions/base.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/distributions/installed.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/distributions/wheel.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/index/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/collector.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/sources.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/index/collector.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/index/package_finder.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/index/sources.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/locations/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_distutils.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/locations/_distutils.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/locations/_sysconfig.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/locations/base.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/main.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/_json.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/_json.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/base.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_compat.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_dists.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_envs.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_compat.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_dists.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_envs.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/metadata/pkg_resources.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/candidate.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/format_control.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/index.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/installation_report.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/link.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/scheme.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/target_python.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/candidate.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/direct_url.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/format_control.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/index.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/installation_report.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/link.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/scheme.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/search_scope.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/target_python.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/models/wheel.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/auth.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/cache.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/download.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/session.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/utils.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/auth.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/cache.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/download.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/lazy_wheel.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/session.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/utils.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/check.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_editable.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/build_tracker.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_editable.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_legacy.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_editable.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_legacy.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/check.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/freeze.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/operations/prepare.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/pyproject.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/constructors.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_file.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_install.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_set.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/constructors.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/req_file.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/req_install.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/req_set.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/base.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/reporter.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/self_outdated_check.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_jaraco_text.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_log.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compat.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/logging.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/misc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/retry.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/urls.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/_jaraco_text.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/_log.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/appdirs.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/compat.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/datetime.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/deprecation.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/egg_link.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/filesystem.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/filetypes.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/glibc.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/hashes.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/logging.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/misc.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/packaging.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/retry.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/subprocess.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/unpacking.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/urls.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/utils/wheel.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/vcs/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/git.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/vcs/git.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/vcs/subversion.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 venv/lib/python3.12/site-packages/pip/_internal/wheel_builder.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/typing_extensions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/certifi/core.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/certifi/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/compat.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/database.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/index.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/locators.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/markers.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/resources.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/util.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/version.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distro/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distro/__main__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/distro.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distro/distro.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/distro/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/core.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/codec.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/compat.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/core.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/intranges.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/package_data.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/msgpack/ext.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_parser.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/metadata.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/_elffile.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/_manylinux.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/_musllinux.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/_parser.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/_tokenizer.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/__pycache__/_spdx.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/licenses/_spdx.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/markers.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/metadata.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/tags.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/utils.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/packaging/version.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__main__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/android.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/api.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/version.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/android.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/api.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/macos.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/unix.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/version.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/windows.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__main__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/cmdline.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/console.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/filter.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/formatter.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/lexer.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/modeline.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/plugin.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/regexopt.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/scanner.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/sphinxext.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/style.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/token.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/unistring.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/cmdline.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/console.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/filter.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatter.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/bbcode.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/groff.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/html.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/img.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/irc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/latex.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/other.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/pangomarkup.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/rtf.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/svg.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal256.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/_mapping.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/bbcode.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/groff.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/html.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/img.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/irc.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/latex.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/other.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/pangomarkup.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/rtf.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/svg.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/terminal.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/terminal256.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexer.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/python.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/_mapping.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/python.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/modeline.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/plugin.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/regexopt.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/scanner.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/sphinxext.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/style.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__pycache__/_mapping.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/_mapping.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/token.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/unistring.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pygments/util.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/api.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/help.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/models.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/__version__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/adapters.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/api.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/auth.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/certs.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/compat.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/cookies.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/help.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/hooks.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/models.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/packages.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/sessions.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/structures.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/requests/utils.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/resolvers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/providers.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/reporters.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/structs.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__main__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__main__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_cell_widths.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_replace.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_export_format.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_extension.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_fileno.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_inspect.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_log_render.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_loop.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_null_file.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_palettes.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_pick.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_ratio.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_spinners.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_stack.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_timer.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_win32_console.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows_renderer.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_wrap.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/abc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/align.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/ansi.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/bar.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/box.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/cells.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color_triplet.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/columns.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/console.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/constrain.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/containers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/control.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/default_styles.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/diagnose.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/emoji.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/errors.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/file_proxy.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/filesize.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/highlighter.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/json.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/jupyter.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/layout.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live_render.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/logging.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/markup.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/measure.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/padding.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pager.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/palette.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/panel.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pretty.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress_bar.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/prompt.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/protocol.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/region.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/repr.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/rule.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/scope.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/screen.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/segment.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/spinner.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/status.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/style.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/styled.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/syntax.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/table.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/terminal_theme.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/text.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/theme.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/themes.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/traceback.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/tree.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_cell_widths.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_codes.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_replace.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_extension.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_fileno.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_inspect.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_log_render.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_loop.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_null_file.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_palettes.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_pick.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_ratio.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_spinners.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_stack.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_timer.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_win32_console.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows_renderer.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/_wrap.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/abc.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/align.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/ansi.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/bar.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/box.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/cells.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/color.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/color_triplet.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/columns.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/console.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/constrain.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/containers.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/control.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/default_styles.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/diagnose.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/emoji.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/errors.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/file_proxy.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/filesize.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/highlighter.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/json.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/jupyter.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/layout.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/live.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/live_render.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/logging.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/markup.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/measure.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/padding.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/pager.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/palette.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/panel.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/pretty.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/progress.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/progress_bar.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/prompt.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/protocol.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/region.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/repr.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/rule.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/scope.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/screen.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/segment.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/spinner.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/status.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/style.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/styled.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/syntax.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/table.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/terminal_theme.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/text.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/theme.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/themes.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/traceback.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/rich/tree.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_parser.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_re.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_types.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli/_parser.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli/_re.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli/_types.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/tomli/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_api.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_macos.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_openssl.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_ssl_constants.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_windows.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/_api.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/_macos.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/_openssl.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/_ssl_constants.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/_windows.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/truststore/py.typed create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/typing_extensions.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/_version.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/_version.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/weakref_finalize.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/weakref_finalize.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/request.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/response.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/connection.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/proxy.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/queue.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/wait.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/proxy.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssltransport.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 venv/lib/python3.12/site-packages/pip/_vendor/vendor.txt create mode 100644 venv/lib/python3.12/site-packages/pip/py.typed create mode 100644 venv/lib/python3.12/site-packages/python_dotenv-1.0.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/python_dotenv-1.0.0.dist-info/LICENSE create mode 100644 venv/lib/python3.12/site-packages/python_dotenv-1.0.0.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/python_dotenv-1.0.0.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/python_dotenv-1.0.0.dist-info/REQUESTED create mode 100644 venv/lib/python3.12/site-packages/python_dotenv-1.0.0.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/python_dotenv-1.0.0.dist-info/entry_points.txt create mode 100644 venv/lib/python3.12/site-packages/python_dotenv-1.0.0.dist-info/top_level.txt create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/__pycache__/events.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/__pycache__/exc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/__pycache__/inspection.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/__pycache__/log.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/__pycache__/schema.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/__pycache__/types.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/connectors/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/connectors/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/connectors/__pycache__/aioodbc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/connectors/__pycache__/asyncio.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/connectors/__pycache__/pyodbc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/connectors/aioodbc.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/connectors/asyncio.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/connectors/pyodbc.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/cyextension/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/cyextension/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/cyextension/collections.pyx create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/cyextension/immutabledict.pxd create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/cyextension/immutabledict.pyx create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/cyextension/processors.pyx create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/cyextension/resultproxy.pyx create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/cyextension/util.pyx create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/__pycache__/_typing.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/_typing.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/__pycache__/aioodbc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/__pycache__/json.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/__pycache__/provision.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/aioodbc.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/information_schema.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/json.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/provision.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/pymssql.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mssql/pyodbc.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/asyncmy.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/dml.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/expression.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/json.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/provision.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/reserved_words.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/__pycache__/types.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/aiomysql.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/asyncmy.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/cymysql.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/dml.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/enumerated.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/expression.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/json.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/mariadb.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/mariadbconnector.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/mysqlconnector.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/mysqldb.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/provision.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/pymysql.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/pyodbc.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/reflection.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/reserved_words.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/mysql/types.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/__pycache__/dictionary.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/__pycache__/oracledb.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/__pycache__/provision.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/__pycache__/types.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/dictionary.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/oracledb.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/provision.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/oracle/types.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/_psycopg_common.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/array.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/json.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/named_types.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/operators.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/pg_catalog.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/psycopg.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/__pycache__/types.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/_psycopg_common.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/array.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/dml.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/ext.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/hstore.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/json.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/named_types.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/operators.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/pg8000.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/pg_catalog.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/provision.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/psycopg.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/psycopg2cffi.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/ranges.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/postgresql/types.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/__pycache__/json.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/aiosqlite.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/dml.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/json.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/provision.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/pysqlcipher.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/dialects/type_migration_guidelines.txt create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/_py_processors.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/_py_row.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/_py_util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/characteristics.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/create.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/cursor.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/default.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/events.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/interfaces.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/mock.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/processors.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/reflection.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/result.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/row.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/strategies.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/url.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/__pycache__/util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/_py_processors.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/_py_row.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/_py_util.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/characteristics.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/create.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/cursor.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/default.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/events.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/interfaces.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/mock.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/processors.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/reflection.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/result.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/row.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/strategies.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/url.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/engine/util.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/event/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/event/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/event/__pycache__/api.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/event/__pycache__/attr.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/event/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/event/__pycache__/legacy.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/event/__pycache__/registry.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/event/api.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/event/attr.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/event/base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/event/legacy.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/event/registry.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/events.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/exc.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__pycache__/associationproxy.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__pycache__/automap.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__pycache__/baked.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__pycache__/compiler.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__pycache__/horizontal_shard.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__pycache__/hybrid.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__pycache__/indexable.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__pycache__/instrumentation.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__pycache__/mutable.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__pycache__/orderinglist.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/__pycache__/serializer.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/associationproxy.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/__pycache__/engine.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/__pycache__/exc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/__pycache__/result.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/__pycache__/session.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/engine.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/exc.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/result.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/scoping.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/asyncio/session.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/automap.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/baked.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/compiler.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/declarative/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/declarative/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/declarative/__pycache__/extensions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/declarative/extensions.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/horizontal_shard.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/hybrid.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/indexable.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/instrumentation.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mutable.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/__pycache__/apply.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/__pycache__/infer.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/__pycache__/names.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/__pycache__/plugin.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/__pycache__/util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/apply.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/decl_class.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/infer.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/names.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/plugin.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/mypy/util.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/orderinglist.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/ext/serializer.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/future/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/future/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/future/__pycache__/engine.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/future/engine.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/inspection.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/log.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/_orm_constructors.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/_typing.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/attributes.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/bulk_persistence.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/clsregistry.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/collections.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/context.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/decl_api.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/decl_base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/dependency.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/descriptor_props.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/dynamic.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/evaluator.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/events.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/exc.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/identity.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/instrumentation.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/interfaces.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/loading.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/mapped_collection.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/mapper.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/path_registry.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/persistence.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/properties.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/query.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/relationships.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/scoping.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/session.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/state.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/state_changes.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/strategies.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/strategy_options.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/sync.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/unitofwork.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/__pycache__/writeonly.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/_orm_constructors.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/_typing.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/attributes.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/bulk_persistence.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/clsregistry.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/collections.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/context.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/decl_api.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/decl_base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/dependency.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/descriptor_props.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/dynamic.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/evaluator.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/events.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/exc.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/identity.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/instrumentation.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/interfaces.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/loading.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/mapped_collection.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/mapper.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/path_registry.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/persistence.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/properties.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/query.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/relationships.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/scoping.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/session.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/state.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/state_changes.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/strategies.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/strategy_options.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/sync.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/unitofwork.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/util.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/orm/writeonly.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/pool/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/pool/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/pool/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/pool/__pycache__/events.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/pool/__pycache__/impl.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/pool/base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/pool/events.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/pool/impl.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/py.typed create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/schema.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/_dml_constructors.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/_elements_constructors.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/_orm_types.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/_py_util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/_selectable_constructors.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/_typing.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/annotation.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/cache_key.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/coercions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/compiler.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/crud.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/ddl.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/default_comparator.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/dml.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/elements.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/events.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/expression.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/functions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/lambdas.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/naming.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/operators.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/roles.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/schema.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/selectable.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/sqltypes.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/traversals.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/type_api.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/__pycache__/visitors.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/_dml_constructors.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/_elements_constructors.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/_orm_types.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/_py_util.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/_selectable_constructors.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/_typing.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/annotation.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/cache_key.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/coercions.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/compiler.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/crud.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/ddl.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/default_comparator.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/dml.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/elements.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/events.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/expression.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/functions.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/lambdas.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/naming.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/operators.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/roles.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/schema.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/selectable.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/sqltypes.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/traversals.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/type_api.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/util.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/sql/visitors.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/assertions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/assertsql.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/asyncio.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/config.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/engines.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/entities.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/exclusions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/pickleable.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/profiling.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/provision.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/requirements.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/schema.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/util.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/__pycache__/warnings.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/assertions.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/assertsql.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/asyncio.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/config.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/engines.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/entities.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/exclusions.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/fixtures/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/fixtures/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/fixtures/__pycache__/base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/fixtures/__pycache__/mypy.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/fixtures/__pycache__/orm.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/fixtures/__pycache__/sql.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/fixtures/base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/fixtures/mypy.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/fixtures/orm.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/fixtures/sql.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/pickleable.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/plugin/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/plugin/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/plugin/bootstrap.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/plugin/plugin_base.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/plugin/pytestplugin.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/profiling.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/provision.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/requirements.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/schema.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_cte.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_insert.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_results.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_select.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_types.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_cte.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_ddl.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_deprecations.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_dialect.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_insert.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_reflection.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_results.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_rowcount.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_select.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_sequence.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_types.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_unicode_ddl.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/suite/test_update_delete.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/util.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/testing/warnings.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/types.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__init__.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/_collections.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/_has_cy.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/_py_collections.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/compat.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/concurrency.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/deprecations.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/langhelpers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/preloaded.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/queue.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/tool_support.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/topological.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/__pycache__/typing.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/_collections.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/_concurrency_py3k.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/_has_cy.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/_py_collections.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/compat.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/concurrency.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/deprecations.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/langhelpers.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/preloaded.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/queue.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/tool_support.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/topological.py create mode 100644 venv/lib/python3.12/site-packages/sqlalchemy/util/typing.py create mode 100644 venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE create mode 100644 venv/lib/python3.12/site-packages/typing_extensions.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug-3.1.8.dist-info/INSTALLER create mode 100644 venv/lib/python3.12/site-packages/werkzeug-3.1.8.dist-info/METADATA create mode 100644 venv/lib/python3.12/site-packages/werkzeug-3.1.8.dist-info/RECORD create mode 100644 venv/lib/python3.12/site-packages/werkzeug-3.1.8.dist-info/WHEEL create mode 100644 venv/lib/python3.12/site-packages/werkzeug-3.1.8.dist-info/licenses/LICENSE.txt create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__init__.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/_internal.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/_reloader.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/formparser.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/http.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/local.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/security.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/serving.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/test.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/testapp.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/urls.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/user_agent.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/utils.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/__pycache__/wsgi.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/_internal.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/_reloader.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/__init__.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/__pycache__/accept.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/__pycache__/auth.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/__pycache__/cache_control.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/__pycache__/csp.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/__pycache__/etag.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/__pycache__/file_storage.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/__pycache__/headers.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/__pycache__/mixins.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/__pycache__/range.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/__pycache__/structures.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/accept.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/auth.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/cache_control.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/csp.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/etag.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/file_storage.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/headers.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/mixins.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/range.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/datastructures/structures.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/__init__.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/__pycache__/console.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/__pycache__/repr.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/__pycache__/tbtools.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/console.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/repr.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/shared/ICON_LICENSE.md create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/shared/console.png create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/shared/debugger.js create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/shared/less.png create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/shared/more.png create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/shared/style.css create mode 100644 venv/lib/python3.12/site-packages/werkzeug/debug/tbtools.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/formparser.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/http.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/local.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/__init__.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/__pycache__/lint.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/dispatcher.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/http_proxy.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/lint.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/profiler.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/proxy_fix.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/middleware/shared_data.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/py.typed create mode 100644 venv/lib/python3.12/site-packages/werkzeug/routing/__init__.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/routing/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/routing/__pycache__/converters.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/routing/__pycache__/exceptions.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/routing/__pycache__/map.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/routing/__pycache__/matcher.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/routing/__pycache__/rules.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/routing/converters.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/routing/exceptions.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/routing/map.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/routing/matcher.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/routing/rules.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/sansio/__init__.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/sansio/__pycache__/http.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/sansio/__pycache__/request.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/sansio/__pycache__/response.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/sansio/__pycache__/utils.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/sansio/http.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/sansio/multipart.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/sansio/request.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/sansio/response.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/sansio/utils.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/security.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/serving.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/test.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/testapp.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/urls.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/user_agent.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/utils.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/wrappers/__init__.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/wrappers/__pycache__/request.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/wrappers/__pycache__/response.cpython-312.pyc create mode 100644 venv/lib/python3.12/site-packages/werkzeug/wrappers/request.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/wrappers/response.py create mode 100644 venv/lib/python3.12/site-packages/werkzeug/wsgi.py create mode 120000 venv/lib64 create mode 100644 venv/pyvenv.cfg diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 00000000..6ddb5765 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,21 @@ +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate +import os + +db = SQLAlchemy() +migrate = Migrate() + +def create_app(): + app = Flask(__name__) + app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-secret-key') + app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///octofit.db') + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + + db.init_app(app) + migrate.init_app(app, db) + + from . import routes + app.register_blueprint(routes.bp) + + return app \ No newline at end of file diff --git a/app/models.py b/app/models.py new file mode 100644 index 00000000..cb524334 --- /dev/null +++ b/app/models.py @@ -0,0 +1,17 @@ +from . import db +from datetime import datetime + +class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True, nullable=False) + email = db.Column(db.String(120), unique=True, nullable=False) + workouts = db.relationship('Workout', backref='user', lazy=True) + +class Workout(db.Model): + id = db.Column(db.Integer, primary_key=True) + date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) + exercise = db.Column(db.String(100), nullable=False) + sets = db.Column(db.Integer, nullable=False) + reps = db.Column(db.Integer, nullable=False) + weight = db.Column(db.Float, nullable=True) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) \ No newline at end of file diff --git a/app/routes.py b/app/routes.py new file mode 100644 index 00000000..d5ffd535 --- /dev/null +++ b/app/routes.py @@ -0,0 +1,25 @@ +from flask import Blueprint, render_template, request, redirect, url_for +from . import db +from .models import User, Workout + +bp = Blueprint('main', __name__) + +@bp.route('/') +def index(): + workouts = Workout.query.all() + return render_template('index.html', workouts=workouts) + +@bp.route('/add_workout', methods=['GET', 'POST']) +def add_workout(): + if request.method == 'POST': + exercise = request.form['exercise'] + sets = int(request.form['sets']) + reps = int(request.form['reps']) + weight = float(request.form['weight']) if request.form['weight'] else None + user_id = 1 # For simplicity, assume user 1 + + workout = Workout(exercise=exercise, sets=sets, reps=reps, weight=weight, user_id=user_id) + db.session.add(workout) + db.session.commit() + return redirect(url_for('main.index')) + return render_template('add_workout.html') \ No newline at end of file diff --git a/app/static/style.css b/app/static/style.css new file mode 100644 index 00000000..6a8b5447 --- /dev/null +++ b/app/static/style.css @@ -0,0 +1,47 @@ +body { + font-family: Arial, sans-serif; + margin: 0; + padding: 0; + background-color: #f4f4f4; +} + +header { + background-color: #333; + color: white; + text-align: center; + padding: 1rem; +} + +main { + max-width: 800px; + margin: 2rem auto; + padding: 1rem; + background: white; + border-radius: 8px; + box-shadow: 0 0 10px rgba(0,0,0,0.1); +} + +form { + display: flex; + flex-direction: column; +} + +label { + margin-top: 1rem; +} + +input, button { + padding: 0.5rem; + margin-top: 0.5rem; +} + +button { + background-color: #333; + color: white; + border: none; + cursor: pointer; +} + +button:hover { + background-color: #555; +} \ No newline at end of file diff --git a/app/templates/add_workout.html b/app/templates/add_workout.html new file mode 100644 index 00000000..c327d946 --- /dev/null +++ b/app/templates/add_workout.html @@ -0,0 +1,21 @@ +{% extends 'base.html' %} + +{% block content %} +

Add Workout

+
+ +
+ + +
+ + +
+ + +
+ + +
+Back +{% endblock %} \ No newline at end of file diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 00000000..b6e2bd8e --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,17 @@ + + + + + + OctoFit Tracker + + + +
+

OctoFit Tracker

+
+
+ {% block content %}{% endblock %} +
+ + \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 00000000..0f3abe1d --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} + +{% block content %} +

Your Workouts

+ +Add Workout +{% endblock %} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..64661f08 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +Flask==2.3.3 +SQLAlchemy==2.0.23 +Flask-SQLAlchemy==3.0.5 +Flask-Migrate==4.0.5 +python-dotenv==1.0.0 \ No newline at end of file diff --git a/run.py b/run.py new file mode 100644 index 00000000..523d51a0 --- /dev/null +++ b/run.py @@ -0,0 +1,6 @@ +from app import create_app + +app = create_app() + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file diff --git a/venv/bin/Activate.ps1 b/venv/bin/Activate.ps1 new file mode 100644 index 00000000..b49d77ba --- /dev/null +++ b/venv/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/venv/bin/activate b/venv/bin/activate new file mode 100644 index 00000000..16c4bf7e --- /dev/null +++ b/venv/bin/activate @@ -0,0 +1,76 @@ +# 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 + + # Call hash to forget past locations. Without forgetting + # past locations the $PATH changes we made may not be respected. + # See "man bash" for more details. hash is usually a builtin of your shell + hash -r 2> /dev/null + + 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 + +# on Windows, a path can contain colons and backslashes and has to be converted: +case "$(uname)" in + CYGWIN*|MSYS*|MINGW*) + # transform D:\path\to\venv to /d/path/to/venv on MSYS and MINGW + # and to /cygdrive/d/path/to/venv on Cygwin + VIRTUAL_ENV=$(cygpath /workspaces/code-with-codespaces/venv) + export VIRTUAL_ENV + ;; + *) + # use the path as-is + export VIRTUAL_ENV=/workspaces/code-with-codespaces/venv + ;; +esac + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/"bin":$PATH" +export PATH + +VIRTUAL_ENV_PROMPT='(venv) ' +export VIRTUAL_ENV_PROMPT + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1="("'(venv) '") ${PS1:-}" + export PS1 +fi + +# Call hash to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +hash -r 2> /dev/null diff --git a/venv/bin/activate.csh b/venv/bin/activate.csh new file mode 100644 index 00000000..2e901058 --- /dev/null +++ b/venv/bin/activate.csh @@ -0,0 +1,27 @@ +# 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 /workspaces/code-with-codespaces/venv + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/"bin":$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = '(venv) '"$prompt" + setenv VIRTUAL_ENV_PROMPT '(venv) ' +endif + +alias pydoc python -m pydoc + +rehash diff --git a/venv/bin/activate.fish b/venv/bin/activate.fish new file mode 100644 index 00000000..421b469d --- /dev/null +++ b/venv/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 /workspaces/code-with-codespaces/venv + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/"bin $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) '(venv) ' (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT '(venv) ' +end diff --git a/venv/bin/alembic b/venv/bin/alembic new file mode 100755 index 00000000..4f30ba76 --- /dev/null +++ b/venv/bin/alembic @@ -0,0 +1,8 @@ +#!/workspaces/code-with-codespaces/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from alembic.config import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/dotenv b/venv/bin/dotenv new file mode 100755 index 00000000..6c7af169 --- /dev/null +++ b/venv/bin/dotenv @@ -0,0 +1,8 @@ +#!/workspaces/code-with-codespaces/venv/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/venv/bin/flask b/venv/bin/flask new file mode 100755 index 00000000..bcd3a46b --- /dev/null +++ b/venv/bin/flask @@ -0,0 +1,8 @@ +#!/workspaces/code-with-codespaces/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from flask.cli import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/mako-render b/venv/bin/mako-render new file mode 100755 index 00000000..09aa9ce2 --- /dev/null +++ b/venv/bin/mako-render @@ -0,0 +1,8 @@ +#!/workspaces/code-with-codespaces/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from mako.cmd import cmdline +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(cmdline()) diff --git a/venv/bin/pip b/venv/bin/pip new file mode 100755 index 00000000..2b7d1756 --- /dev/null +++ b/venv/bin/pip @@ -0,0 +1,8 @@ +#!/workspaces/code-with-codespaces/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip3 b/venv/bin/pip3 new file mode 100755 index 00000000..2b7d1756 --- /dev/null +++ b/venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/workspaces/code-with-codespaces/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/pip3.12 b/venv/bin/pip3.12 new file mode 100755 index 00000000..2b7d1756 --- /dev/null +++ b/venv/bin/pip3.12 @@ -0,0 +1,8 @@ +#!/workspaces/code-with-codespaces/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/python b/venv/bin/python new file mode 120000 index 00000000..b8a0adbb --- /dev/null +++ b/venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/venv/bin/python3 b/venv/bin/python3 new file mode 120000 index 00000000..ae65fdaa --- /dev/null +++ b/venv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/venv/bin/python3.12 b/venv/bin/python3.12 new file mode 120000 index 00000000..b8a0adbb --- /dev/null +++ b/venv/bin/python3.12 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/venv/include/site/python3.12/greenlet/greenlet.h b/venv/include/site/python3.12/greenlet/greenlet.h new file mode 100644 index 00000000..d02a16e4 --- /dev/null +++ b/venv/include/site/python3.12/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/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/LICENSE b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/LICENSE new file mode 100644 index 00000000..2448fd26 --- /dev/null +++ b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Miguel Grinberg + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/METADATA b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/METADATA new file mode 100644 index 00000000..0590a4cf --- /dev/null +++ b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/METADATA @@ -0,0 +1,86 @@ +Metadata-Version: 2.1 +Name: Flask-Migrate +Version: 4.0.5 +Summary: SQLAlchemy database migrations for Flask applications using Alembic. +Home-page: https://github.com/miguelgrinberg/flask-migrate +Author: Miguel Grinberg +Author-email: miguel.grinberg@gmail.com +License: MIT +Project-URL: Bug Tracker, https://github.com/miguelgrinberg/flask-migrate/issues +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: Flask >=0.9 +Requires-Dist: Flask-SQLAlchemy >=1.0 +Requires-Dist: alembic >=1.9.0 + +Flask-Migrate +============= + +[![Build status](https://github.com/miguelgrinberg/flask-migrate/workflows/build/badge.svg)](https://github.com/miguelgrinberg/flask-migrate/actions) + +Flask-Migrate is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic. The database operations are provided as command-line arguments under the `flask db` command. + +Installation +------------ + +Install Flask-Migrate with `pip`: + + pip install Flask-Migrate + +Example +------- + +This is an example application that handles database migrations through Flask-Migrate: + +```python +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate + +app = Flask(__name__) +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db' + +db = SQLAlchemy(app) +migrate = Migrate(app, db) + +class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(128)) +``` + +With the above application you can create the database or enable migrations if the database already exists with the following command: + + $ flask db init + +Note that the `FLASK_APP` environment variable must be set according to the Flask documentation for this command to work. This will add a `migrations` folder to your application. The contents of this folder need to be added to version control along with your other source files. + +You can then generate an initial migration: + + $ flask db migrate + +The migration script needs to be reviewed and edited, as Alembic currently does not detect every change you make to your models. In particular, Alembic is currently unable to detect indexes. Once finalized, the migration script also needs to be added to version control. + +Then you can apply the migration to the database: + + $ flask db upgrade + +Then each time the database models change repeat the `migrate` and `upgrade` commands. + +To sync the database in another system just refresh the `migrations` folder from source control and run the `upgrade` command. + +To see all the commands that are available run this command: + + $ flask db --help + +Resources +--------- + +- [Documentation](http://flask-migrate.readthedocs.io/en/latest/) +- [pypi](https://pypi.python.org/pypi/Flask-Migrate) +- [Change Log](https://github.com/miguelgrinberg/Flask-Migrate/blob/master/CHANGES.md) diff --git a/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/RECORD b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/RECORD new file mode 100644 index 00000000..07a0f4b8 --- /dev/null +++ b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/RECORD @@ -0,0 +1,31 @@ +Flask_Migrate-4.0.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask_Migrate-4.0.5.dist-info/LICENSE,sha256=kfkXGlJQvKy3Y__6tAJ8ynIp1HQfeROXhL8jZU1d-DI,1082 +Flask_Migrate-4.0.5.dist-info/METADATA,sha256=d-EcnhZa_vyVAph2u84OpGIteJaBmqLQxO5Rf6wUI7Y,3095 +Flask_Migrate-4.0.5.dist-info/RECORD,, +Flask_Migrate-4.0.5.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Flask_Migrate-4.0.5.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92 +Flask_Migrate-4.0.5.dist-info/top_level.txt,sha256=jLoPgiMG6oR4ugNteXn3IHskVVIyIXVStZOVq-AWLdU,14 +flask_migrate/__init__.py,sha256=-JFdExGtr7UrwCpmjYvTfzFHqMjE7AmP0Rr3T53tBNU,10037 +flask_migrate/__pycache__/__init__.cpython-312.pyc,, +flask_migrate/__pycache__/cli.cpython-312.pyc,, +flask_migrate/cli.py,sha256=H-N4NNS5HyEB61HpUADLU8pW3naejyDPgeEbzEqG5-w,10298 +flask_migrate/templates/aioflask-multidb/README,sha256=Ek4cJqTaxneVjtkue--BXMlfpfp3MmJRjqoZvnSizww,43 +flask_migrate/templates/aioflask-multidb/__pycache__/env.cpython-312.pyc,, +flask_migrate/templates/aioflask-multidb/alembic.ini.mako,sha256=SjYEmJKzz6K8QfuZWtLJAJWcCKOdRbfUhsVlpgv8ock,857 +flask_migrate/templates/aioflask-multidb/env.py,sha256=UcjeqkAbyUjTkuQFmCFPG7QOvqhco8-uGp8QEbto0T8,6573 +flask_migrate/templates/aioflask-multidb/script.py.mako,sha256=198VPxVEN3NZ3vHcRuCxSoI4XnOYirGWt01qkbPKoJw,1246 +flask_migrate/templates/aioflask/README,sha256=KKqWGl4YC2RqdOdq-y6quTDW0b7D_UZNHuM8glM1L-c,44 +flask_migrate/templates/aioflask/__pycache__/env.cpython-312.pyc,, +flask_migrate/templates/aioflask/alembic.ini.mako,sha256=SjYEmJKzz6K8QfuZWtLJAJWcCKOdRbfUhsVlpgv8ock,857 +flask_migrate/templates/aioflask/env.py,sha256=m6ZtBhdpwuq89vVeLTWmNT-1NfJZqarC_hsquCdR9bw,3478 +flask_migrate/templates/aioflask/script.py.mako,sha256=8_xgA-gm_OhehnO7CiIijWgnm00ZlszEHtIHrAYFJl0,494 +flask_migrate/templates/flask-multidb/README,sha256=AfiP5foaV2odZxXxuUuSIS6YhkIpR7CsOo2mpuxwHdc,40 +flask_migrate/templates/flask-multidb/__pycache__/env.cpython-312.pyc,, +flask_migrate/templates/flask-multidb/alembic.ini.mako,sha256=SjYEmJKzz6K8QfuZWtLJAJWcCKOdRbfUhsVlpgv8ock,857 +flask_migrate/templates/flask-multidb/env.py,sha256=F44iqsAxLTVBN_zD8CMUkdE7Aub4niHMmo5wl9mY4Uw,6190 +flask_migrate/templates/flask-multidb/script.py.mako,sha256=198VPxVEN3NZ3vHcRuCxSoI4XnOYirGWt01qkbPKoJw,1246 +flask_migrate/templates/flask/README,sha256=JL0NrjOrscPcKgRmQh1R3hlv1_rohDot0TvpmdM27Jk,41 +flask_migrate/templates/flask/__pycache__/env.cpython-312.pyc,, +flask_migrate/templates/flask/alembic.ini.mako,sha256=SjYEmJKzz6K8QfuZWtLJAJWcCKOdRbfUhsVlpgv8ock,857 +flask_migrate/templates/flask/env.py,sha256=ibK1hsdOsOBzXNU2yQoAIza7f_EFzaVSWwON_NSpNzQ,3344 +flask_migrate/templates/flask/script.py.mako,sha256=8_xgA-gm_OhehnO7CiIijWgnm00ZlszEHtIHrAYFJl0,494 diff --git a/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/REQUESTED b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/WHEEL b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/WHEEL new file mode 100644 index 00000000..7e688737 --- /dev/null +++ b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.41.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/top_level.txt new file mode 100644 index 00000000..0652762c --- /dev/null +++ b/venv/lib/python3.12/site-packages/Flask_Migrate-4.0.5.dist-info/top_level.txt @@ -0,0 +1 @@ +flask_migrate diff --git a/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/LICENSE b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/LICENSE new file mode 100644 index 00000000..7bf9bbe9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright 2005-2023 SQLAlchemy authors and contributors . + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/METADATA b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/METADATA new file mode 100644 index 00000000..9f2808f0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/METADATA @@ -0,0 +1,241 @@ +Metadata-Version: 2.1 +Name: SQLAlchemy +Version: 2.0.23 +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 :: 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.2.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/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/RECORD b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/RECORD new file mode 100644 index 00000000..32301068 --- /dev/null +++ b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/RECORD @@ -0,0 +1,530 @@ +SQLAlchemy-2.0.23.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +SQLAlchemy-2.0.23.dist-info/LICENSE,sha256=2lSTeluT1aC-5eJXO8vhkzf93qCSeV_mFXLrv3tNdIU,1100 +SQLAlchemy-2.0.23.dist-info/METADATA,sha256=znDChLueFNPCOPuNix-FfY7FG6aQOCM-lQwwN-cPLQs,9551 +SQLAlchemy-2.0.23.dist-info/RECORD,, +SQLAlchemy-2.0.23.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +SQLAlchemy-2.0.23.dist-info/WHEEL,sha256=ABfLmba86pDGRdQWLpjqBW4L2FK9_PTmdDC9w-KM8dE,113 +SQLAlchemy-2.0.23.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11 +sqlalchemy/__init__.py,sha256=DjKCAltzrHGfaVdXVeFJpBmTaX6JmyloHANzewBUWo4,12708 +sqlalchemy/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/__pycache__/events.cpython-312.pyc,, +sqlalchemy/__pycache__/exc.cpython-312.pyc,, +sqlalchemy/__pycache__/inspection.cpython-312.pyc,, +sqlalchemy/__pycache__/log.cpython-312.pyc,, +sqlalchemy/__pycache__/schema.cpython-312.pyc,, +sqlalchemy/__pycache__/types.cpython-312.pyc,, +sqlalchemy/connectors/__init__.py,sha256=uKUYWQoXyleIyjWBuh7gzgnazJokx3DaasKJbFOfQGA,476 +sqlalchemy/connectors/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/connectors/__pycache__/aioodbc.cpython-312.pyc,, +sqlalchemy/connectors/__pycache__/asyncio.cpython-312.pyc,, +sqlalchemy/connectors/__pycache__/pyodbc.cpython-312.pyc,, +sqlalchemy/connectors/aioodbc.py,sha256=QiafuN9bx_wcIs8tByLftTmGAegXPoFPwUaxCDU_ZQA,5737 +sqlalchemy/connectors/asyncio.py,sha256=ZZmJSFT50u-GEjZzytQOdB_tkBFxi3XPWRrNhs_nASc,6139 +sqlalchemy/connectors/pyodbc.py,sha256=NskMydn26ZkHL8aQ1V3L4WIAWin3zwJ5VEnlHvAD1DE,8453 +sqlalchemy/cyextension/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sqlalchemy/cyextension/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/cyextension/collections.cpython-312-x86_64-linux-musl.so,sha256=zkTcRjQllDv_lV7M-JfqQftJbk4pD8OCJPDAB5RJ59g,1933152 +sqlalchemy/cyextension/collections.pyx,sha256=KDI5QTOyYz9gDl-3d7MbGMA0Kc-wxpJqnLmCaUmQy2U,12323 +sqlalchemy/cyextension/immutabledict.cpython-312-x86_64-linux-musl.so,sha256=OMxFFyR350iFpgycJdOYpJ5KfAD-kEBpdQXmwOiu8GE,809896 +sqlalchemy/cyextension/immutabledict.pxd,sha256=oc8BbnQwDg7pWAdThB-fzu8s9_ViOe1Ds-8T0r0POjI,41 +sqlalchemy/cyextension/immutabledict.pyx,sha256=aQJPZKjcqbO8jHDqpC9F-v-ew2qAjUscc5CntaheZUk,3285 +sqlalchemy/cyextension/processors.cpython-312-x86_64-linux-musl.so,sha256=ZhsBZRrn0HjnXa1lhF_VsWkZGdx8WG4NUtaY68sa1zo,542264 +sqlalchemy/cyextension/processors.pyx,sha256=0swFIBdR19x1kPRe-dijBaLW898AhH6QJizbv4ho9pk,1545 +sqlalchemy/cyextension/resultproxy.cpython-312-x86_64-linux-musl.so,sha256=PXa8-5TGLS9NEmxIDihHDVTQcHvloSU3mJmavF3vtI0,622192 +sqlalchemy/cyextension/resultproxy.pyx,sha256=cDtMjLTdC47g7cME369NSOCck3JwG2jwZ6j25no3_gw,2477 +sqlalchemy/cyextension/util.cpython-312-x86_64-linux-musl.so,sha256=O6H-HEPVLr_OQvx5alV-IoVp37ytwP_lja_t6QzGCeE,961048 +sqlalchemy/cyextension/util.pyx,sha256=lv03p63oVn23jLhMI4_RYGewUnJfh-4FkrNMEFL7A3Y,2289 +sqlalchemy/dialects/__init__.py,sha256=hLsgIEomunlp4mNLnvjCQTLOnBVva8N7IT2-RYrN2_4,1770 +sqlalchemy/dialects/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/dialects/__pycache__/_typing.cpython-312.pyc,, +sqlalchemy/dialects/_typing.py,sha256=P2ML2o4b_bWAAy3zbdoUjx3vXsMNwpiOblef8ThCxlM,648 +sqlalchemy/dialects/mssql/__init__.py,sha256=CYbbydyMSLjUq8vY1siNStd4lvjVXod8ddeDS6ELHLk,1871 +sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/dialects/mssql/__pycache__/aioodbc.cpython-312.pyc,, +sqlalchemy/dialects/mssql/__pycache__/base.cpython-312.pyc,, +sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-312.pyc,, +sqlalchemy/dialects/mssql/__pycache__/json.cpython-312.pyc,, +sqlalchemy/dialects/mssql/__pycache__/provision.cpython-312.pyc,, +sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-312.pyc,, +sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-312.pyc,, +sqlalchemy/dialects/mssql/aioodbc.py,sha256=ncj3yyfvW91o3g19GB5s1I0oaZKUO_P-R2nwnLF0t9E,2013 +sqlalchemy/dialects/mssql/base.py,sha256=l9vX6fK6DJEYA00N9uDnvSbqfgvxXfYUn2C4AF5T920,133649 +sqlalchemy/dialects/mssql/information_schema.py,sha256=ll0zAupJ4cPvhi9v5hTi7PQLU1lae4o6eQ5Vg7gykXQ,8074 +sqlalchemy/dialects/mssql/json.py,sha256=B0m6H08CKuk-yomDHcCwfQbVuVN2WLufuVueA_qb1NQ,4573 +sqlalchemy/dialects/mssql/provision.py,sha256=x7XRSQDxz4jz2uIpqwhuIXpL9bic0Vw7Mhy39HOkyqY,5013 +sqlalchemy/dialects/mssql/pymssql.py,sha256=BfJp9t-IQabqWXySJBmP9pwNTWnJqbjA2jJM9M4XeWc,4029 +sqlalchemy/dialects/mssql/pyodbc.py,sha256=qwZ8ByOTZ1WObjxeOravoJBSBX-s4RJ_PZ5VJ_Ch5Ws,27048 +sqlalchemy/dialects/mysql/__init__.py,sha256=btLABiNnmbWt9ziW-XgVWEB1qHWQcSFz7zxZNw4m_LY,2144 +sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/asyncmy.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/base.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/dml.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/expression.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/json.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/provision.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/reserved_words.cpython-312.pyc,, +sqlalchemy/dialects/mysql/__pycache__/types.cpython-312.pyc,, +sqlalchemy/dialects/mysql/aiomysql.py,sha256=Zb-_F9Pzl0t-fT1bZwbNNne6jjCUqBXxeizbhMFPqls,9750 +sqlalchemy/dialects/mysql/asyncmy.py,sha256=zqupDz7AJihjv3E8w_4XAtq95d8stdrETNx60MLNVr0,9819 +sqlalchemy/dialects/mysql/base.py,sha256=q-DzkR_txwDTeWTEByzHAoIArYU3Bb5HT2Bnmuw7WIM,120688 +sqlalchemy/dialects/mysql/cymysql.py,sha256=5CQVJAlqQ3pT4IDGSQJH2hCzj-EWjUitA21MLqJwEEs,2291 +sqlalchemy/dialects/mysql/dml.py,sha256=qw0ZweHbMsbNyVSfC17HqylCnf7XAuIjtgofiWABT8k,7636 +sqlalchemy/dialects/mysql/enumerated.py,sha256=1L2J2wT6nQEmRS4z-jzZpoi44IqIaHgBRZZB9m55czo,8439 +sqlalchemy/dialects/mysql/expression.py,sha256=WW5G2XPwqJfXjuzHBt4BRP0pCLcPJkPD1mvZX1g0JL0,4066 +sqlalchemy/dialects/mysql/json.py,sha256=JlSFBAHhJ9JmV-3azH80xkLgeh7g6A6DVyNVCNZiKPU,2260 +sqlalchemy/dialects/mysql/mariadb.py,sha256=Sugyngvo6j6SfFFuJ23rYeFWEPdZ9Ji9guElsk_1WSQ,844 +sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=F1VPosecC1hDZqjzZI29j4GUduyU4ewPwb-ekBQva5w,8725 +sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=5glmkPhD_KP-Mci8ZXBr4yzqH1MDfzCJ9F_kZNyXcGo,5666 +sqlalchemy/dialects/mysql/mysqldb.py,sha256=R5BDiXiHX5oFuAOzyxZ6TYUTGzly-dulMeQLkeia6kk,9649 +sqlalchemy/dialects/mysql/provision.py,sha256=uPT6-BIoP_12XLmWAza1TDFNhOVVJ3rmQoMH7nvh-Vg,3226 +sqlalchemy/dialects/mysql/pymysql.py,sha256=d2-00IPoyEDkR9REQTE-DGEQrGshUq_0G5liZ5FiSEM,4032 +sqlalchemy/dialects/mysql/pyodbc.py,sha256=mkOvumrxpmAi6noZlkaTVKz2F7G5vLh2vx0cZSn9VTA,4288 +sqlalchemy/dialects/mysql/reflection.py,sha256=ak6E-eCP9346ixnILYNJcrRYblWbIT0sjXf4EqmfBsY,22556 +sqlalchemy/dialects/mysql/reserved_words.py,sha256=DsPHsW3vwOrvU7bv3Nbfact2Z_jyZ9xUTT-mdeQvqxo,9145 +sqlalchemy/dialects/mysql/types.py,sha256=i8DpRkOL1QhPErZ25AmCQOuFLciWhdjNL3I0CeHEhdY,24258 +sqlalchemy/dialects/oracle/__init__.py,sha256=pjk1aWi9XFCAHWNSJzSzmoIcL32-AkU_1J9IV4PtwpA,1318 +sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/dialects/oracle/__pycache__/base.cpython-312.pyc,, +sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-312.pyc,, +sqlalchemy/dialects/oracle/__pycache__/dictionary.cpython-312.pyc,, +sqlalchemy/dialects/oracle/__pycache__/oracledb.cpython-312.pyc,, +sqlalchemy/dialects/oracle/__pycache__/provision.cpython-312.pyc,, +sqlalchemy/dialects/oracle/__pycache__/types.cpython-312.pyc,, +sqlalchemy/dialects/oracle/base.py,sha256=u55_R9NrCRijud7ioHMxT-r0MSW0gMFjOwbrDdPgFsc,118036 +sqlalchemy/dialects/oracle/cx_oracle.py,sha256=L0GvcB6xb0-zyv5dx3bpQCeptp0KSqH6g9FUQ4y-d-g,55108 +sqlalchemy/dialects/oracle/dictionary.py,sha256=iUoyFEFM8z0sfVWR2n_nnre14kaQkV_syKO0R5Dos4M,19487 +sqlalchemy/dialects/oracle/oracledb.py,sha256=_-fUQ94xai80B7v9WLVGoGDIv8u54nVspBdyGEyI76g,3457 +sqlalchemy/dialects/oracle/provision.py,sha256=5cvIc3yTWxz4AIRYxcesbRJ1Ft-zT9GauQ911yPnN2o,8055 +sqlalchemy/dialects/oracle/types.py,sha256=TeOhUW5W9qZC8SaJ-9b3u6OvOPOarNq4MmCQ7l3wWX0,8204 +sqlalchemy/dialects/postgresql/__init__.py,sha256=bZEPsLbRtB7s6TMQAHCIzKBgkxUa3eDXvCkeARua37E,3734 +sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/_psycopg_common.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/array.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/base.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/json.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/named_types.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/operators.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pg_catalog.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/psycopg.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/types.cpython-312.pyc,, +sqlalchemy/dialects/postgresql/_psycopg_common.py,sha256=U3aWzbKD3VOj6Z6r-4IsIQmtjGGIB4RDZH6NXfd8Xz0,5655 +sqlalchemy/dialects/postgresql/array.py,sha256=tLyU9GDAeIypNhjTuFQUYbaTeijVM1VVJS6UdzzXXn4,13682 +sqlalchemy/dialects/postgresql/asyncpg.py,sha256=XNaoOZ5Da4-jUTaES1zEOTEW3WG8UKyVCoIS3LsFhzE,39967 +sqlalchemy/dialects/postgresql/base.py,sha256=DGhaquFJWDQL7wIvQ2EE57LxD7zGR06BKQxvNZHFLgY,175634 +sqlalchemy/dialects/postgresql/dml.py,sha256=_He69efdpDA5gGmBsE7Lo4ViSi3QnR38BiFmrR1tw6k,11203 +sqlalchemy/dialects/postgresql/ext.py,sha256=oPP22Pq-n2lMmQ8ahifYmsmzRhSiSv1RV-xrTT0gycw,16253 +sqlalchemy/dialects/postgresql/hstore.py,sha256=q5x0npbAMI8cdRFGTMwLoWFj9P1G9DUkw5OEUCfTXpI,11532 +sqlalchemy/dialects/postgresql/json.py,sha256=panGtnEbcirQDy4yR2huWydFqa_Kmv8xhpLyf-SSRWE,11203 +sqlalchemy/dialects/postgresql/named_types.py,sha256=zNoHsP3nVq5xxA7SOQ6LLDwYZEHFciZ-nDjw_I9f_G0,17092 +sqlalchemy/dialects/postgresql/operators.py,sha256=MB40xq1124OnhUzkvtbnTmxEiey0VxMOYyznF96wwhI,2799 +sqlalchemy/dialects/postgresql/pg8000.py,sha256=w6pJ3LaIKWmnwvB0Pr1aTJX5OKNtG5RNClVfkE019vU,18620 +sqlalchemy/dialects/postgresql/pg_catalog.py,sha256=0lLnIgxfCrqkx_LNijMxo0trNLsodcd8KwretZIj4uM,8875 +sqlalchemy/dialects/postgresql/provision.py,sha256=oxyAzs8_PhuK0ChivXC3l2Nldih3_HKffvGsZqD8XWI,5509 +sqlalchemy/dialects/postgresql/psycopg.py,sha256=YMubzQHMYN1By8QJScIPb_PwNiACv6srddQ6nX6WltQ,22238 +sqlalchemy/dialects/postgresql/psycopg2.py,sha256=3Xci4bTA2BvhrZAQa727uFWdaXEZmvfD-Z-upE3NyQE,31592 +sqlalchemy/dialects/postgresql/psycopg2cffi.py,sha256=2EOuDwBetfvelcPoTzSwOHe6X8lTwaYH7znNzXJt9eM,1739 +sqlalchemy/dialects/postgresql/ranges.py,sha256=yHB1BRlUreQPZB3VEn0KMMLf02zjf5jjYdmg4N4S2Sw,30220 +sqlalchemy/dialects/postgresql/types.py,sha256=l24rs8_nK4vqLyQC0aUkf4S7ecw6T_7Pgq50Icc5CBs,7292 +sqlalchemy/dialects/sqlite/__init__.py,sha256=wnZ9vtfm0QXmth1jiGiubFgRiKxIoQoNthb1bp4FhCs,1173 +sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-312.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/base.cpython-312.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-312.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/json.cpython-312.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-312.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-312.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-312.pyc,, +sqlalchemy/dialects/sqlite/aiosqlite.py,sha256=GZJioZLot0D5CQ6ovPQoqv2iV8FAFm3G75lEFCzopoE,12296 +sqlalchemy/dialects/sqlite/base.py,sha256=YYEB5BeuemLC3FAR7EB8vA0zoUOwHTKoF_srvnAStps,96785 +sqlalchemy/dialects/sqlite/dml.py,sha256=PYESBj8Ip7bGs_Fi7QjbWLXLnU9a-SbP96JZiUoZNHg,8434 +sqlalchemy/dialects/sqlite/json.py,sha256=XFPwSdNx0DxDfxDZn7rmGGqsAgL4vpJbjjGaA73WruQ,2533 +sqlalchemy/dialects/sqlite/provision.py,sha256=O4JDoybdb2RBblXErEVPE2P_5xHab927BQItJa203zU,5383 +sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=_JuOCoic--ehAGkCgnwUUKKTs6xYoBGag4Y_WkQUDwU,5347 +sqlalchemy/dialects/sqlite/pysqlite.py,sha256=xBg6DKqvml5cCGxVSAQxR1dcMvso8q4uyXs2m4WLzz0,27891 +sqlalchemy/dialects/type_migration_guidelines.txt,sha256=-uHNdmYFGB7bzUNT6i8M5nb4j6j9YUKAtW4lcBZqsMg,8239 +sqlalchemy/engine/__init__.py,sha256=fJCAl5P7JH9iwjuWo72_3LOIzWWhTnvXqzpAmm_T0fY,2818 +sqlalchemy/engine/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/_py_processors.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/_py_row.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/_py_util.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/base.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/characteristics.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/create.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/cursor.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/default.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/events.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/interfaces.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/mock.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/processors.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/reflection.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/result.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/row.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/strategies.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/url.cpython-312.pyc,, +sqlalchemy/engine/__pycache__/util.cpython-312.pyc,, +sqlalchemy/engine/_py_processors.py,sha256=RSVKm9YppSBDSCEi8xvbZdRCP9EsCYfbyEg9iDCMCiI,3744 +sqlalchemy/engine/_py_row.py,sha256=Zdta0JGa7V2aV04L7nzXUEp-H1gpresKyBlneQu60pk,3549 +sqlalchemy/engine/_py_util.py,sha256=5m3MZbEqnUwP5kK_ghisFpzcXgBwSxTSkBEFB6afiD8,2245 +sqlalchemy/engine/base.py,sha256=RbIfWZ1Otyb4VzMYjDpK5BiDIE8QZwa4vQgRX0yCa28,122246 +sqlalchemy/engine/characteristics.py,sha256=YvMgrUVAt3wsSiQ0K8l44yBjFlMK3MGajxhg50t5yFM,2344 +sqlalchemy/engine/create.py,sha256=8372TLpy4FOAIZ9WmuNkx1v9DPgwpoCAH9P7LNXZCwY,32629 +sqlalchemy/engine/cursor.py,sha256=6e1Tp63r0Kt-P4pEaYR7wUew2aClTdKAEI-FoAAxJxE,74405 +sqlalchemy/engine/default.py,sha256=bi--ytxYJ0EtsCudl38owGtytnwTHX-PjlsYTFe8LpA,84065 +sqlalchemy/engine/events.py,sha256=PQyc_sbmqks6pqyN7xitO658KdKzzJWfW1TKYwEd5vo,37392 +sqlalchemy/engine/interfaces.py,sha256=pAFYR15f1Z_-qdzTYI4mAm8IYbD6maLBKbG3pBaJ8Us,112824 +sqlalchemy/engine/mock.py,sha256=ki4ud7YrUrzP2katdkxlJGFUKB2kS7cZZAHK5xWsNF8,4179 +sqlalchemy/engine/processors.py,sha256=ENN6XwndxJPW-aXPu_3NzAZsy5SvNznHoa1Qn29ERAw,2383 +sqlalchemy/engine/reflection.py,sha256=2aakNheQJNMUXZbhY8s1NtqGoGWTxM2THkJlMMfiX_s,75125 +sqlalchemy/engine/result.py,sha256=shRAsboHPTvKR38ryGgC4KLcUeVTbABSlWzAfOUKVZs,77841 +sqlalchemy/engine/row.py,sha256=doiXKaUI6s6OkfqPIwNyTPLllxJfR8HYgEI8ve9VYe0,11955 +sqlalchemy/engine/strategies.py,sha256=HjCj_FHQOgkkhhtnVmcOEuHI_cftNo3P0hN5zkhZvDc,442 +sqlalchemy/engine/url.py,sha256=_WNE7ia0JIPRc1PLY_jSA3F7bB5kp1gzuzkc5eoKviA,30694 +sqlalchemy/engine/util.py,sha256=3-ENI9S-3KLWr0GW27uWQfsvCJwMBGTKbykkKPUgiAE,5667 +sqlalchemy/event/__init__.py,sha256=CSBMp0yu5joTC6tWvx40B4p87N7oGKxC-ZLx2ULKUnQ,997 +sqlalchemy/event/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/event/__pycache__/api.cpython-312.pyc,, +sqlalchemy/event/__pycache__/attr.cpython-312.pyc,, +sqlalchemy/event/__pycache__/base.cpython-312.pyc,, +sqlalchemy/event/__pycache__/legacy.cpython-312.pyc,, +sqlalchemy/event/__pycache__/registry.cpython-312.pyc,, +sqlalchemy/event/api.py,sha256=nQAvPK1jrLpmu8aKCUtc-vYWcIuG-1FgAtp3GRkfIiI,8227 +sqlalchemy/event/attr.py,sha256=NMe_sPQTju2PE-f68C8TcKJGW-Gxyi1CLXumAmE368Y,20438 +sqlalchemy/event/base.py,sha256=Cr_PNJlCYJSU3rtT8DkplyjBRb-E2Wa3OAeK9woFJkk,14980 +sqlalchemy/event/legacy.py,sha256=OpPqE64xk1OYjLW1scvc6iijhoa5GZJt5f7-beWhgOc,8211 +sqlalchemy/event/registry.py,sha256=Zig9q2Galo8kO2aqr7a2rNAhmIkdJ-ntHSEcM5MfSgw,10833 +sqlalchemy/events.py,sha256=pRcPKKsPQHGPH_pvTtKRmzuEIy-QHCtkUiZl4MUbxKs,536 +sqlalchemy/exc.py,sha256=4SMKOJtz7_SWt5vskCSeXSi4ZlFyL4jh53Q8sk4-ODQ,24011 +sqlalchemy/ext/__init__.py,sha256=w4h7EpXjKPr0LD4yHa0pDCfrvleU3rrX7mgyb8RuDYQ,322 +sqlalchemy/ext/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/ext/__pycache__/associationproxy.cpython-312.pyc,, +sqlalchemy/ext/__pycache__/automap.cpython-312.pyc,, +sqlalchemy/ext/__pycache__/baked.cpython-312.pyc,, +sqlalchemy/ext/__pycache__/compiler.cpython-312.pyc,, +sqlalchemy/ext/__pycache__/horizontal_shard.cpython-312.pyc,, +sqlalchemy/ext/__pycache__/hybrid.cpython-312.pyc,, +sqlalchemy/ext/__pycache__/indexable.cpython-312.pyc,, +sqlalchemy/ext/__pycache__/instrumentation.cpython-312.pyc,, +sqlalchemy/ext/__pycache__/mutable.cpython-312.pyc,, +sqlalchemy/ext/__pycache__/orderinglist.cpython-312.pyc,, +sqlalchemy/ext/__pycache__/serializer.cpython-312.pyc,, +sqlalchemy/ext/associationproxy.py,sha256=5voNXWIJYGt6c8mwuSA6alm3SmEHOZ-CVK8ikgfzk8s,65960 +sqlalchemy/ext/asyncio/__init__.py,sha256=iG_0TmBO1pCB316WS-p17AImwqRtUoaKo7UphYZ7bYw,1317 +sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/ext/asyncio/__pycache__/base.cpython-312.pyc,, +sqlalchemy/ext/asyncio/__pycache__/engine.cpython-312.pyc,, +sqlalchemy/ext/asyncio/__pycache__/exc.cpython-312.pyc,, +sqlalchemy/ext/asyncio/__pycache__/result.cpython-312.pyc,, +sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-312.pyc,, +sqlalchemy/ext/asyncio/__pycache__/session.cpython-312.pyc,, +sqlalchemy/ext/asyncio/base.py,sha256=PXF4YqfRi2-mADAtaL2_-Uv7CzoBVojPbzyA5phJ9To,8959 +sqlalchemy/ext/asyncio/engine.py,sha256=h4pe3ixuX6YfI97B5QWo2V4_CCCnOvM_EHPZhX19Mgc,47796 +sqlalchemy/ext/asyncio/exc.py,sha256=1hCdOKzvSryc_YE4jgj0l9JASOmZXutdzShEYPiLbGI,639 +sqlalchemy/ext/asyncio/result.py,sha256=zETerVB53gql1DL6tkO_JiqeU-m1OM-8kX0ULxmoL_I,30554 +sqlalchemy/ext/asyncio/scoping.py,sha256=cBNluB7n_lwdAAo6pySbvNRqPN7UBzwQHZ6XhRDyWgA,52685 +sqlalchemy/ext/asyncio/session.py,sha256=yWwhI5i_yVWjykxmxkcP3-xmw3UpoGYNhHZL8sYXQMA,62998 +sqlalchemy/ext/automap.py,sha256=7p13-VpN0MOM525r7pmEnftedya9l5G-Ei_cFXZfpTc,61431 +sqlalchemy/ext/baked.py,sha256=R8ZAxiVN6eH50AJu0O3TtFXNE1tnRkMlSj3AvkcWFhY,17818 +sqlalchemy/ext/compiler.py,sha256=h7eR0NcPJ4F_k8YGRP3R9YX75Y9pgiVxoCjRyvceF7g,20391 +sqlalchemy/ext/declarative/__init__.py,sha256=VJu8S1efxil20W48fJlpDn6gHorOudn5p3-lF72WcJ8,1818 +sqlalchemy/ext/declarative/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/ext/declarative/__pycache__/extensions.cpython-312.pyc,, +sqlalchemy/ext/declarative/extensions.py,sha256=vwZjudPFA_mao1U04-RZCaU_tvPMBgQa5OTmSI7K7SU,19547 +sqlalchemy/ext/horizontal_shard.py,sha256=eh14W8QWHYH22PL1l5qF_ad9Fyh1WAFjKi_vNfsme94,16766 +sqlalchemy/ext/hybrid.py,sha256=98D72WBmlileYBtEKMSNF9l-bwRavThSV8-LyB2gjo0,52499 +sqlalchemy/ext/indexable.py,sha256=RkG9BKwil-TqDjVBM14ML9c-geUrHxtRKpYkSJEwGHA,11028 +sqlalchemy/ext/instrumentation.py,sha256=rjjSbTGilYeGLdyEWV932TfTaGxiVP44_RajinANk54,15723 +sqlalchemy/ext/mutable.py,sha256=d3Pp8PcAVN4pHN9rhc1ReXBWe0Q70Q5S1klFoYGyDPA,37393 +sqlalchemy/ext/mypy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sqlalchemy/ext/mypy/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/ext/mypy/__pycache__/apply.cpython-312.pyc,, +sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-312.pyc,, +sqlalchemy/ext/mypy/__pycache__/infer.cpython-312.pyc,, +sqlalchemy/ext/mypy/__pycache__/names.cpython-312.pyc,, +sqlalchemy/ext/mypy/__pycache__/plugin.cpython-312.pyc,, +sqlalchemy/ext/mypy/__pycache__/util.cpython-312.pyc,, +sqlalchemy/ext/mypy/apply.py,sha256=uUES4grydYtKykLKlxzJeBXeGe8kfWou9_rzEyEkfp0,10503 +sqlalchemy/ext/mypy/decl_class.py,sha256=Ls2Efh4kEhle6Z4VMz0GRBgGQTYs2fHr5b4DfuDj44c,17377 +sqlalchemy/ext/mypy/infer.py,sha256=si720RW6iGxMRZNP5tcaIxA1_ehFp215TzxVXaLjglU,19364 +sqlalchemy/ext/mypy/names.py,sha256=tch4f5fDmdv4AWWFzXgGZdCpxmae59XRPT02KyMvrEI,10625 +sqlalchemy/ext/mypy/plugin.py,sha256=fLXDukvZqbJ0JJCOoyZAuOniYZ_F1YT-l9gKppu8SEs,9750 +sqlalchemy/ext/mypy/util.py,sha256=TlEQq4bcs8ARLL3PoFS8Qw6oYFeMqcGnWTeJ7NsPPFk,9408 +sqlalchemy/ext/orderinglist.py,sha256=8Vcg7UUkLg-QbYAbLVDSqu-5REkR6L-FLLhCYsHYxCQ,14384 +sqlalchemy/ext/serializer.py,sha256=ox6dbMOBmFR0H2RQFt17mcYBOGKgn1cNVFfqY8-jpgQ,6178 +sqlalchemy/future/__init__.py,sha256=79DZx3v7TQZpkS_qThlmuCOm1a9UK2ObNZhyMmjfNB0,516 +sqlalchemy/future/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/future/__pycache__/engine.cpython-312.pyc,, +sqlalchemy/future/engine.py,sha256=6uOpOedIqiT1-3qJSJIlv9_raMJU8NTkhQwN_Ngg8kI,499 +sqlalchemy/inspection.py,sha256=i3aR-IV101YU8D9TA8Pxb2wi08QZuJ34sMy6L5M__rY,5145 +sqlalchemy/log.py,sha256=aSlZ8DFHkOuI-AMmaOUUYtS9zGPadi_7tAo98QpUOiY,8634 +sqlalchemy/orm/__init__.py,sha256=cBn0aPWyDFY4ya-cHRshQBcuThk1smTUCTrlp6LHdlE,8463 +sqlalchemy/orm/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/_orm_constructors.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/_typing.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/attributes.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/base.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/bulk_persistence.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/clsregistry.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/collections.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/context.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/decl_api.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/decl_base.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/dependency.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/descriptor_props.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/dynamic.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/evaluator.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/events.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/exc.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/identity.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/instrumentation.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/interfaces.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/loading.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/mapped_collection.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/mapper.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/path_registry.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/persistence.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/properties.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/query.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/relationships.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/scoping.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/session.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/state.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/state_changes.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/strategies.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/strategy_options.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/sync.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/unitofwork.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/util.cpython-312.pyc,, +sqlalchemy/orm/__pycache__/writeonly.cpython-312.pyc,, +sqlalchemy/orm/_orm_constructors.py,sha256=_7_GY6qw2sA-GG_WXLz1GOO-0qC-SCBeA43GhVuS2Qw,99803 +sqlalchemy/orm/_typing.py,sha256=oRUJVAGpU3_DhSkIb1anXgneweVIARjB51HlPhMNfcM,5015 +sqlalchemy/orm/attributes.py,sha256=NFhYheqqu2VcXmKTdcvQKiRR_6qo0rHLK7nda7rpviA,92578 +sqlalchemy/orm/base.py,sha256=iZXsygk4fn8wd7wx1iXn_PfnGDY7d41YRfS0mC_q5vE,27700 +sqlalchemy/orm/bulk_persistence.py,sha256=S9VK5a6GSqnw3z7O5UG5OOnc9WxzmS_ooDkA5JmCIsY,69878 +sqlalchemy/orm/clsregistry.py,sha256=4J-kKshmLOEyx3VBqREm2k_XY0cer4zwUoHJT3n5Xmw,17949 +sqlalchemy/orm/collections.py,sha256=0AZFr9us9MiHo_Xcyi7DUsN02jSBERUOd-jIK8qQ1DA,52159 +sqlalchemy/orm/context.py,sha256=VyJl1ZJ5OnJUACKlM-bPLyyoqu4tyaKKdxeC-QF4EuU,111698 +sqlalchemy/orm/decl_api.py,sha256=a2Cyvjh6j5BlXJQ2i0jpQx7xkeI_6xo5MMxr0d2ndQY,63589 +sqlalchemy/orm/decl_base.py,sha256=g9xW9G-n9iStMI0i3i-9Rt4LDRW8--3iCCRPlWF6Cko,81660 +sqlalchemy/orm/dependency.py,sha256=g3R_1H_OGzagXFeen3Irm3c1lO3yeXGdGa0muUZgZAk,47583 +sqlalchemy/orm/descriptor_props.py,sha256=SdrfVu05zhWLGe_DnBlgbU6e5sWkkfBTirH9Nrr1MLk,37176 +sqlalchemy/orm/dynamic.py,sha256=pYlMIrpp80Ex4KByqdyhx0x0kIrl_cIADwkeVxvYu4s,9798 +sqlalchemy/orm/evaluator.py,sha256=jPjVrP7XbVOG6aXTCBREq0rF3oNHLqB4XAT-gt_cpaA,11925 +sqlalchemy/orm/events.py,sha256=fGnUHwDTV9FTiifB2mmIJispwPbIT4mZongRJD7uiw4,127258 +sqlalchemy/orm/exc.py,sha256=A3wvZVs5sC5XCef4LoTUBG-UfhmliFpU9rYMdS2t_To,7356 +sqlalchemy/orm/identity.py,sha256=gRiuQSrurHGEAJXH9QGYioXL49Im5EGcYQ-IKUEpHmQ,9249 +sqlalchemy/orm/instrumentation.py,sha256=o1mTv5gCgl9d-SRvEXXjl8rzl8uBasRL3bpDgWg9P58,24337 +sqlalchemy/orm/interfaces.py,sha256=RW7bBXGWtZHY2wXFOSqtvYm6UDl7yHZUyRX_6Yd3GfQ,48395 +sqlalchemy/orm/loading.py,sha256=F1ZEHTPBglmznST2nGj_0ARccoFgTyaOOwjcqpYeuvM,57366 +sqlalchemy/orm/mapped_collection.py,sha256=ZgYHaF37yo6-gZ7Da1Gg25rMgG2GynAy-RJoDhljV5g,19698 +sqlalchemy/orm/mapper.py,sha256=kyq4pBkTvvEqlW4H4XK_ktP1sOiALNAycgvF5f-xtqw,170969 +sqlalchemy/orm/path_registry.py,sha256=olyutgn0uNB7Wi32YNQx9ZHV6sUgV3TbyGplfSxfZ6g,25938 +sqlalchemy/orm/persistence.py,sha256=qr1jUgo-NZ0tLa5eIis2271QDt4KNJwYlYU_9CaKNhQ,60545 +sqlalchemy/orm/properties.py,sha256=dt1Gy06pbRY6zgm4QGR9nU6z2WCyoTZWBJYKpUhLq_c,29095 +sqlalchemy/orm/query.py,sha256=VBSD0k15xU_XykggvLGAwGdwNglBAoBKbOk8qAoMKdI,117714 +sqlalchemy/orm/relationships.py,sha256=wrHyICb8A5qPoyxf-nITQVJ13kCNr2MedDqEY8QMSt8,127816 +sqlalchemy/orm/scoping.py,sha256=75iPEWDFhPcIXgl8EUd_sPTCL6punfegEaTRE5mP3e8,78835 +sqlalchemy/orm/session.py,sha256=TeBcZNdY4HWQFdXNCIqbsQTtkvfJkBweMzvA9p3BiPA,193279 +sqlalchemy/orm/state.py,sha256=EaWkVNWHaDeJ_FZGXHakSamUk51BXmtMWLGdFhlJmh8,37536 +sqlalchemy/orm/state_changes.py,sha256=pqkjSDOR6H5BufMKdzFUIatDp3DY90SovOJiJ1k6Ayw,6815 +sqlalchemy/orm/strategies.py,sha256=V0o-1kB1IVTxhOGqGtRyjddZqAbPdsl_h-k0N3MKCGo,114052 +sqlalchemy/orm/strategy_options.py,sha256=EmgH28uMQhwwBCDVcXmywLk_Q8AbpnK02seMsMV4nmc,84102 +sqlalchemy/orm/sync.py,sha256=5Nt_OqP4IfhAtHwFRar4dw-YjLENRLvp4d3jDC4wpnw,5749 +sqlalchemy/orm/unitofwork.py,sha256=Wk5YZocBbxe4m1wU2aFQ7gY1Cp5CROi13kDEM1iOSz4,27033 +sqlalchemy/orm/util.py,sha256=7hCRYbQjqhWJTkrPf_NXY9zF_18VWTpyguu-nfYfc6c,80340 +sqlalchemy/orm/writeonly.py,sha256=WCPXCAwHqVCfhVWXQEFCP3OocIiHgqNJ5KnuJwSgGq4,22329 +sqlalchemy/pool/__init__.py,sha256=CIv4b6ctueY7w3sML_LxyLKAdl59esYOhz3O7W5w7WE,1815 +sqlalchemy/pool/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/pool/__pycache__/base.cpython-312.pyc,, +sqlalchemy/pool/__pycache__/events.cpython-312.pyc,, +sqlalchemy/pool/__pycache__/impl.cpython-312.pyc,, +sqlalchemy/pool/base.py,sha256=wuwKIak5d_4-TqKI2RFN8OYMEyOvV0djnoSVR8gbxAQ,52249 +sqlalchemy/pool/events.py,sha256=IcWfORKbHM69Z9FdPJlXI7-NIhQrR9O_lg59tiUdTRU,13148 +sqlalchemy/pool/impl.py,sha256=vU0n82a7uxdE34p3hU7cvUDA5QDy9MkIv1COT4kYFP8,17724 +sqlalchemy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sqlalchemy/schema.py,sha256=mt74CGCBtfv_qI1_6zzNFMexYGyWDj2Jkh-XdH4kEWI,3194 +sqlalchemy/sql/__init__.py,sha256=jAQx9rwhyPhoSjntM1BZSElJiMRmLowGThJVDGvExSU,5820 +sqlalchemy/sql/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/_dml_constructors.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/_elements_constructors.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/_orm_types.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/_py_util.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/_selectable_constructors.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/_typing.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/annotation.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/base.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/cache_key.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/coercions.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/compiler.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/crud.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/ddl.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/default_comparator.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/dml.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/elements.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/events.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/expression.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/functions.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/lambdas.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/naming.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/operators.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/roles.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/schema.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/selectable.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/sqltypes.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/traversals.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/type_api.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/util.cpython-312.pyc,, +sqlalchemy/sql/__pycache__/visitors.cpython-312.pyc,, +sqlalchemy/sql/_dml_constructors.py,sha256=hoNyINY3FNi1ZQajR6lbcRN7oYsNghM1wuzzVWxIv3c,3867 +sqlalchemy/sql/_elements_constructors.py,sha256=-qksx59Gqhmzxo1xByPtZZboNvL8uYcCN14pjHYHxL8,62914 +sqlalchemy/sql/_orm_types.py,sha256=_vR3_HQYgZR_of6_ZpTQByie2gaVScxQjVAVWAP3Ztg,620 +sqlalchemy/sql/_py_util.py,sha256=iiwgX3dQhOjdB5-10jtgHPIdibUqGk49bC1qdZMBpYI,2173 +sqlalchemy/sql/_selectable_constructors.py,sha256=RDqgejqiUuU12Be1jBpMIx_YdJho8fhKfnMoJLPFTFE,18812 +sqlalchemy/sql/_typing.py,sha256=C8kNZQ3TIpM-Q12Of3tTaESB1UxIfRME_lXouqgwMT8,12252 +sqlalchemy/sql/annotation.py,sha256=pTNidcQatCar6H1I9YAoPP1e6sOewaJ15B7_-7ykZOE,18271 +sqlalchemy/sql/base.py,sha256=dVvZoPoa3pb6iuwTU4QoCvVWQPyHZthaekl5J2zV_SU,73928 +sqlalchemy/sql/cache_key.py,sha256=Dl163qHjTkMCa5LTipZud8X3w0d8DvdIvGvv4AqriHE,32823 +sqlalchemy/sql/coercions.py,sha256=ju8xEi7b9G_GzxaQ6Nwu0cFIWFZ--ottIVfdiuhHY7Y,40553 +sqlalchemy/sql/compiler.py,sha256=9Wx423H72Yq7NHR8cmMAH6GpMCJmghs1L85YJqs_Lng,268763 +sqlalchemy/sql/crud.py,sha256=nyAPlmvuyWxMqSBdWPffC5P3CGXTQKK0bJoDbNgB3iQ,56457 +sqlalchemy/sql/ddl.py,sha256=XuUhulJLvvPjU4nYD6N42QLg8rEgquD6Jwn_yIHZejk,45542 +sqlalchemy/sql/default_comparator.py,sha256=SE0OaK1BlY0RinQ21ZXJOUGkO00oGv6GMMmAH-4iNTQ,16663 +sqlalchemy/sql/dml.py,sha256=eftbzdFJgMk7NV0BHKfK4dQ2R7XsyyJn6fCgYFJ0KNQ,65728 +sqlalchemy/sql/elements.py,sha256=dsNa2K57RygsGoaWuTMPp2QQ6SU3uZXSMW6CLGBbcIY,171208 +sqlalchemy/sql/events.py,sha256=xe3vJ6pQJau3dJWBAY0zU7Lz52UKuMrpLycriLm3AWA,18301 +sqlalchemy/sql/expression.py,sha256=baMnCH04jeE8E3tA2TovXlsREocA2j3fdHKnzOB8H4U,7586 +sqlalchemy/sql/functions.py,sha256=AcI_KstJxeLw6rEXx6QnIgR2rq4Ru6RXMbq4EIIUURA,55319 +sqlalchemy/sql/lambdas.py,sha256=EfDdUBi5cSmkjz8pQCSRo858UWQCFNZxXkM-1qS0CgU,49281 +sqlalchemy/sql/naming.py,sha256=l8udFP2wvXLgehIB0uF2KXwpkXSVSREDk6fLCH9F-XY,6865 +sqlalchemy/sql/operators.py,sha256=BYATjkBQLJAmwHAlGUSV-dv9RLtGw_ziAvFbKDrN4YU,76107 +sqlalchemy/sql/roles.py,sha256=71zm_xpRkUdnu-WzG6lxQVnFHwvUjf6X6e3kRIkbzAs,7686 +sqlalchemy/sql/schema.py,sha256=TOBTbcRY6ehosJEcpYn2NX0_UGZP9lfFs-o8lJVc5tI,228104 +sqlalchemy/sql/selectable.py,sha256=9dO2yhN83zjna7nPjOE1hcvGyJGjc_lj5SAz7SP5CBQ,233041 +sqlalchemy/sql/sqltypes.py,sha256=_0FpFLH0AFueb3TIB5Vcx9nXWDNj31XFQTP0u8OXnSo,126540 +sqlalchemy/sql/traversals.py,sha256=7b98JSeLxqecmGHhhLXT_2M4QMke6W-xCci5RXndhxI,33521 +sqlalchemy/sql/type_api.py,sha256=D9Kq-ppwZvlNmxaHqvVmM8IVg4n6_erzJpVioye9WKE,83823 +sqlalchemy/sql/util.py,sha256=lBEAf_-eRepTErOBCp1PbEMZDYdJqAiK1GemQtgojYo,48175 +sqlalchemy/sql/visitors.py,sha256=KD1qOYm6RdftCufVGB8q6jFTIZIQKS3zPCg78cVV0mQ,36427 +sqlalchemy/testing/__init__.py,sha256=9M2SMxBBLJ8xLUWXNCWDzkcvOqFznWcJzrSd712vATU,3126 +sqlalchemy/testing/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/assertions.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/assertsql.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/asyncio.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/config.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/engines.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/entities.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/exclusions.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/pickleable.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/profiling.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/provision.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/requirements.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/schema.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/util.cpython-312.pyc,, +sqlalchemy/testing/__pycache__/warnings.cpython-312.pyc,, +sqlalchemy/testing/assertions.py,sha256=lNNZ-gfF4TDRXmB7hZDdch7JYZRb_qWGeqWDFKtopx0,31439 +sqlalchemy/testing/assertsql.py,sha256=EIVk3i5qjiSI63c1ikTPoGhulZl88SSeOS2VNo1LJvM,16817 +sqlalchemy/testing/asyncio.py,sha256=cAw68tzu3h5wjdIKfOqhFATcbMb38XeK0ThjIalUHuQ,3728 +sqlalchemy/testing/config.py,sha256=MZOWz7wqzc1pbwHWSAR0RJkt2C-SD6ox-nYY7VHdi_U,12030 +sqlalchemy/testing/engines.py,sha256=w5-0FbanItRsOt6x4n7wM_OnToCzJnrvZZ2hk5Yzng8,13355 +sqlalchemy/testing/entities.py,sha256=rysywsnjXHlIIC-uv0L7-fLmTAuNpHJvcSd1HeAdY5M,3354 +sqlalchemy/testing/exclusions.py,sha256=uoYLEwyNOK1eR8rpfOZ2Q3dxgY0akM-RtsIFML-FPrY,12444 +sqlalchemy/testing/fixtures/__init__.py,sha256=9snVns5A7g28LqC6gqQuO4xRBoJzdnf068GQ6Cae75I,1198 +sqlalchemy/testing/fixtures/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/testing/fixtures/__pycache__/base.cpython-312.pyc,, +sqlalchemy/testing/fixtures/__pycache__/mypy.cpython-312.pyc,, +sqlalchemy/testing/fixtures/__pycache__/orm.cpython-312.pyc,, +sqlalchemy/testing/fixtures/__pycache__/sql.cpython-312.pyc,, +sqlalchemy/testing/fixtures/base.py,sha256=OayRr25soCqj1_yc665D5XbWWzFCm7Xl9Txtps953p4,12256 +sqlalchemy/testing/fixtures/mypy.py,sha256=7fWVZzYzNjqmLIoFa-MmXSGDPS3eZYFXlH-WxaxBDDY,11845 +sqlalchemy/testing/fixtures/orm.py,sha256=x27qjpK54JETATcYuiphtW-HXRy8ej8h3aCDkeQXPfY,6095 +sqlalchemy/testing/fixtures/sql.py,sha256=Q7Qq0n4qTT681nWt5DqjThopgjv5BB2KmSmrmAxUqHM,15704 +sqlalchemy/testing/pickleable.py,sha256=B9dXGF7E2PywB67SngHPjSMIBDTFhyAV4rkDUcyMulk,2833 +sqlalchemy/testing/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sqlalchemy/testing/plugin/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-312.pyc,, +sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-312.pyc,, +sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-312.pyc,, +sqlalchemy/testing/plugin/bootstrap.py,sha256=GrBB27KbswjE3Tt-zJlj6uSqGh9N-_CXkonnJSSBz84,1437 +sqlalchemy/testing/plugin/plugin_base.py,sha256=4SizjghFdDddt5o5gQ16Nw0bJHrtuBa4smxJcea-ti8,21573 +sqlalchemy/testing/plugin/pytestplugin.py,sha256=yh4PP406O0TwPMDzpJHpcNdU2WHXCLYI10F3oOLePjE,27295 +sqlalchemy/testing/profiling.py,sha256=HPjYvRLT1nD90FCZ7AA8j9ygkMtf1SGA47Xze2QPueo,10148 +sqlalchemy/testing/provision.py,sha256=w4F_ceGHPpWHUeh6cVcE5ktCC-ISrGc2yOSnXauOd5U,14200 +sqlalchemy/testing/requirements.py,sha256=gkviA8f5p4qdoDwAK791I4oGvnEqlm0ZZwJZpJzobFY,51393 +sqlalchemy/testing/schema.py,sha256=OSfMoIJ7ORbevGkeJdrKcTrQ0s7wXebuCU08mC1Y9jA,6513 +sqlalchemy/testing/suite/__init__.py,sha256=_firVc2uS3TMZ3vH2baQzNb17ubM78RHtb9kniSybmk,476 +sqlalchemy/testing/suite/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_cte.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_insert.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_results.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_select.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_types.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-312.pyc,, +sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-312.pyc,, +sqlalchemy/testing/suite/test_cte.py,sha256=O5idVeBnHm9zdiG3tuCBUn4hYU_TA63-6LNnRygr8g0,6205 +sqlalchemy/testing/suite/test_ddl.py,sha256=xWimTjggpTe3S1Xfmt_IPofTXkUUcKuVSVCIfIyGMbA,11785 +sqlalchemy/testing/suite/test_deprecations.py,sha256=XI8ZU1NxC-6uvPDImaaq9O7Ov6MF5gmy-yk3TfesLAo,5082 +sqlalchemy/testing/suite/test_dialect.py,sha256=HUpHZb7pnHbsoRpDLONpsCO_oWhBgjglU9pBO-EOUw4,22673 +sqlalchemy/testing/suite/test_insert.py,sha256=Wm_pW0qqUNV1Fs7mXoxtmaTHMQGmaVDgDsYgZs1jlxM,18308 +sqlalchemy/testing/suite/test_reflection.py,sha256=Nd4Ao_J3Sr-VeAeWbUe3gs6STPvik9DC37WkyJc-PVg,106205 +sqlalchemy/testing/suite/test_results.py,sha256=Hd6R4jhBNNQSp0xGa8wwTgpw-XUrCEZ3dWXpoZ4_DKs,15687 +sqlalchemy/testing/suite/test_rowcount.py,sha256=zhKVv0ibFSQmnE5luLwgHAn840zOJ6HxtkR3oL995cs,7652 +sqlalchemy/testing/suite/test_select.py,sha256=QHsBX16EZpxlEZZLM0pMNcwayPU0dig39McKwiiith0,58325 +sqlalchemy/testing/suite/test_sequence.py,sha256=c80CBWrU930GPnPfr9TCRbTTuITR7BpIactncLIj2XU,9672 +sqlalchemy/testing/suite/test_types.py,sha256=QjV48MqR7dB8UVzt56UL2z7Nt28-IhywX3DKuQeLYsY,65429 +sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=7obItCpFt4qlWaDqe25HWgQT6FoUhgz1W7_Xycfz9Xk,5887 +sqlalchemy/testing/suite/test_update_delete.py,sha256=1hT0BTxB4SNipd6hnVlMnq25dLtQQoXov7z7UR0Sgi8,3658 +sqlalchemy/testing/util.py,sha256=Wsu4GZgCW6wX9mmxfiffhDz1cZm3778OB3LtiWNgb3Y,14080 +sqlalchemy/testing/warnings.py,sha256=pmfT33PF1q1PI7DdHOsup3LxHq1AC4-aYl1oL8HmrYo,1546 +sqlalchemy/types.py,sha256=DgBpPaT-vtsn6_glx5wocrIhR2A1vy56SQNRY3NiPUw,3168 +sqlalchemy/util/__init__.py,sha256=Bh0SkfkeCsz6-rbDmC41lAWOuCvKCiXVZthN2cWJEXk,8245 +sqlalchemy/util/__pycache__/__init__.cpython-312.pyc,, +sqlalchemy/util/__pycache__/_collections.cpython-312.pyc,, +sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-312.pyc,, +sqlalchemy/util/__pycache__/_has_cy.cpython-312.pyc,, +sqlalchemy/util/__pycache__/_py_collections.cpython-312.pyc,, +sqlalchemy/util/__pycache__/compat.cpython-312.pyc,, +sqlalchemy/util/__pycache__/concurrency.cpython-312.pyc,, +sqlalchemy/util/__pycache__/deprecations.cpython-312.pyc,, +sqlalchemy/util/__pycache__/langhelpers.cpython-312.pyc,, +sqlalchemy/util/__pycache__/preloaded.cpython-312.pyc,, +sqlalchemy/util/__pycache__/queue.cpython-312.pyc,, +sqlalchemy/util/__pycache__/tool_support.cpython-312.pyc,, +sqlalchemy/util/__pycache__/topological.cpython-312.pyc,, +sqlalchemy/util/__pycache__/typing.cpython-312.pyc,, +sqlalchemy/util/_collections.py,sha256=FYqVQg3CaqiEd21OFN1pNCfFbQ8gvlchW_TMtihSFNE,20169 +sqlalchemy/util/_concurrency_py3k.py,sha256=31vs1oXaLzeTRgmOXRrWToRQskWmJk-CBs3-JxSTcck,8223 +sqlalchemy/util/_has_cy.py,sha256=XMkeqCDGmhkd0uuzpCdyELz7gOjHxyFQ1AIlc5NneoY,1229 +sqlalchemy/util/_py_collections.py,sha256=cYjsYLCLBy5jdGBJATLJCmtfzr_AaJ-HKTUN8OdAzxY,16630 +sqlalchemy/util/compat.py,sha256=FkeHnW9asJYJvNmxVltee8jQNwQSdVRdKJlVRRInJI4,9388 +sqlalchemy/util/concurrency.py,sha256=ZxcQYOKy-GBsQkPmCrBO5MzMpqW3JZme2Hiyqpbt9uc,2284 +sqlalchemy/util/deprecations.py,sha256=pr9DSAf1ECqDk7X7F6TNc1jrhOeFihL33uEb5Wt2_T0,11971 +sqlalchemy/util/langhelpers.py,sha256=CQQP2Q9c68nL5mcWL-Q38-INrtoDHDnBmq7QhnWyEDM,64980 +sqlalchemy/util/preloaded.py,sha256=KKNLJEqChDW1TNUsM_TzKu7JYEA3kkuh2N-quM_2_Y4,5905 +sqlalchemy/util/queue.py,sha256=ITejs6KS4Hz_ojrss2oFeUO9MoIeR3qWmZQ8J7yyrNU,10205 +sqlalchemy/util/tool_support.py,sha256=epm8MzDZpVmhE6LIjrjJrP8BUf12Wab2m28A9lGq95s,5969 +sqlalchemy/util/topological.py,sha256=hjJWL3C_B7Rpv9s7jj7wcTckcZUSkxc6xRDhiN1xyec,3458 +sqlalchemy/util/typing.py,sha256=ESYm4oQtt-SarN04YTXCgovXT8tFupMiPmuGCDCMEIc,15831 diff --git a/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/REQUESTED b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/WHEEL b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/WHEEL new file mode 100644 index 00000000..68b1a99c --- /dev/null +++ b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.41.3) +Root-Is-Purelib: false +Tag: cp312-cp312-musllinux_1_1_x86_64 + diff --git a/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/top_level.txt new file mode 100644 index 00000000..39fb2bef --- /dev/null +++ b/venv/lib/python3.12/site-packages/SQLAlchemy-2.0.23.dist-info/top_level.txt @@ -0,0 +1 @@ +sqlalchemy diff --git a/venv/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc b/venv/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..786acea28bcb2ebc7b7e9fcf5d30a4b7090587b0 GIT binary patch literal 163642 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/METADATA b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/METADATA new file mode 100644 index 00000000..04d82dce --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/METADATA @@ -0,0 +1,139 @@ +Metadata-Version: 2.4 +Name: alembic +Version: 1.18.4 +Summary: A database migration tool for SQLAlchemy. +Author-email: Mike Bayer +License-Expression: MIT +Project-URL: Homepage, https://alembic.sqlalchemy.org +Project-URL: Documentation, https://alembic.sqlalchemy.org/en/latest/ +Project-URL: Changelog, https://alembic.sqlalchemy.org/en/latest/changelog.html +Project-URL: Source, https://github.com/sqlalchemy/alembic/ +Project-URL: Issue Tracker, https://github.com/sqlalchemy/alembic/issues/ +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Environment :: Console +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Database :: Front-Ends +Requires-Python: >=3.10 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: SQLAlchemy>=1.4.23 +Requires-Dist: Mako +Requires-Dist: typing-extensions>=4.12 +Requires-Dist: tomli; python_version < "3.11" +Provides-Extra: tz +Requires-Dist: tzdata; extra == "tz" +Dynamic: license-file + +Alembic is a database migrations tool written by the author +of `SQLAlchemy `_. A migrations tool +offers the following functionality: + +* Can emit ALTER statements to a database in order to change + the structure of tables and other constructs +* Provides a system whereby "migration scripts" may be constructed; + each script indicates a particular series of steps that can "upgrade" a + target database to a new version, and optionally a series of steps that can + "downgrade" similarly, doing the same steps in reverse. +* Allows the scripts to execute in some sequential manner. + +The goals of Alembic are: + +* Very open ended and transparent configuration and operation. A new + Alembic environment is generated from a set of templates which is selected + among a set of options when setup first occurs. The templates then deposit a + series of scripts that define fully how database connectivity is established + and how migration scripts are invoked; the migration scripts themselves are + generated from a template within that series of scripts. The scripts can + then be further customized to define exactly how databases will be + interacted with and what structure new migration files should take. +* Full support for transactional DDL. The default scripts ensure that all + migrations occur within a transaction - for those databases which support + this (Postgresql, Microsoft SQL Server), migrations can be tested with no + need to manually undo changes upon failure. +* Minimalist script construction. Basic operations like renaming + tables/columns, adding/removing columns, changing column attributes can be + performed through one line commands like alter_column(), rename_table(), + add_constraint(). There is no need to recreate full SQLAlchemy Table + structures for simple operations like these - the functions themselves + generate minimalist schema structures behind the scenes to achieve the given + DDL sequence. +* "auto generation" of migrations. While real world migrations are far more + complex than what can be automatically determined, Alembic can still + eliminate the initial grunt work in generating new migration directives + from an altered schema. The ``--autogenerate`` feature will inspect the + current status of a database using SQLAlchemy's schema inspection + capabilities, compare it to the current state of the database model as + specified in Python, and generate a series of "candidate" migrations, + rendering them into a new migration script as Python directives. The + developer then edits the new file, adding additional directives and data + migrations as needed, to produce a finished migration. Table and column + level changes can be detected, with constraints and indexes to follow as + well. +* Full support for migrations generated as SQL scripts. Those of us who + work in corporate environments know that direct access to DDL commands on a + production database is a rare privilege, and DBAs want textual SQL scripts. + Alembic's usage model and commands are oriented towards being able to run a + series of migrations into a textual output file as easily as it runs them + directly to a database. Care must be taken in this mode to not invoke other + operations that rely upon in-memory SELECTs of rows - Alembic tries to + provide helper constructs like bulk_insert() to help with data-oriented + operations that are compatible with script-based DDL. +* Non-linear, dependency-graph versioning. Scripts are given UUID + identifiers similarly to a DVCS, and the linkage of one script to the next + is achieved via human-editable markers within the scripts themselves. + The structure of a set of migration files is considered as a + directed-acyclic graph, meaning any migration file can be dependent + on any other arbitrary set of migration files, or none at + all. Through this open-ended system, migration files can be organized + into branches, multiple roots, and mergepoints, without restriction. + Commands are provided to produce new branches, roots, and merges of + branches automatically. +* Provide a library of ALTER constructs that can be used by any SQLAlchemy + application. The DDL constructs build upon SQLAlchemy's own DDLElement base + and can be used standalone by any application or script. +* At long last, bring SQLite and its inability to ALTER things into the fold, + but in such a way that SQLite's very special workflow needs are accommodated + in an explicit way that makes the most of a bad situation, through the + concept of a "batch" migration, where multiple changes to a table can + be batched together to form a series of instructions for a single, subsequent + "move-and-copy" workflow. You can even use "move-and-copy" workflow for + other databases, if you want to recreate a table in the background + on a busy system. + +Documentation and status of Alembic is at https://alembic.sqlalchemy.org/ + +The SQLAlchemy Project +====================== + +Alembic is part of the `SQLAlchemy Project `_ and +adheres to the same standards and conventions as the core project. + +Development / Bug reporting / Pull requests +___________________________________________ + +Please refer to the +`SQLAlchemy Community Guide `_ for +guidelines on coding and participating in this project. + +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 +======= + +Alembic is distributed under the `MIT license +`_. diff --git a/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/RECORD b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/RECORD new file mode 100644 index 00000000..9d3c8a8e --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/RECORD @@ -0,0 +1,178 @@ +../../../bin/alembic,sha256=s5N9B43caiai4ugYRk6Kj20EpaVQmxgNH8QNk8Hfavg,246 +alembic-1.18.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +alembic-1.18.4.dist-info/METADATA,sha256=sPH3Zq5eEaNtbnI1os9Rvk7eBbFJSMPq13poNNaxvfs,7217 +alembic-1.18.4.dist-info/RECORD,, +alembic-1.18.4.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91 +alembic-1.18.4.dist-info/entry_points.txt,sha256=aykM30soxwGN0pB7etLc1q0cHJbL9dy46RnK9VX4LLw,48 +alembic-1.18.4.dist-info/licenses/LICENSE,sha256=bmjZSgOg4-Mn3fPobR6-3BTuzjkiAiYY_CRqNilv0Mw,1059 +alembic-1.18.4.dist-info/top_level.txt,sha256=FwKWd5VsPFC8iQjpu1u9Cn-JnK3-V1RhUCmWqz1cl-s,8 +alembic/__init__.py,sha256=6ppwNUS6dfdFIm5uwZaaZ9lDZ7pIwkTNyQcbjY47V3I,93 +alembic/__main__.py,sha256=373m7-TBh72JqrSMYviGrxCHZo-cnweM8AGF8A22PmY,78 +alembic/__pycache__/__init__.cpython-312.pyc,, +alembic/__pycache__/__main__.cpython-312.pyc,, +alembic/__pycache__/command.cpython-312.pyc,, +alembic/__pycache__/config.cpython-312.pyc,, +alembic/__pycache__/context.cpython-312.pyc,, +alembic/__pycache__/environment.cpython-312.pyc,, +alembic/__pycache__/migration.cpython-312.pyc,, +alembic/__pycache__/op.cpython-312.pyc,, +alembic/autogenerate/__init__.py,sha256=ntmUTXhjLm4_zmqIwyVaECdpPDn6_u1yM9vYk6-553E,543 +alembic/autogenerate/__pycache__/__init__.cpython-312.pyc,, +alembic/autogenerate/__pycache__/api.cpython-312.pyc,, +alembic/autogenerate/__pycache__/render.cpython-312.pyc,, +alembic/autogenerate/__pycache__/rewriter.cpython-312.pyc,, +alembic/autogenerate/api.py,sha256=8tVNDSHlqsBgj1IVLdqvZr_jlvz9kp3O5EKIL9biaZg,22781 +alembic/autogenerate/compare/__init__.py,sha256=kCvA0ZK0rTahNv9wlgyIB5DH2lFEhTRO4PFmoqcL9JE,1809 +alembic/autogenerate/compare/__pycache__/__init__.cpython-312.pyc,, +alembic/autogenerate/compare/__pycache__/comments.cpython-312.pyc,, +alembic/autogenerate/compare/__pycache__/constraints.cpython-312.pyc,, +alembic/autogenerate/compare/__pycache__/schema.cpython-312.pyc,, +alembic/autogenerate/compare/__pycache__/server_defaults.cpython-312.pyc,, +alembic/autogenerate/compare/__pycache__/tables.cpython-312.pyc,, +alembic/autogenerate/compare/__pycache__/types.cpython-312.pyc,, +alembic/autogenerate/compare/__pycache__/util.cpython-312.pyc,, +alembic/autogenerate/compare/comments.py,sha256=agSrWsZhJ47i-E-EqiP3id2CXTTbP0muOKk1-9in9lg,3234 +alembic/autogenerate/compare/constraints.py,sha256=7sLSvUK9M2CbMRRQy5pveIXbjDLRDnfPx0Dvi_KXOf8,27906 +alembic/autogenerate/compare/schema.py,sha256=plQ7JJ1zJGlnajweSV8lAD9tDYPks5G40sliocTuXJA,1695 +alembic/autogenerate/compare/server_defaults.py,sha256=D--5EvEfyX0fSVkK6iLtRoer5sYK6xeNC2TIdu7klUk,10792 +alembic/autogenerate/compare/tables.py,sha256=47pAgVhbmXGLrm3dMK6hrNABxOAe_cGSQmPtCBwORVc,10611 +alembic/autogenerate/compare/types.py,sha256=75bOduz-dOiyLI065XD5sEP_JF9GPLkDAQ_y5B8lXF0,4005 +alembic/autogenerate/compare/util.py,sha256=K_GArJ2xQXZi6ftb8gkgZuIdVqvyep3E2ZXq8F3-jIU,9521 +alembic/autogenerate/render.py,sha256=ceQL8nk8m2kBtQq5gtxtDLR9iR0Sck8xG_61Oez-Sqs,37270 +alembic/autogenerate/rewriter.py,sha256=NIASSS-KaNKPmbm1k4pE45aawwjSh1Acf6eZrOwnUGM,7814 +alembic/command.py,sha256=7RzAwwXR31sOl0oVItyZl9B0j3TeR5dRyx9634lVsLM,25297 +alembic/config.py,sha256=VoCZV2cFZoF0Xa1OxHqsA-MKzuwBRaJSC7hxZ3-uWN4,34983 +alembic/context.py,sha256=hK1AJOQXJ29Bhn276GYcosxeG7pC5aZRT5E8c4bMJ4Q,195 +alembic/context.pyi,sha256=b_naI_W8dyiZRsL_n299a-LbqLZxKTAgDIXubRLVKlY,32531 +alembic/ddl/__init__.py,sha256=Df8fy4Vn_abP8B7q3x8gyFwEwnLw6hs2Ljt_bV3EZWE,152 +alembic/ddl/__pycache__/__init__.cpython-312.pyc,, +alembic/ddl/__pycache__/_autogen.cpython-312.pyc,, +alembic/ddl/__pycache__/base.cpython-312.pyc,, +alembic/ddl/__pycache__/impl.cpython-312.pyc,, +alembic/ddl/__pycache__/mssql.cpython-312.pyc,, +alembic/ddl/__pycache__/mysql.cpython-312.pyc,, +alembic/ddl/__pycache__/oracle.cpython-312.pyc,, +alembic/ddl/__pycache__/postgresql.cpython-312.pyc,, +alembic/ddl/__pycache__/sqlite.cpython-312.pyc,, +alembic/ddl/_autogen.py,sha256=Blv2RrHNyF4cE6znCQXNXG5T9aO-YmiwD4Fz-qfoaWA,9275 +alembic/ddl/base.py,sha256=dNhLIZnFMP7Cr8rE_e2Zb5skGgCMBOdca1PajXqZYhs,11977 +alembic/ddl/impl.py,sha256=IU3yHFVI3v0QHEwNL_LSN1PRpPF0n09NFFqRZkW86wE,31376 +alembic/ddl/mssql.py,sha256=dee0acwnxmTZXuYPqqlYaDiSbKS46zVH0WRULjX5Blg,17398 +alembic/ddl/mysql.py,sha256=2fvzGcdg4qqCJogGnzvQN636vUi9mF6IoQWLGevvF_A,18456 +alembic/ddl/oracle.py,sha256=669YlkcZihlXFbnXhH2krdrvDry8q5pcUGfoqkg_R6Y,6243 +alembic/ddl/postgresql.py,sha256=04M4OpZOCJJ3ipuHoVwlR1gI1sgRwOguRRVx_mFg8Uc,30417 +alembic/ddl/sqlite.py,sha256=TmzU3YaR3aw_0spSrA6kcUY8fyDfwsu4GkH5deYPEK8,8017 +alembic/environment.py,sha256=MM5lPayGT04H3aeng1H7GQ8HEAs3VGX5yy6mDLCPLT4,43 +alembic/migration.py,sha256=MV6Fju6rZtn2fTREKzXrCZM6aIBGII4OMZFix0X-GLs,41 +alembic/op.py,sha256=flHtcsVqOD-ZgZKK2pv-CJ5Cwh-KJ7puMUNXzishxLw,167 +alembic/op.pyi,sha256=ABBlNk4Eg7DR17knSKIjmvHQBNAmKh3aHQNHU8Oyw08,53347 +alembic/operations/__init__.py,sha256=e0KQSZAgLpTWvyvreB7DWg7RJV_MWSOPVDgCqsd2FzY,318 +alembic/operations/__pycache__/__init__.cpython-312.pyc,, +alembic/operations/__pycache__/base.cpython-312.pyc,, +alembic/operations/__pycache__/batch.cpython-312.pyc,, +alembic/operations/__pycache__/ops.cpython-312.pyc,, +alembic/operations/__pycache__/schemaobj.cpython-312.pyc,, +alembic/operations/__pycache__/toimpl.cpython-312.pyc,, +alembic/operations/base.py,sha256=ubpv1HDol0g0nuLi0b8-uN7-HEVRZ6mq8arvK9EGo0g,78432 +alembic/operations/batch.py,sha256=hYOpzG2FK_8hk-rHNuLuFAA3-VXRSOnsTrpz2YlA61Q,26947 +alembic/operations/ops.py,sha256=ofbHkReZkZX2n9lXDaIPlrKe2U1mwgQpZNhEbuC4QrM,99325 +alembic/operations/schemaobj.py,sha256=Wp-bBe4a8lXPTvIHJttBY0ejtpVR5Jvtb2kI-U2PztQ,9468 +alembic/operations/toimpl.py,sha256=f8rH3jdob9XvEJr6CoWEkX6X1zgNB5qxdcEQugyhBvU,8466 +alembic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +alembic/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +alembic/runtime/__pycache__/__init__.cpython-312.pyc,, +alembic/runtime/__pycache__/environment.cpython-312.pyc,, +alembic/runtime/__pycache__/migration.cpython-312.pyc,, +alembic/runtime/__pycache__/plugins.cpython-312.pyc,, +alembic/runtime/environment.py,sha256=1cR1v18sIKvOPZMlc4fHGU4J8r6Dec9h4o3WXkMmFKQ,42400 +alembic/runtime/migration.py,sha256=mR2Ee1h9Yy6OMFeDL4LOYorLYby2l2f899WGK_boECw,48427 +alembic/runtime/plugins.py,sha256=pWCDhMX8MvR8scXhiGSRNYNW7-ckEbOW2qK58xRFy1Q,5707 +alembic/script/__init__.py,sha256=lSj06O391Iy5avWAiq8SPs6N8RBgxkSPjP8wpXcNDGg,100 +alembic/script/__pycache__/__init__.cpython-312.pyc,, +alembic/script/__pycache__/base.cpython-312.pyc,, +alembic/script/__pycache__/revision.cpython-312.pyc,, +alembic/script/__pycache__/write_hooks.cpython-312.pyc,, +alembic/script/base.py,sha256=OInSjbfcnUSjVCc5vVYY33UJ1Uo5xE5Huicp8P9VM1I,36698 +alembic/script/revision.py,sha256=SEePZPTMIyfjF73QAD0VIax9jc1dALkiLQZwTzwiyPw,62312 +alembic/script/write_hooks.py,sha256=KWH12250h_JcdBkGsLVo9JKYKpNcJxBUjwZ9r_r88Bc,5369 +alembic/templates/async/README,sha256=ISVtAOvqvKk_5ThM5ioJE-lMkvf9IbknFUFVU_vPma4,58 +alembic/templates/async/__pycache__/env.cpython-312.pyc,, +alembic/templates/async/alembic.ini.mako,sha256=esbuCnpkyjntJC7k9NnYcCAzhrRQ8NVC4pWineiRk_w,5010 +alembic/templates/async/env.py,sha256=zbOCf3Y7w2lg92hxSwmG1MM_7y56i_oRH4AKp0pQBYo,2389 +alembic/templates/async/script.py.mako,sha256=04kgeBtNMa4cCnG8CfQcKt6P6rnloIfj8wy0u_DBydM,704 +alembic/templates/generic/README,sha256=MVlc9TYmr57RbhXET6QxgyCcwWP7w-vLkEsirENqiIQ,38 +alembic/templates/generic/__pycache__/env.cpython-312.pyc,, +alembic/templates/generic/alembic.ini.mako,sha256=2i2vPsGQSmE9XMiLz8tSBF_UIA8PJl0-fAvbRVmiK_w,5010 +alembic/templates/generic/env.py,sha256=TLRWOVW3Xpt_Tpf8JFzlnoPn_qoUu8UV77Y4o9XD6yI,2103 +alembic/templates/generic/script.py.mako,sha256=04kgeBtNMa4cCnG8CfQcKt6P6rnloIfj8wy0u_DBydM,704 +alembic/templates/multidb/README,sha256=dWLDhnBgphA4Nzb7sNlMfCS3_06YqVbHhz-9O5JNqyI,606 +alembic/templates/multidb/__pycache__/env.cpython-312.pyc,, +alembic/templates/multidb/alembic.ini.mako,sha256=asVt3aJVwjuuw9bopfMofVvonO31coXBbV5DeMRN6cM,5336 +alembic/templates/multidb/env.py,sha256=6zNjnW8mXGUk7erTsAvrfhvqoczJ-gagjVq1Ypg2YIQ,4230 +alembic/templates/multidb/script.py.mako,sha256=ZbCXMkI5Wj2dwNKcxuVGkKZ7Iav93BNx_bM4zbGi3c8,1235 +alembic/templates/pyproject/README,sha256=dMhIiFoeM7EdeaOXBs3mVQ6zXACMyGXDb_UBB6sGRA0,60 +alembic/templates/pyproject/__pycache__/env.cpython-312.pyc,, +alembic/templates/pyproject/alembic.ini.mako,sha256=bQnEoydnLOUgg9vNbTOys4r5MaW8lmwYFXSrlfdEEkw,782 +alembic/templates/pyproject/env.py,sha256=TLRWOVW3Xpt_Tpf8JFzlnoPn_qoUu8UV77Y4o9XD6yI,2103 +alembic/templates/pyproject/pyproject.toml.mako,sha256=W6x_K-xLfEvyM8D4B3Fg0l20P1h6SPK33188pqRFroQ,3000 +alembic/templates/pyproject/script.py.mako,sha256=04kgeBtNMa4cCnG8CfQcKt6P6rnloIfj8wy0u_DBydM,704 +alembic/templates/pyproject_async/README,sha256=2Q5XcEouiqQ-TJssO9805LROkVUd0F6d74rTnuLrifA,45 +alembic/templates/pyproject_async/__pycache__/env.cpython-312.pyc,, +alembic/templates/pyproject_async/alembic.ini.mako,sha256=bQnEoydnLOUgg9vNbTOys4r5MaW8lmwYFXSrlfdEEkw,782 +alembic/templates/pyproject_async/env.py,sha256=zbOCf3Y7w2lg92hxSwmG1MM_7y56i_oRH4AKp0pQBYo,2389 +alembic/templates/pyproject_async/pyproject.toml.mako,sha256=W6x_K-xLfEvyM8D4B3Fg0l20P1h6SPK33188pqRFroQ,3000 +alembic/templates/pyproject_async/script.py.mako,sha256=04kgeBtNMa4cCnG8CfQcKt6P6rnloIfj8wy0u_DBydM,704 +alembic/testing/__init__.py,sha256=PTMhi_2PZ1T_3atQS2CIr0V4YRZzx_doKI-DxKdQS44,1297 +alembic/testing/__pycache__/__init__.cpython-312.pyc,, +alembic/testing/__pycache__/assertions.cpython-312.pyc,, +alembic/testing/__pycache__/env.cpython-312.pyc,, +alembic/testing/__pycache__/fixtures.cpython-312.pyc,, +alembic/testing/__pycache__/requirements.cpython-312.pyc,, +alembic/testing/__pycache__/schemacompare.cpython-312.pyc,, +alembic/testing/__pycache__/util.cpython-312.pyc,, +alembic/testing/__pycache__/warnings.cpython-312.pyc,, +alembic/testing/assertions.py,sha256=VKXMEVWjuPAsYnNxP3WnUpXaFN3ytNFf9LI72OEJ074,5344 +alembic/testing/env.py,sha256=oQN56xXHtHfK8RD-8pH8yZ-uWcjpuNL1Mt5HNrzZyc0,12151 +alembic/testing/fixtures.py,sha256=meqm10rd1ynppW6tw1wcpDJJLyQezZ7FwKyqcrwIOok,11931 +alembic/testing/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +alembic/testing/plugin/__pycache__/__init__.cpython-312.pyc,, +alembic/testing/plugin/__pycache__/bootstrap.cpython-312.pyc,, +alembic/testing/plugin/bootstrap.py,sha256=9C6wtjGrIVztZ928w27hsQE0KcjDLIUtUN3dvZKsMVk,50 +alembic/testing/requirements.py,sha256=OZSHd8I3zOb7288cZxUTebqxx8j0T6I8MekH15TyPvY,4566 +alembic/testing/schemacompare.py,sha256=N5UqSNCOJetIKC4vKhpYzQEpj08XkdgIoqBmEPQ3tlc,4838 +alembic/testing/suite/__init__.py,sha256=MvE7-hwbaVN1q3NM-ztGxORU9dnIelUCINKqNxewn7Y,288 +alembic/testing/suite/__pycache__/__init__.cpython-312.pyc,, +alembic/testing/suite/__pycache__/_autogen_fixtures.cpython-312.pyc,, +alembic/testing/suite/__pycache__/test_autogen_comments.cpython-312.pyc,, +alembic/testing/suite/__pycache__/test_autogen_computed.cpython-312.pyc,, +alembic/testing/suite/__pycache__/test_autogen_diffs.cpython-312.pyc,, +alembic/testing/suite/__pycache__/test_autogen_fks.cpython-312.pyc,, +alembic/testing/suite/__pycache__/test_autogen_identity.cpython-312.pyc,, +alembic/testing/suite/__pycache__/test_environment.cpython-312.pyc,, +alembic/testing/suite/__pycache__/test_op.cpython-312.pyc,, +alembic/testing/suite/_autogen_fixtures.py,sha256=3nNTd8iDeVeSgpPIj8KAraNbU-PkJtxDb4X_TVsZ528,14200 +alembic/testing/suite/test_autogen_comments.py,sha256=aEGqKUDw4kHjnDk298aoGcQvXJWmZXcIX_2FxH4cJK8,6283 +alembic/testing/suite/test_autogen_computed.py,sha256=puJ0hBtLzNz8LiPGqDPS8vse6dUS9VCBpUdw-cOksZo,4554 +alembic/testing/suite/test_autogen_diffs.py,sha256=T4SR1n_kmcOKYhR4W1-dA0e5sddJ69DSVL2HW96kAkE,8394 +alembic/testing/suite/test_autogen_fks.py,sha256=wHKjD4Egf7IZlH0HYw-c8uti0jhJpOm5K42QMXf5tIw,32930 +alembic/testing/suite/test_autogen_identity.py,sha256=kcuqngG7qXAKPJDX4U8sRzPKHEJECHuZ0DtuaS6tVkk,5824 +alembic/testing/suite/test_environment.py,sha256=OwD-kpESdLoc4byBrGrXbZHvqtPbzhFCG4W9hJOJXPQ,11877 +alembic/testing/suite/test_op.py,sha256=2XQCdm_NmnPxHGuGj7hmxMzIhKxXNotUsKdACXzE1mM,1343 +alembic/testing/util.py,sha256=CQrcQDA8fs_7ME85z5ydb-Bt70soIIID-qNY1vbR2dg,3350 +alembic/testing/warnings.py,sha256=cDDWzvxNZE6x9dME2ACTXSv01G81JcIbE1GIE_s1kvg,831 +alembic/util/__init__.py,sha256=xNpZtajyTF4eVEbLj0Pcm2FbNkIZD_pCvKGKSPucTEs,1777 +alembic/util/__pycache__/__init__.cpython-312.pyc,, +alembic/util/__pycache__/compat.cpython-312.pyc,, +alembic/util/__pycache__/editor.cpython-312.pyc,, +alembic/util/__pycache__/exc.cpython-312.pyc,, +alembic/util/__pycache__/langhelpers.cpython-312.pyc,, +alembic/util/__pycache__/messaging.cpython-312.pyc,, +alembic/util/__pycache__/pyfiles.cpython-312.pyc,, +alembic/util/__pycache__/sqla_compat.cpython-312.pyc,, +alembic/util/compat.py,sha256=NytmcsMtK8WEEVwWc-ZWYlSOi55BtRlmJXjxnF3nsh8,3810 +alembic/util/editor.py,sha256=JIz6_BdgV8_oKtnheR6DZoB7qnrHrlRgWjx09AsTsUw,2546 +alembic/util/exc.py,sha256=SublpLmAeAW8JeEml-1YyhIjkSORTkZbvHVVJeoPymg,993 +alembic/util/langhelpers.py,sha256=GBbR01xNi1kmz8W37h0NzXl3hBC1SY7k7Bj-h5jVgps,13164 +alembic/util/messaging.py,sha256=3bEBoDy4EAXETXAvArlYjeMITXDTgPTu6ZoE3ytnzSw,3294 +alembic/util/pyfiles.py,sha256=QUZYc5kE3Z7nV64PblcRffzA7VfVaiFB2x3vtcG0_AE,4707 +alembic/util/sqla_compat.py,sha256=llgJVtOsO1c3euS9_peORZkM9QeSvQWa-1LNHqrzEM4,15246 diff --git a/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/WHEEL b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/WHEEL new file mode 100644 index 00000000..1ef55833 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (82.0.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/entry_points.txt b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/entry_points.txt new file mode 100644 index 00000000..59452681 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +alembic = alembic.config:main diff --git a/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/licenses/LICENSE b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/licenses/LICENSE new file mode 100644 index 00000000..b03e235f --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/licenses/LICENSE @@ -0,0 +1,19 @@ +Copyright 2009-2026 Michael Bayer. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/top_level.txt b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/top_level.txt new file mode 100644 index 00000000..b5bd98d3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic-1.18.4.dist-info/top_level.txt @@ -0,0 +1 @@ +alembic diff --git a/venv/lib/python3.12/site-packages/alembic/__init__.py b/venv/lib/python3.12/site-packages/alembic/__init__.py new file mode 100644 index 00000000..059b6b15 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/__init__.py @@ -0,0 +1,6 @@ +from . import context +from . import op +from .runtime import plugins + + +__version__ = "1.18.4" diff --git a/venv/lib/python3.12/site-packages/alembic/__main__.py b/venv/lib/python3.12/site-packages/alembic/__main__.py new file mode 100644 index 00000000..af1b8e87 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/__main__.py @@ -0,0 +1,4 @@ +from .config import main + +if __name__ == "__main__": + main(prog="alembic") diff --git a/venv/lib/python3.12/site-packages/alembic/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c8fe30bd87bc24b264a91779179015e18325a52 GIT binary patch literal 316 zcmXw#zfQw25XSBNE231eR4mL$kw~ln0|Vj_7#SiVD^0A{s$)mClM=Q*1<$~<@CK10 z1}3&j-IzF|a>L#4KHaA~-N$Tpj3Bm`pZpo>muvpRJHY-5;0;x%!UEMe$Jnt4?B(7c z`@*k-JOB>zun*(t8-pdr!=zNwvKI^Jsm2M7XqQ}?*CFr9%n>4#7 zhFg|^XI(78RtQ!vcu6Sbl3PmCrXP;(YgM&^J?Ilyh7^Vr#vF!R#wbQchE%2$rfdxch9WK| zhE&EB<~1y<86m0}87f&TSvA>Sg7}(@w^(u$GxKh7#K(i^`1o7wi8-mcNtwwYse+>X zbU#hzTWrbsd1;yHMIZ}7O7jwPQ{&@TGJFOZ`72DnJijQrxF9h(wOBtnKP6SSJhLQ2 z7sP@JmZj#E>E~o7=@(R%WaQ@=>lqs97iX5F>H_s-C#D0nK`qsXIYY0Y@)n0pZhlH> yPO4oI56~KrbBkqx#0O?ZM#j4g5)YWUJE|@*OWtJR=*YarBJqKlfkmna6j}h;&{RtR literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/__pycache__/command.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/__pycache__/command.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fe6a47b519278adf1c9528380a0aa71d198c979 GIT binary patch literal 29910 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 Tp?43NTKLchgJ^v{n->2U6yoSM literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/__pycache__/config.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39847e005f90d402cf81fb1f91e79fa86af5d0b8 GIT binary patch literal 39095 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/__pycache__/context.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/__pycache__/context.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..041373bbfb57298eaecdd1481f2a7d4a51e4cd45 GIT binary patch literal 374 zcmX@j%ge<81dVF-sd}jh=^|#JmRpj^MX8A;sqwk_DWy57@yR)f#l`UjMfnw#x7gEj@{lQNTm+QDwrE2zB1VFPi4T@g3X7Lb366@kPDW=2NF ly9~x3I2agpFE9vQXVAIGptCZ3b>vRA-5ggL9E&)CssJ6lWo7^X literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/__pycache__/environment.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/__pycache__/environment.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ed5da498b14490a86c1f2be9c201055348e4ba9 GIT binary patch literal 227 zcmX@j%ge<81dVF zf>=<&GN3N~oXjNsg36MN{5)elLnHm-%#u`Hpq}i+bfC7xoYdT;%w&BeVo7Fx-b#kgAnm`x^~>{%vWp86lT(ZJlk-zjb;~nLGIT*K zs9;%YUYUMQW|DqEWl2VUp0S>xk$!PzNvbYTPj+HDP+MY7YHm_yvOe5!y@JYH95%W6 eDWy57c10XOdqA!!1~EP`Gcq#XWl$($19AYaEj?)f literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/__pycache__/op.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/__pycache__/op.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf9ebb26050df5564159f2a4bce271f48873dffd GIT binary patch literal 356 zcmYLDze~eF6n>YqHAafFn?qGHG>19~;ve9kZlX)LBu5*2$%VVvnoa)-XK@heB>ovr z4k8?ggOgjKo0Bgz(Ko#B2jAm+FWs&SI5(T0$phk7C7Nm1WPXU`7Gj8rfRvmQ0v2#` z(Q1PKh`v3oCGu9or`EzGP+Fe-O;CBnxW_qHtVm?0kG!^7m4DI?ndZvIm|2SyX9W*Z z8D9z>M1pA@c>AtS^`m{&DFCv+m!Narl#nnp6_{Yg>``xUFrG0(HXPL=h=CKJ++LXU+9lrJ^%m! literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/__init__.py b/venv/lib/python3.12/site-packages/alembic/autogenerate/__init__.py new file mode 100644 index 00000000..445ddb25 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/autogenerate/__init__.py @@ -0,0 +1,10 @@ +from .api import _render_migration_diffs as _render_migration_diffs +from .api import compare_metadata as compare_metadata +from .api import produce_migrations as produce_migrations +from .api import render_python_code as render_python_code +from .api import RevisionContext as RevisionContext +from .compare import _produce_net_changes as _produce_net_changes +from .compare import comparators as comparators +from .render import render_op_text as render_op_text +from .render import renderers as renderers +from .rewriter import Rewriter as Rewriter diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91e699a98a49d65df5b659a9f3bcd042354a4a2f GIT binary patch literal 622 zcmXw$!H&}~5QgohZPKP)P_+_A4oFB{scXT)0SO_Yy>r;3FP1l%ZLN*tXyR?D#D%Be z8F&`npi>g=)~LO4wJOcsT}wkx3uhaRpe%S@YOhIKH0a8Qg0$20 zFmus+G<&}xgTj?S>XRGTnwA`|UG3qsC+S?B*4N-wvC;J!fyEwc*t_^$b?jruM91EA M?BXZ8zy;0w4=?YsWB>pF literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/api.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/api.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..328de987a221c887f748996f365de4dcbc1659e8 GIT binary patch literal 22967 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
?M0liMzYS~uMHTH0>*zSnz8 z`gZ31%%8MOZ=5LkL*F>}&beEI-=2Mc_GABK*&&2xb^~o2O?zcgFPdUU_!%^OQ%L>) zAK_Ix!WFjz%>4TCULv)z=CYKtFVR?PE~Bn-hmp*o%v6kbl1;KNnI@e|Wy$fjQ1Naf zaz!7*Y10^J@#XC0kDy z$xwT6#Hdp_klbBvwz0~(V;)pQMtcno05)PG%6WQw@i0SA3h*MC)T9&~H!aO2l1fgk zpmBzRXeijlY;F(YQ@nZ(!DBEt88ch8V9n{3<#dWjfgWs+VZz}L`OrW^b!C}8vXG_2 zObCC|GIX;qb7GvjGqXC`*BRcckkKlQPxBC+q|Cm2q2*E%DFhE#hP3u{{Xwy*i{_M# z-VI0mJnU0z_GvMxA2D_aNdY-PDjt~vxr(E!qcC*GrZY;d-$L90@PK$K#MKnWi(Cs`{Fu|{h4j?h3$?P9=Pmn=WGuh&W=!7aJGcXexWI>6WNLP zCqDKMm%|{kI>(wkj!7gJSSv4P+p_t6q2=0(-CtT7(#BAX%QbjUbvvV zf!DC><#K7_6coy(IVmg|y2!6lb+XCp@LKcpSV?lhyiO%9L_AEA`gxPtINH6y1S@5b z>RzWrq=Yr~KA?zS`3hxpQ$ljsS`YdEDJ~h`>_y|JShNWFt`>k(mbOSOQK$vw?^3Ob zXX#a@bzx+g3zly9=+}Jo3FqtQiT+$LW)Z9`f&QYEOp2su#p5t!8qp3=x8iY9)buwE zaRlTf<8jV(1M#@!OH$-AtLSqyz$_(auU#A3EFK}jlh#*SvU9zCAA^Cg41G~*N z01M&a<2Rn$YJO_ld8}*?xIE<{VPg8mXSRYP+s>)7-Hn`}(A>7|Y%SYecxV>7`ffbC z@%e3MZ`tlcMz_#8vF#i$ITv#98(>KWt*T3wFWnExLLNuAZM^7!wa2f*V}|!hqbtqN~5)>i?PD z;S%Z1K$)Pjci7dr+uT!j;f~&d{pALN5Ee0AZY0P{9ry_PjIAAITTm8vlnqRl8wi5Y ztiRkykQa((*+-CHXp5Aa2x_MK0fP2H7rpVquB*`)W0R|0A0wc0zh(68`kM!KKzv(h zGm`i-FCx>pY!81E>0Sr83Du4>!r4WX5|%*V-7qnvqmIO74OdHa_!|%z#z(=OZTBdHIoF8DG$PfO$zo z^qLjnBGErWRu`%~$;ADzNw$Y;Y=fB34iLX!>J#EXEB%T5W>lVCFfj;meS5Kiwsl*I zOsr1xz3@0s@ntFt9?dU!-RujR8#IBw&Tdf1^r;28^F|*zzJ1pGpqJz;;(;N*jY7&R zRI%i+2QDiV*}0t;N57XI5J-tncp5cvNe2k z!}+mCy@9dVx*&ln^FZy4THFz}0M9BKA^q@lVuY znHK5=w$yXf*KY^8oi$D4aS>q#ctd=<=0a|7O%@)JO99z67jng#EIcB2Xm76M{FZy& z{+qUcNQMU20y!k%&?Q8s5I&B*=m!=*wCY_n5{J*D0q}&96sH z#vx%`(Y%sO4o@|XpJ)hitZ^oD_(Y=t&Qh{gXl8yAl?Br-zy$)miyBy)3e zu-3f?OU-wagr|Rnn!r=H5Sb}X%oir+ixbBS6UVnFPHZ&&*caLg4{yfr$UD9xrT(E} z|Di(vp^cZ|QW~H9j=b^Gj(@lm7$^qDe;61q1qX`3@j`IC7>pKz(XH9z#o1>Iv(N4X zpJQ&(0|oy9l-U~Gf9EJ772)^+{<#feLB?y}tU6};H=!!|sk)dCNXYeNK^gb;{)Bi;s9%7a(9(=g&W_k-jOx2cDJZOYl-J*&&Wx}_N=j~=;*EyxL%JkRKq$hB%b3f^->gEqCs=`_$MiyFpIBYO|Pn>PA=9n)f2Nmo$M*Nn^>!9Rfu1hD(L-pRe*;^OZ5AMm@OKMam2&i0}Jo z&xaxup|vz()T`V-rTwu=NdrRru8;gi^qaARV9ybw1i5@aYBEY(Z(vbhmUt?qJwIcV zU5Xk|<&N@hqc+AZED^P@xs*QS@2|Ec{s&Zcbw3 z>e|qG7Sm<=d&z`G0a+`{4hoAHE@HwLs+)F_!Vu|BXxeJ**mY1O0md z@CN-czrBGQ6MiFITo5K{N}-qt&g2;{Z=$F&5O?;&MJh9+Fj-pp=P0KIjqdS58aKsL z7h0$?4D$tb%0>cvf~A%sQ+}8d!UL_DeeT2Ogu47AHqs)Me?bWuo|UI5`E4W!K_^P7 z{2NMMq=e0h#=s`YBE*^e0MUrP3nclLqzHC#N&k3glcd>oE&^}&jJ%_j_vRK($LZ3h}55N0FF+5QSPZYxk3*m!z)a~%$ z?ZA;AI9UADXdyUS3?3*158QcnJBW}L`w3g~&jfq08Fdjl#M<1NcyYV^+*a`1ZfB&} z87*{1Z@*Q54Gm!ncJ5N{{1`tes^Fh0`sWJ%Ih5g%VVj{l@txoqRtf@D zPo(7UyZs^_d%PO~^l|WvjZ~tcKl63m^M{N6Xu%)l@{=Jw`GZB@)DL}AB>?{Ef`1yJ zV4DYSJBt&~6egZAN+F&tFj@$VZjGHP1Ws=_@A?9F{lTr4q0LjbTZ@qsg~*8=|H)l( zq$nQR5f5QvrJ4PFdJi1J;M1HZTg4!v;EX5#PXLcnzTkO88GhCCF|I@DB6@SprnZ81 zn*PPQK1ID9)Ui_?OUqvUmE~6X+Qd@MR{}w$`5~TIs0l=->L!hPIv)j&SY4FUp z^9)qLrj~M`#dT~q*j{$wj`uyV`VvGR&XpSp^4iC|WgkI7vh}tQG}i0#mc-ez1NYs= zu7@t%%SRk8#P0UNgoXRuNp|0DYxxa+%bmU z>2d=>7(-9FksxlR2J~>**5nF((r)SMe^nDC!={c8 zKn3o%vK--~b^62 z-?v0p`NVzaGxwcy3>aars0xs^q4UP&qPwHu?zrXIb`RcgJam7_ zW;;+`cl6qNOHCjLbdT(3_gz;jXoan}e9|V2PL>A8O1*=nzJbzYwDiQG(!_MRWuMJm z3eS}txbOB4mR-0LMHnbI5abcsyUUFPc?Ed2d<6N0&_ub3pk|?St{fmJD0Gdnwm{?D xjSs_=w`dEYZ5j~Wqes{s?y83_Jpbk7Rfnzpfk5eBDh)5#Z3llQP|B#x{{dXQ7q;?fZtpP67pDAqoV+ecuEE1WAzuWr>m{NHh%c6)Aw&^r}D-5MY6_ zCIW3G1S7?SV!8#{`bMCw#FW!BrZefWvb#Gy-Bl>43#>rhbfRS9nMrq%P){OFPdxv* zZ!O?~QZn7&H{U#h@7=oZ-gB3C?>Xn5bMCo+=JmQbTvzJ;L-)6?bKGCj59P8*0e*25 zX>M==C-A*oAK$_A6gKsm`pg|>7B=@Svc5R(pTD1%EBRp%R0(fxB%huj&g*eySi{0)2v>Jhvv4`!)^yac@G69BJ8D^Y zb#GnY+K#m>T!Cdnu0nWS$2t~XgK%RS3)djr z)X~JkwFqzM*ucVd2yg7z$ii!TH}!4q*v!K9y<7UWc5G$gXz#YZ?H$`$xS@AP-_DMm zEWECFSKscA-7MUQ@Scu62(Rzh>*Tb5!dao|6-&oHK@c|JxnFxDg@l-}5%C91Tx@mE zxH7P`=MUuIWxk`ik+o;;2m8N-Wv)O?P&<_g0Ni&@7Rj)rZhN8 zNP#Ex3OjT#POJ#yrjk>8sxbP5ojMpN^ZKw~*oANL*ott!tHD!3D%=5Kw+`;(E5i8i zm7LmBg)u1X(ZM*iB8=~Ad6kd?Bd~#!Z!1u=ry)F1*r$W{^osC)q`^@_ z3Or%|E1Wt?J6447KPvgOrwZYK4#IOQLik-RrxH>iWpctqExudE2~e^#JqPX(Tk z5Sn!`zPcieKhW|jAr;1ua8L*1%!)AnM9HZ=6&QK#x+t{h;0Y$~FekLW!U>0dV3Kln z#Dq&i8^-%N;jnN7|Bl`?>mg`i;j-}PE4GgFT8xtuhJ|Cu`@BAm!orAf9P!=4iC4Ie z9^ply9ltK%c@odwWvNGn#}MDAj~B*-FCng9_?qxIo&$jSlKwq@j|)${V(l2z#|l@3 zQ%Lh`crtuk@M^N_xSya@MS+e%G*S%d-wMAXJcGE4NcE~dUJDBo zLI-kR`aJ5;aaqeFg@miZvw%GuyKIQ{Lp@*7zOE87`pN`N_=fPD?n{p>E9;xWX_WP% zK3;fD_zL1iS1kW&rBv;?V)?%+bn40sR0$o9UL}Cum6JLqkPug)!#pm=t^`C z^vChj@kB!G?mvI@WIKMCoBN0Hu$=9RC+OMQeU?3sceCG)lY^A4s~7LAr-lZ5V+eRp zJ@eS1&XyyGT8_;Mc^Vo3W`GNk&06!5E5n+p;eLmLLg^)S^0t1p59*j{f z{GF}cUA?ihiDvQqQ{Aykh_aj==)Z`k^?28r7$tVUFf@>e37!32eX(B<0-;1hiEesl z9vCDrZh-CTJUh@g*hTfWAMZxNth$aCL=GW4q8&$tSbw5BK~43x4D=55^&jeu^&t)s zu2ZqgiI(23p*S_p-|~Fy?1dI|RYL4SGZ1Bya!}H|cu!j_f#wNMb@dKWR#)4A80$XY ze=J7jgtcFJxvWR~h1g{zaGZ!Gx>~yu)H`8xSznhpjI4Qy1L)ZohGKbf*5=0_Z+?c# zJB7YH)PKIapGx;N44LQSWjb+Is^4#$L@vePxn zDFb+vS5iK~C7jY*j_YB|DCL**L`u1&C%9XDd(@V7b;rB$HM;uG#>5a3W$m5k*pSGs z>cj}@JJWr(vrB3_K7AtAIVi@?bzc@sC~fgs^-HomodaTL{{TA7l=aJ*FnU;zWi2$$ z;?%ZCnS8hUX1l$+|b*7X2T#G^jn%XZQc;a z@LP{O7rM?Pn_RmM`r2&}WBC4J)8KG6&?$vG@zq%6#GNR65B|qD<2KHH;@}*i$(oy% zIs57bf6-j=mfI)O#jPI*Y5%dL`^}BI=E%C5ETXa|F_yIx%INNmW%(08O+#n0R?PSAK}-w_ z&7L@QVnp15JBC{~zF2=kY{job_#b~9w{dR4?Y*}9>h4M5=E1jG-)v30t1|A|l)HBN z+>DTRZ^^iKrQEyH?mgqJO3HIL&%7nPiI>$G_u7#hY_DOk8}@a14DzrhC-|pB&4$!gf1Hr1}^pI$FZ-i zP0_P`Lg$$QVL0nmGA9Nu#QL*-EvFP==^5zm2i-%dAU!s58xHxcU_ z>?Lf~C7zE`hYZwBl4h(D7ZsHJ{Y2jfldYH#V#=}o+j%ae-p3J)T)VlWn(`;I6OF7m|xF=74V&k^% zS@5iydUj?*+OvDnY})FY@J?RH6m3Z1&#@5+b{?Pvr)N&4J$sR0Cnf016m3ZrZJBj! z&3OSW7vdr{Zwq55gh*qZ-MSEne+M^4K-~r3 zY#47@@Rd)sq23u7jY^)edcCJwlo(#x|=&4-!u1Sf6;%%~;nl)h91xhpis+7NKdQIA2JAP!r z6S#K#>hU*ArZ?Q)oG#s-_UsrxH17_KAAd;K5Q7AvI^RR|dC8?i4+hP}lrXi6ZO9Wf ziL`)YHN0ig8YOdn64CdmBNtfpGFSuTcm@CCWw-&G+Bj5y*VSEbJTvvuZBx2%OWM6P zY1z8G>NHTMs87c~I_Av-m5(aR!{3f)loJz$+Fb z-b^6K2CVqEO@ak)45&QbSifUcMiSr})(qIOl3`ar`hZ;+hk4l5h`x`i7#3KwiZG^O zopq_8Tfjf1pQx2j{5P)A6Wn=lm(|=RF3w*v@!T`$TdCa3=4ZG|eAM2a^~)m&IP46~ zrnZqXT0ukx2AkM#tX;5F?M%=DtbWAV7?GCB`$l}xNLM@}r5PC9E$&0tMV(p8z+fVt zH3NZ4xYgT9;JW)I=viCW;2>~x*2Mry%L(GG*f7u9Foy9K$eQ>OHiyMt3SFSvS8)U1 z3w{W*UBb9=8c8&zaY-{GtZ*ECY~d(XTqG>@4g8NU;D&kO;5_9ScV)_539h!N?8fug zpHEh9yj`0v+Btsmp56KC#g{M62SO9pyY902U?dZ4N(Gxf3~rnc6yEUsy60YT>5Z}1 z$EMG`+x@-noS84&^oiLV3<2!w%D38XwoSG!RIHux-wCHH_D>$350~CJe*O4#@rU7P z&dRN>#q4+FJ;P+EzSDEU2qaKia_Ra*Yf;xr9@+T7T(*8GQStu34Ed5dLrme)OGmWyVA zA2aoXM;%*XzH69VzjQXj>X>m<=924*pUq{1HzS}f{k%Z)!eFM$`3&CZ@)_WPD}^gz z2+Ai|-?ROIs2Z*(XpZLn#vB=W>P2a-K6X}&5icx_U1Be=-l})KC@s9kUX)|T8r7)S zWsoFDu}JhoiTAyz#EiM3BQJk3xNBcb(2{>_tp@iWSCp1dp1dzgPt@TvQ?2-!Wo>PI ztw<{p$T4hETeDVHC|iM2&9e@r<|574tW9ck)Sh*95(C5#LDqI|Kn-N*+A6? zVNB6LDC1v~@~=tyn*hNZoZOr#s7n>p&3fwY1q<&X-!pGNldRo6adFD@mh(*~c!qg@ zVXlz#lq_;?k1fZ!!M26CX8sVr{Kaw-OsI^J;QxrgO3Uc~4^K{MlC3Z~*`$(XZ|Wpd zf72vuRwjcWg5zFciY*;S`SYpoi-0hgO%H+kkf|e*#8JFNH#T*w;$;dC)9ppNF}g;~ zCDzwt6!A5>X;b4RHPJXi`9r@<8T!k6LnH?8Us8_W#BIsIHz@<(XHf<|_jpGF5m ztR#;jEH=!V@aDSdwSWa_#>~3(K=QN@7fqvPyhkPIy-p5nG9p&UZ;{I@MShEJk&WfL zG2{6InMp<|<*XD7pjc%nR152<^@?fKs@6hfQVCs-*%BHnsxpv?0Bb&O7g{llN{H$1 zQH4y(JM@B@gJ20}2GQOh{Jhj-cEOS;RN$apw5(ckDXeq?Jt18>2EZ;@Z<>jqjyaIK zOi7_V#qv>yV9P7RAeB-EK^nnw9nefYt28J|Xv{h46oY_h*Fkg|=B`lkXwNa%s7rJp zw`0`xZPRy5Qc1e8!1qM7)JjN!jwM3_YUW0M)2O?rT6wQM1*hOrSp9GFf*U;&(cUVd zQ8#rY%C|`6zt)I_gsG=Sd9OXi2Ek+WwFU1hE97WZO$n4*k9kmE->65|vwTmTR)!L~ zLf^@sh$_EPcY{<4R2?*j5Rku^;|a_yp3SdI%R?45&U%T92HQtW4bOgNYIJ4!(<4o_ z^%4UTg$PpPw(4u^8A%{UC@MC(YU@cL8I4EHS*QAfA>gcq-i){#T{5#%D~@;?T?zw+ zU-m{(ke?(fIBAEs6JE3#+q~kKTUbwf3nk)BX=V8}FQWK#_}9o5yzFg3v_EeTZWG z4`65D$(aywpI&|9;hg@Qk6XPavuge9s`ZObYnksp7LiSpt)JMrVD)^sghYIqb`^^-cz z;hxy~_2D-T%-Yw?7Zgv`zSZz%!!#C{4XJ{LnI}>O8?QR<6_niAeSP2j&AM_wANPc-wu>LZHzV4%e&0FAvW6PA0v0%Ent58t)>D2@M}x|!ndZAb++r5u~^+8rPb)p+Y&yPICu zPi>v5PX?+}j_L`^$KHa;gEtOcKlE#-C-}P#&s5i2-EVf!m}j2&uwv8g%|G7u{A8s^)Y-^MnJXBm--v zpH2nVryT211G_V0FHYHullE%%uJ%34%;TB$`%>%orR(>n0{c^r{YYkUOdPs$LRN%* z`rs^&FZt;M`jnqO2%*;zMz_a_a=uk?c%$XV?!((Gzh7K&c(?2Kce@WCaOJJWRb9d- zNKfKIO`D*RRh_udP)?=j>7_tZkvc&$Sf_(TK}K!Ze%kqAf@AnLTWk5p**USo9$+e?@pXjSE^KXa?nC!jW8V+1%tMRi#|3O1YJ zn44&owD=@!lXa@eBs8L1bu#pj&v?5#wQ7jJjr_3w<>JKUWBR+-DRW>USd3Ml_f2m) zSa;uQ-hjDaW^)0$aCI(t-?2@L?+(8`oQ`Z#2RnpIqgO_g_DVL;BJXU)bn)Esq^q~3 z0^3rKZPGAnxpFK|->PSXX^902#V?8ofHFOljRxas0>f-(3^N8M)1ZRw9W{NMMjTN( zcIlbtZ*$)v2Dt>Rt9i^khA+j4ISYeeLqPo+@_^1+p+@P8XNwe4F$g;t7_fBp;=2rr zvM4_56C@Lw&I^|aFK5jcV#8T8^eg!SdDuzFvdH}OovaY?e?XD2aOUEVB7jwoiz{85 zDQQfVG|rVYePTuL|GcE>>XC_-$pRn&kAI?e;)S{3T40LKbZ}oXuz%KZV7{d30j11w zW~=SfMfzl{4sVoivE@xob)_Pr<3F#9G37WTBLTM7AYqz?3DYd#?1*|VxyDV8axe2b z&Xf2#gDIzQlUO?#3&+-uT8)%#nDp9e!Z4?Pnkr!u6DL%)whkSsD-NuEOg-TxjloZ) zo(WQ|vCFvKhI#RQ=@KkG>Iy@Op?jiS36rWwDMzU>N-k9)C+P;i4~&Mg5##0p>Nx$4T#a%K5|?`j4~rk61U4h zjWQ1$dA7D*X6K^u(~XfKW`nh_wq6QE#oBsl!huU3>H1t~GJ_4~`U?P-SnFt9Uqm2} zWdrvq{wf;RCengl(hk@qT)v$mzDu{?z%6T&>MM&Cn1Ybi~_XdXSNbnO9^Hfb6LhNFw`UD6pP2n&`|s_WgR7T&v# z1eM;Lhxcv+-a^Pl@|rzY_axnGl9n~Ne+&~G|DD3jt`n(UC(`bA5Qcny<(m7dI~lHf zFFbQ5)3iU;v_Bm^kP07|b36iz9hG7QBGZ+(Yk%DEenUF2kLZ{6E%%%5Oi6vJq&^*t z%DmEoXWh)9+XvE~7RJ|tl>$rHc)f8pw063dWJZP8UcCAuRHU>1D#mQxd}{j8yT{%> zmZ{mEs@a}iy<^t9<6f}f=ZzaDYo@|)mA+Xzz4hCbv!VLiXYQB#3P31*j8IlUHK|w= z>8Tr5SrT>o8T#(O7V6ari3n{OaXxY#7;2NJvRV zM+`khWvMMg8*G_MxInyvThu51gu+a8t6`qsqBsN00|QXP8Inpfp9Pf^`Tr9#E=L zUFn)_v*B$&3*9xE;cgqva4d+hEQH|MaE7bvp517r-r5smRpm$VO1|iW(KQNwRS~S7 z=b53zDe$1a&Y>5`Ku0!&hRNE1?x*-Xa^?wQirDgAqB;76e}aT#)b}t3K;NTJz2#Gz zGv(`2t|m2z>@YHO1j&UmbT@hZPGe2p8Rp#KObvpe0LsZGF-VSGhUgG=Xo%e zoDqu{Uk{5c101C*O^>T+q%XOYGL}ApugpeP5Gz17!dvKCx)jJ-PJDF(Hc}^cN1Y*d z*J`fXmtl?Km&EIaP_f!sdi~R&p4ZvDoyRErE)ObU`~^aw5(ccRfVH>s9nqIo2#|N7 zRbcke`m&|9+1g69t)RPt(3+X zcq2wup@a^>nyAuJD}4+KP_4vjPtD48)B);3biyc=LxDn!FPL7YFn~gk7+^Up3yw7* zF+yO7p=pM#%DEWUhzXdd(teGsW1xR%5JJcpg@jmd3?XNCe>W{bdxx_wfzpdiQs~6K z3CWfvYlVgyW@FUMtT#R|B%Y1Q0NJ{BWT!@cWX>B}ZQ3!>iG?eum?U|wt6L*gG(c^1ezMP@*s^=n8=A{va)Fv>#YBkPf?#t5AF0!6{xOYQ(k zoan~c-Ae(`mp?#en2}PkBJHKpHcZ5?PB5);)|nBoh%iyaRqxa@u~Pd%0(nl zB>j5U%1j|yGq6oeCo`4gr6Gn;&t1?X)4NFa3L#U%s7yr}3TJ{Whg-NP&aoL{ zxL1!|Irgz9@J7*;DN_*IQ8!ia8|!9Pr-D1u zo}JR$itksXgFA0ksBbG$!JTQ(uAJTCbbVsBS#1w|4xeqj4XS%G5|ZlTyXL*>o$Q*b zoO49~(pxaW&$~U7rW@Al)^F^d4;PVua;ob3k%>d|{&314nGb|-xUajv`@*f-7uxx0Hn@`+1pPs6sIAiXhk;OA zO(0LsL302Ei(e#MsVBt>(S4a`rkA()Y(e`#;waWYY(G*=I3$V#BJ2_~$jHW09+8#C z#9-c-`1eTpI~Wd&M4`sHk6o+NuF9mPayc>mqO}tLoC^O4x4gm&*?FqQ-2n$pjtgQCA{L%^+;igg+_;QDB0CaYk#Cc+2O( z%4@p;)mKVb`lR*Jm9Q~%fgiPy3RlTP^mWCwP`WQkYg;j`8B~Z_SO-F69|?Q+PUmFb##52< zR7@Ao6wSDjm0RXKTj%Ybq`hR`A!mkq_0aULWcm6zN7KALkhHJTUUkekr(aB#Z<%v! zz2^&0Je@48ofh8he!Kg&J6*RoS-bDfmy+%SNy~wUpp+c?iY*H8TzX+@>d}z=8nFh* z1+fOAi*uIekai40ecBpj%$PE}FYZYwrk{>9A4gZmd#z~RNTas3SgOUOymuy%eg{=N*1Oz4VX9b%2 zM_K2KBUMZdq}O`a=gA?XqR1wc&N?e=lR!#y>TUEm4323+NJ^vsokA2V%_PP%eTx$5 zXA(<3Oo`OwkXXu_rNkDzFqlMci7+u0y!f|BGl!`YpGE*Il8tk^uQ{(e=N&HMHRvJz z$~a0=j*^_y+GtBz>c`tA4oz;K+6R@dV(vI!u#Ggg2PPbM-NAWJI9akjEhI7r!Gz2PF%|fVTs_6Bm`NE1XG}wtAGB)`X?kgd3rg=9&<%|;qf?j$t-p&A>uOxv6sy5^W z!ND1GcVvb~cxuUIB*%dQK%2f*`3{>26)=y4}S(I5r&| z4|q<+956TvsR-(&mEvU*9f?#2M?JcHFzeIhvyFR4-MY8_QMc}`Q}-q?>eRiVe&EE? zQEy&fc(p#$^+hnRFMMLL@t2EhwGWN@bY%sF0>*#@M}uG%!tGfvZT#%+hscHY8;rOW z1{BXcMuP1}lF*liOGnn}7>WE8%W-K`md-Hx{h#p7#b3c~#K&+c?baZ^cwgIyA2O7S z*!%=BQrA##V&6!hKXysxVi@M!Cw@#Bo&7_-y(DGYH{ww23`c@0G&L%uMrj#*d9B_z zQlh3=Qn>h+R8hvugd|3Vka4z9-shB`I_p-pKcOXAzx+cfHS3cjlGR$I01{uKWW{Q-{7Oiqhb>2liNZz^5`MAH)l5e6vqTGYs}=S0WPSOm#J?tR z4z(5}OU&BwZCL#m@k{)aZlBSOswn13cS z3d{Ebow(t>v~O7M9VL zc&zuFw&>3{baQmVETTKn**OxB9n~}`F(+tf2LNN7n=e{5VR_9BL(~b&uR5hAu39Qm zsJgu4_yNwy(Dw&uu-?+l*lUXn6?=9;|3DGUFbh9dFCjb`UlU`>3_PN|XhnLx`x7*W zVVAt-NHJfc<`Bby=B$dmb?n>6;OD{eW9R$M52L%2{yj;@9@a2;@nM|>Pb;t-raqJ$ z`Pjr*Vo2=ITH2XM9uYoGIT21y$w>=}C8X2Y3GCJhtUA<7VNYl03qxJK(i;&zQ#lb1 zQaSM&!Q4f+9dx5F&Thx4h93NSg#VUm`k3S;*bpA9>eo{vSkpg963KQAVg?9px`qxyz54#;aj3RL6anM>G@bcSx}MVB!b)Be{rDqVoZ>$(GHej z6Z%FZnWgmYc{W0HLr`SHlWtGa=cMri(h%Srp7GInf5C)%!C5fbdUM^Jvu?p3n%w%$ z;iRKPimRS;RW&TYPSzf|zBlae+ zj;5;4@4;G+Utq~4_&zwwLrtEfQNIIEzkTzQwCaN)6CN| z$M5jTlHC(We;z1V2voCp z?^GqL_oj;XVkZIOlYz*`*dyV1eSgwZ^|8NT^3XS5OghRW0*I!l7h!FNj{g+`D-l34 z!j^ac9VJPvPK{(rsA$({x~T)aQd6C1a#`JUGO;B*O?_O-U{{{~UY!jvO!@`WduGLy z21XQcVuhWMikUY;P`F##+1NM*QcGhv>d3nFW|K5jv(9*zqBy4&IU7O@3uf)Gy6j4T ziqfYCA~Sajm)WSwngJz`5fOivveNDAG(^Z=2WT_Ig^O<-`1J#eUOTqXc(`!&L<_dW z1j?qWZw_ZFH>4^z%=tHdLi@7cD7tB%Hcvk_b1Gf3IUOYN{>PHt_Xml&;PLxbllKG< zwl!RRpMd2!J8Zuna0oJYRo9w*%@Io;<3;K;_D#cJZ*IVe1{2{Ra_Pk!Ht9!$oIqT? zf@>tOa7NPpbE+JXsn!h8?urmWqK(E)9z;w02#{|ZQj#ER$z|kdLEN}P{q@rSFAPl? zg?V){#kZh7Dk9e^C9YkeY`r}?^`k*vWwz)`(m7(ITq^q4N>p+&67{zlqSuj>&nZu7 zAxyJC%;JsEov128-5fRUF{5^1#?ht0m31{XBPU{7S!iF0lv95P_JJD^wgCjS8nf$a zWH;WwmV4pro&0l^CGBtR~;35UY;5RAjbR zIuj^jbJ)HEW>F<++1n`&Cq{7aznG!$Hr%pS=J`l6`6tE&P88*ROmK0;_6{)XuJ2I> zW+%m8j1%QhBu9#$u|J{&bekmlwiY+gHy2l3JKn0Z>b++VX6&nGN%PF?o+h80-kqu5 znX2BI4DXtA?4GCh<+Jv3<^8kM4Vg9DQ){*-nJw>Kclgah%CaR@y(Lq%mHU%yd%#_HV6x*z=dX9lN$0%OU1VFlh~{3@GbC&UA?MG(f$X4)f$=f9#mNg`|A4!!S zO@|+ya~zY?zh5&K-tm6Tt(uvtnJ;~pe|ytxVeRdjS;?+h?!uFEj+%M@zJ;RA*w9ea z0wD&ItqHH6#Fo8go5BjKK`~eMDF5l7hj)DXz{dsJSTIhw z{Eur^9o_2rlk(uvU9LafQhW4(0!0Uy8TnR%6@`w*`h~ z2V}7iL`i!y;h8jqHM^)tH6+{p5p!+435%OvfD&3sB%FTHM9$;yvZb92 zjNZGj;`i*`0FlnRPec5tEkYht&4qLuxDX!z*x%f{DtL+W`lO;MYf-iZiyR zY@yB@t8=SeZfs=g1$*4r)N0vXVj!xsY$97*3n$ch-OK z15?s*P->pM_&_F|{NW6K(B5Y3x?wN!Zx=bS%J_{uTFG5Y0KOq%@ecW?Nm;aj@wNyi z=*HmR7ZNHHjaC@I!UkAm+eO<%9E=0`oTMqROLD4Cd8A6|M4sAC@=J!WB{_ds8mmzF zz@Mie?-&ztXb$&psY6l%L}Lck}y7HZ)6VQYE!}5>AqBO>x2zf7)e?&2RSx)NtZz_)&T|e-FqGA$T&lFG z9xEbhh0>pbSF)T_z2?|dE*$mKg`JSh_}ZDxS)2F|fFLq$=Pwb0GwlTL#6_+sN6ULpmBZIWlU%yEXr>RMty0l5IkYPuuF8?=G zqoFhY{J>DJf;^Xqt2+k=aQFZ_tD|>V+D6A(K&(!-u=&YTCy%zbJbviJq4raqt%us0 zpFDm_q}GWztW0*~!pN@1-~ShS%PPg#cqYrdLvgq1#^g^f$w-grH^chUE(b;c=__Q- z=zy&4f}}qF=alMqa5H4)zeR89_UA+o{v#?SISsCvJ~DF-T%NxTx?Q_}awr8(-d{Z7 zzgJYk%wNIm+r1Kp&(w{fjHfE)shU2ZDIGoG1^=%3Kv5=8n+nu|?+*AtYJ!EdlVIuy zTz}q^b`>O7ub;V`T(xs9unX^<@Z`KIQ+2zO;XQMXy{OEqFI{;l30E+3f{y8;cPrnnOiEiU@Otd!G3DLUQy1SFdvh!q zCIeYm!AkmS$(sw?TlmbhW%|#f9$`R10X_;+^BBvNHt(}8VwEVF^VKU;GT zE{u;}J$iTVBXfb~pS0e3cIK(uPyA-v+by%f`a7+&fo90|YS+Dc;_VZenjNW{9dk9i zKCxR0LJ$?#?tarTWuAKCftf2QpIf#0_Le*Txm8CVz+>y9{Jol84_G?0H5ghn+r7T~ zUO(Gh;U_7fpQMBiu6*04i}(l!`A-*302(KJ{>M8)hxZ5mq$YU89RAb7@*~coW!|G0 zG4DWHJux>Ts+t88n7KmuaL166E+g%XmRj+NZI%uYDPKCzpcR632Rm6NYxGi5)6r2g zZ0_orK-#%3(k+Zu#Mh%Xbc#e##=SY^#`cvWj98ED0cY|w^B+6I8Ry27bK~ubwDXai z)nskvmotN^t>`IC8hwW>;cmd}$=nS%T1`g&@Qx6RG`>ZXk;B2PYS5mmJV5C-3sDXGuCy_UlN+v*8q)W{MLo6ft24j>S2>0yke1hZ4LHk)X0WHpsCw4uOOe8~4G`Zz zuBAd9aTtrrZEC%ueP678ob7BBw~5pMu^cy2T#)i=#QK%BqibuVU3npiSP`)iAHhrA z!5Q*g5lQwgHs`%lri!GWtD<+_R>>qxQqzh15XZE_lD?}7FtUa6>Y?+Tbo^4Mw2LlD z@b|MqaDz*GyKG%13!RwBg0wclQh=>ZBx3<0t|HyaXjbMB0Of(Kpe9+f_4fHYFQxq_ zmyn*1?V*glDP?b(xin|rvEZ+K$1>eI<|dm4^FPVxn<5?`SziOP|+LWjf>YWPCbv~W)7!|x28ke zChQ-({co5jznpfjPFhwkUpA?Bx%21_Jz~K`(J!y?mM%;J{yl9CY3e4$j-fTm6|zI6 z{$FwdL7Dd=4&D<8sj&-)0H2>OEY7S(3nRe>CyjxD<@HGb0vAY$wrR zI(wsA)Nj5Me;v^y)ymjcNRWY_!!``##X_W({Tv?tiSM>$>dVuK?|kJazE8#1@kX)J zY(ZG!Hf?NzW$lpIFje$1ns|&MWX#`f%4Ers5hkO2Y)&zCJIkK$Z%S;T%yipDGb)a} zjF7BS8 zm5;E)Sp2U$0>u;bxvrr&u^pj@3_1m08w|Q9@TJAa2oM`zvfhD~3y>8NS;&@24uj-Y z;H&{A>hBbA;Na4MW`^yJoK6~O1Se1SDYjBkbbFMB*a^I6L(Es2@oq_ZAyE%ZhcUp| zc4O=UZdAvQ;<#|>;89)8F&Adi-~o8`qK}WF_(h_wBzF@`8b_(IH{HA>=_M<>5>4Tv zKPj+2%4E6t#B6>ol@zv7%jouHY8h$ZNcf3e+krWG4&=jg)&Cdc9$%ZM6)a6E4n+e{OiA65l@|{1ci+97FwY1~br?ylL(B`{% zm8MuWViC2kBzkmSen<=`*9H=L!#${ixKAMsZ2!U5tFgu{P#rjnCj{)(=!vM+ zPyCE7%C==)iLrekQ5)rj%W%C*Sn)Y#{m>Yux`F8T|a)gx=pi35EREt#4sBApl) zsf}m(rV)0$sATZfG~!o?Z*82}+DF5T>&@D6OarsYDW<7QwNgdq%oL2tP)Mc2j(L-? zVTeIs2Rz`kw(hQeob95Xu=11?BN_C&aP%cku%p9XB=+{-p#E9wd6;VsLck&jaMaUz z5ocs36oXm&`QCvuZ~`ge7g7a9-ArCDal*dN_=WC4aLrP&j3s6V<@I)oiL4ol4XK_| zIReSGvZizWk^;(&!Xw%xanMXsM7Ps~vgm9kAY&Ie=;=1*^330|jDhw|yp-{8O!+t7 zuA8%Cdt_B6v?UeVk_qihg?45_t*H>s*$PgQ5U0R()ecESK{2>imv3U|o9i;JNXiu% zZ<+V4R!+40xx4h{g-pfXRK?yqPtH}eEcmOZ56yrZCnMx}r{`M5)e3CioO3oUUXY;e|VtbXVZGFLF(sEZKW!C{@xr=Q(uO z9hxsFn`r-wit4wYOctz5de+@_2Il=m6F#}5DKBhM=iRHOhBB2~QuuRk`RA8@XE^14 zWFc7cdTFw}ab_s9p(TaC@|IMvb>iUNg2KtzRA;(iebTf3W6cb2wOYi-!G@Uwci_?E zAQT~`m2bl>@!F(g?Q%AU*bYXib|aw2{+fNNj+J4&J5HIBF}{WgN%P|*520)rj@av) z)*X1RJ_^>6t>j1?sBJc(ao%~U69;KB;ipL(ZhCe?bA8Q@uw{V7egOIgjhkvzm*gS- z1&4dVfquv5pFVcG3_}1Om#tPJcUFbm$p{-+I8I%x{2qSj8*I$iDTq{Y*sALcvf$G% zWS|VYLJBeS5WHP3@g}P7WQEHiP-$oi%%fI@`e3PqWsJrxVtb(;Dv&ma1k7M<-?1|5 zBa3v*6*F%o&hH$0q;164L{nawrg|3!772xf8AByck8c|sOU3xniI-?4+dWPNq70QW#&*SvMFiVlyq-eD8Tu8|E{26!j1_% zbw1^*n=oVh2W^XqPo2Ee`tLl=SxuV}>Fkn<_Hr;s7T{!8Wf^Gj1EmBwg{IUWuf zW63^(>cUu?5q~fhN-0hddWCTZNqj|4`WoQzQm{)*Fs}Zf;#3*6#(u37==NR{1)P5OPPvGVHWQInv`Ru z)HE-}=hXtc5n92AzSu{p==NQVhxh{&iFxSbLPe9Y*URDmDF{pKX~#@WI=Er{QJ6L* z?WLe^9|sF3d(y$G@kc)n7ThShUNq%~4jnPOki}nm6COxsO44PUalEv@6-P75iEPtl zsgiXwU%q`A?nXW+O9zjSKl*dKm#9x!(%+bLG%h8`cGs(4yYjVU(YiT%;}<8i*)kQN zLO?xp&f10G#@m(YV6#N;TKMp32JjMEc!A9Fys$n~XzTSrNoZ-f1^k@~_xnCPIW7UgRACq1liw(D^;~1hXNn0iglH@sIxW61v^(s+3A{RX1O~K2yDIwt8FIvprS4 zZK~_FQaN%Q>T^k)YT(&^clZ7|-+`ah+}bm<{q~mMtb4n9)?at0X4ZG$zM1zv z!Y|_ROga&T6|J-*#@?d9P)nfJhL0z!L708=!Do(wFE?`!FcrLvZ zApykwW+NA~+A-XcVVPhqlg62qoNinx()f*0Rm37#F2K+bXGrlFhd7R##^IQ~Ka5j< zRV=L41H3l;BbBKmlUbe6ieSkVe*rVeKr?wIW*l$CL0vgHYPxb_)DFWs0}&JiB?pY( zjM`~kU}V!|#5rje_7Dp>t`zFHA;-hc4nvQU9S%<}O3`EZBDA6yxvcsYjC4iT#=FkN z)<&>#lbp0i2F^v=BoCaJhfQoffsOd_!Pr@B=A=!gE?Vke8lYXD=eoLkaWDv?V<2GU z5mRy(9XZ=Yug_3yT)(xGFS7|;c!^uu;4-so-3GZ`z-8X;SeG&{t8XyKFV>EZiE$ZO0MbL1$bqUPf$|! zP!S$rCp1yZ0AS!k3=ZmtsUSMHiIw4FU^~yj;RO9=t~w3&-DTaK%poW1B=K7Sj9qkG zoNx{(V~FzyuMJ!sNHg~X-te{4S5HrENqZ}y-=On;rdlSSy#gNw6FcBR045^u#AmjD z@5S3)vuk%`Yqq@qWU_S6Ro}hRRd1ExSnRr);LPUf#&qe12{KoK1A>#^I7u!KV6%a} zb8_g~$*U)mp0#l2138s-BP$u(OuYxbn@7ua(zRFesbVs5a=Ukx-1H7?f8G3z?;r^o) z$}qq|E0&>`A{b(?70Mu9Y00%>8P>e-VUTl$G7K;vQw$h=b6Z{+204w&80N6bv$u=? z5-kz`72W=fZvUEY{|nvz4Q`$Mt#F*0zr^B|z zDNJve6>{1>o zq}J|9ui2Xn@0)Y%XOQx?uHaB3SEhbns(xR(THb2H5`7^^#k2NerQ%<{DJXkd(iK~i z;cXu}wgZznN|rCKyl6Z=D!=&G=#E!SuiC#T^ZgJLYGScfGywSW1tqIW;x%TQ{BszI z@*c)wCo~2fnh_->&s{ZL@mw_-5qI(m`wcp(Q)R+6Ed^Nfpi)Qe$*d0y_qZFVcMTy- z*e77LE!bay7RN|q^+n+@X5I>;-+X4Cm@KeQ&=E0~0L;I%JylNEwk?}I)$dnHt5)Q>D(L~9*IM95Qa}};wIS7e9rzXMv{6FVrNDZG&9vR zAsex*(Fx^r0<|x!dsL8$@{~`OY{JQs9Gxu5957>5%$5<1iTE|tOX4JCGYtNm9#u#- z7C8%wH3a*+m77k+4iSq+WsV;{+9`?OKm&yDO-8Jym>31 z*EvLySm!*7z;Bq;?kQWjd#;+Us(a9vG<{Wm(@j#-iHkuuOriwwCc@a{GC0^ftR6(E zXQO2K!1%bN`C&#WU$8^ks6(PC{D~R1xNkf=9Zc5jO76jdi0zrc z$-AYEQkQ%^Qq4=o(iTa?W}!hvXIrw-#Y~Q|y^yUBjh2Rl#HFo^>LL+Gu1jt|;tyFa zYHLODp8gtwCr0> zMM*WlsAv*Uzi5Mv>7IBfkDb!1!cYiZI$Mnm8bvjRW+c!|s7$I6rBtXXHqj|!Gy|57 ziP4OxrbTlcpj+A!jW>Xzl%*gt{h|P;Xh8TST^g;1dZe74l0j?kB^M|yxPj$Vl|);h z{|?;&yL2Zmw~)_#9i_y1N|K=+o%BY$ zrgF$d9`eW7laT*L1XR?IUSgs;p8TSMxZ#Tw!Xjh_NLoi-w)nuo~vm3$yS-3g^PG* zAuK+MLKU+^BJ|>yD8esUPdP$o#~}?#TqNSbx^_kJpJXP? zKwDQ71TNj1Sh7;X#qXp1uTWF}0wpUL;P6bxoyxTT=n@Qo6}S^#&nssgl?$GN$wxEc zb?}5U^EBN2L9;pUDav@NQ=aOKry=EOSnz~1p30P`66b4rX_GX}8viL0!@Y{c5PLEL zg#Nw3$yP$vDf%So)sLjZkIZ_SacmqJ5>p+R!*>4%g=9+Xy6<-#fN0P~s&$u=&5m3?kUzHQ_BX@FDIEkZ zVjU+dSjcG_BwTQX7CEFIOc?3y>OVi!bv_o6^e01S6Y&VVoJZnAgM$Mi41k#nv^^0V z$N{}L^RCu~A6=4DHQ5>4$f_5moy=pg%dxXi`PIKDPv|OfED9oYnT9GGhc;;_Q&_TO zU!YfCqZ=4IZuvP{9yps~Fv;7L_#X1TN<;8C660_WDA&*2=(*nWPW7C-ZoyZTGs%Kp zzr5id){YGs_r{ca;{tRnQ0VO?pZ~7wE-2AGo7mB;nu#ME^Rdla4OA22o<^AI6x-p_ zm1yeJP*yt$#mGV5NW)2Hy4lqW{S<_V&+44S!H;|bTi|53ho?`20=E!kG8WN!_mlm- zvqB%E_h9zXTU7k&R@j3+%b-ZmSrFAY*M0u5D60aLO7F1 zYYtVGm7LeePUQM5Ou0^UpQpic61vwevPEUUKXV?36*yJk^s;Q?Z{;^b-**I7Tu(Pv z>~Bj|)G9;oM@p#J7UqWU6bUh5UQW*EP7dn&W+~8-BFfY%4h(+rEWst^JiGwbg=zsS zrX_o1k?1+A?1&RO%N6=*L|6V%I=XK~n5Ss6#GU>)?e29PYe z_ZobMWN^XI$9BrKq@I*=q)dk>j!XUkpKXKrV+{Dm#x|&qxTSu3wz+@!v|&LOX`d|v z&x(HrRgUtfus%FYWgJv{O)5-p&n+XpQ`jIP?OJM#NX5xc23e`-ehnEM$?t$4;wNhQ zIK>iArAw)Gg-v{l-qQ{&!}oj}>iXC8mT^g1VPs9V{JXJ~P_r2Wvl&8CDZpek@pH3b-k^DM-9Z)_Csaa1z)6?3O=#nV|>q$9d z-SUj$ZKV#PoN|&P)=IxB6KO`xz4o8)s&LB*O5X-CQc23OOU7)H9}9HeDQjS}`b8M>>bI5x)U z!!4^R;7V&2nq$k}(yPv{!EQEJvn8@mnI^p_pC)=u98%V&ygtjgAaNZ<7AcWhdz(N~ zBuT$a$wyZ!g-%f}S8F$HesD%(Ow0!5?UC~0hm42LQo>_&dzx+Svn!* z5)%{^He_L5rdJ`AkI;CVSLlUTmj{FI7R9;M#EAKA#4vASN>YUz_^FyyiTMMHDUkpg zZ~=cpuR{j3{;uwYW`2UQ|2@4ZmU1jvspIX>>1|*cBk(sA?beg~zoi(Dl#mW+LEifm z<;^GEBp8%DR5O|L`$!_W>ii=Lk-5HP&cap)Oqlg8dO`cf*)~;admY;V)kbmuh2D}C zwPd8rECk2tC#~6-0SnVpGi??VPcp$4Tia$mc_@L_8d=*Jv8x}acwVF2yXdw9x2$8Y zmh->(W zEBlD6|A^bb?okB(iYxetTlEoF@pE(FBTL0!S*kyxzil5`HnV%QISe-NDdCygwv*L+&wvCH+gcieQ zK9KVS_}wfGp7)B722Z33Z_3#zJ0Hvi%=|X?lAiPu&wEw}1g`uxm^ho< z8NN8h7f+cpB@L;PhBUt}XDa1GEDJrOJ91_`0q=no&qXt5YGf&LJM%JcOqFa*^P6&} z3f{&t(=*zfGvkT!9$4|D%$r$?+(AAscT=jQDa~)lnabtd^o$o(Bn3oIFZ^2f=U_VNMNrSy#M&YAIK)xeW#fuVuW-+hg@@q1yT#_wGWdHHazo?Bft zUr{|@Sa#oA&SL|xk49cOn`?NYZ;HxtP71l8zR0;L!KTDfS&Ggq=5g?fr}W_mX4WKXnx(Td-TB5&rK`CJk9HRar^ z_==nfn^kjW3ZZc|IVXi&R2lZx!`TWJy%e|K2WY;coSB}44De)7@yxBEcG63F(n~zq zOFS3L%xn&=s><0L`1*NwVa|*vYdM~1d0{DgBexuJiyinbn0J_&EIB>vVJCnmgGw`& zK2^?L$wPn%fOs;1cmkk5l(SO^N4^JgP71j=Zy8IC&t+>^^imv1S!w;0g$=%vXp$?; z+1&h5em+o^GgE*8qyQk6uFKh35MQTY(aD0VI8$+kuTJsR(?>Hk2U9f%(|k+LRE#bN z)a1-~G6;Ce04PNHA2<=BQP|3}~1Lehw!&||!h-@%~M zvk_Po&wEzqq7~2F8siGp$Q8nqRR~WRIEADNA*57@A_xr$eEn{el~LP zWT5brK~ae6`M`+~fqDdOqsWpxNc&PX`_hOt)l#J@sl`&I@Kjr@R0?qf3FXq;$MUgP f{j7`*oKh;F1B6hVw<6=Inf279J#|;415W-Qffgy* literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/rewriter.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/autogenerate/__pycache__/rewriter.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3af75bf2f5a6cf64c3d8a407dff0cde2602bec95 GIT binary patch literal 9497 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 QukdBRaOz7&E!W2X0+E3hE&u=k literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/api.py b/venv/lib/python3.12/site-packages/alembic/autogenerate/api.py new file mode 100644 index 00000000..b2e3faef --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/autogenerate/api.py @@ -0,0 +1,667 @@ +from __future__ import annotations + +import contextlib +import logging +from typing import Any +from typing import Dict +from typing import Iterator +from typing import List +from typing import Optional +from typing import Sequence +from typing import Set +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import inspect + +from . import compare +from . import render +from .. import util +from ..operations import ops +from ..runtime.plugins import Plugin +from ..util import sqla_compat + +if TYPE_CHECKING: + from sqlalchemy.engine import Connection + from sqlalchemy.engine import Dialect + from sqlalchemy.engine import Inspector + from sqlalchemy.sql.schema import MetaData + from sqlalchemy.sql.schema import SchemaItem + from sqlalchemy.sql.schema import Table + + from ..config import Config + from ..operations.ops import DowngradeOps + from ..operations.ops import MigrationScript + from ..operations.ops import UpgradeOps + from ..runtime.environment import NameFilterParentNames + from ..runtime.environment import NameFilterType + from ..runtime.environment import ProcessRevisionDirectiveFn + from ..runtime.environment import RenderItemFn + from ..runtime.migration import MigrationContext + from ..script.base import Script + from ..script.base import ScriptDirectory + from ..script.revision import _GetRevArg + from ..util import PriorityDispatcher + + +log = logging.getLogger(__name__) + + +def compare_metadata(context: MigrationContext, metadata: MetaData) -> Any: + """Compare a database schema to that given in a + :class:`~sqlalchemy.schema.MetaData` instance. + + The database connection is presented in the context + of a :class:`.MigrationContext` object, which + provides database connectivity as well as optional + comparison functions to use for datatypes and + server defaults - see the "autogenerate" arguments + at :meth:`.EnvironmentContext.configure` + for details on these. + + The return format is a list of "diff" directives, + each representing individual differences:: + + from alembic.migration import MigrationContext + from alembic.autogenerate import compare_metadata + from sqlalchemy import ( + create_engine, + MetaData, + Column, + Integer, + String, + Table, + text, + ) + import pprint + + engine = create_engine("sqlite://") + + with engine.begin() as conn: + conn.execute( + text( + ''' + create table foo ( + id integer not null primary key, + old_data varchar, + x integer + ) + ''' + ) + ) + conn.execute(text("create table bar (data varchar)")) + + metadata = MetaData() + Table( + "foo", + metadata, + Column("id", Integer, primary_key=True), + Column("data", Integer), + Column("x", Integer, nullable=False), + ) + Table("bat", metadata, Column("info", String)) + + mc = MigrationContext.configure(engine.connect()) + + diff = compare_metadata(mc, metadata) + pprint.pprint(diff, indent=2, width=20) + + Output:: + + [ + ( + "add_table", + Table( + "bat", + MetaData(), + Column("info", String(), table=), + schema=None, + ), + ), + ( + "remove_table", + Table( + "bar", + MetaData(), + Column("data", VARCHAR(), table=), + schema=None, + ), + ), + ( + "add_column", + None, + "foo", + Column("data", Integer(), table=), + ), + [ + ( + "modify_nullable", + None, + "foo", + "x", + { + "existing_comment": None, + "existing_server_default": False, + "existing_type": INTEGER(), + }, + True, + False, + ) + ], + ( + "remove_column", + None, + "foo", + Column("old_data", VARCHAR(), table=), + ), + ] + + :param context: a :class:`.MigrationContext` + instance. + :param metadata: a :class:`~sqlalchemy.schema.MetaData` + instance. + + .. seealso:: + + :func:`.produce_migrations` - produces a :class:`.MigrationScript` + structure based on metadata comparison. + + """ + + migration_script = produce_migrations(context, metadata) + assert migration_script.upgrade_ops is not None + return migration_script.upgrade_ops.as_diffs() + + +def produce_migrations( + context: MigrationContext, metadata: MetaData +) -> MigrationScript: + """Produce a :class:`.MigrationScript` structure based on schema + comparison. + + This function does essentially what :func:`.compare_metadata` does, + but then runs the resulting list of diffs to produce the full + :class:`.MigrationScript` object. For an example of what this looks like, + see the example in :ref:`customizing_revision`. + + .. seealso:: + + :func:`.compare_metadata` - returns more fundamental "diff" + data from comparing a schema. + + """ + + autogen_context = AutogenContext(context, metadata=metadata) + + migration_script = ops.MigrationScript( + rev_id=None, + upgrade_ops=ops.UpgradeOps([]), + downgrade_ops=ops.DowngradeOps([]), + ) + + compare._populate_migration_script(autogen_context, migration_script) + + return migration_script + + +def render_python_code( + up_or_down_op: Union[UpgradeOps, DowngradeOps], + sqlalchemy_module_prefix: str = "sa.", + alembic_module_prefix: str = "op.", + render_as_batch: bool = False, + imports: Sequence[str] = (), + render_item: Optional[RenderItemFn] = None, + migration_context: Optional[MigrationContext] = None, + user_module_prefix: Optional[str] = None, +) -> str: + """Render Python code given an :class:`.UpgradeOps` or + :class:`.DowngradeOps` object. + + This is a convenience function that can be used to test the + autogenerate output of a user-defined :class:`.MigrationScript` structure. + + :param up_or_down_op: :class:`.UpgradeOps` or :class:`.DowngradeOps` object + :param sqlalchemy_module_prefix: module prefix for SQLAlchemy objects + :param alembic_module_prefix: module prefix for Alembic constructs + :param render_as_batch: use "batch operations" style for rendering + :param imports: sequence of import symbols to add + :param render_item: callable to render items + :param migration_context: optional :class:`.MigrationContext` + :param user_module_prefix: optional string prefix for user-defined types + + .. versionadded:: 1.11.0 + + """ + opts = { + "sqlalchemy_module_prefix": sqlalchemy_module_prefix, + "alembic_module_prefix": alembic_module_prefix, + "render_item": render_item, + "render_as_batch": render_as_batch, + "user_module_prefix": user_module_prefix, + } + + if migration_context is None: + from ..runtime.migration import MigrationContext + from sqlalchemy.engine.default import DefaultDialect + + migration_context = MigrationContext.configure( + dialect=DefaultDialect() + ) + + autogen_context = AutogenContext(migration_context, opts=opts) + autogen_context.imports = set(imports) + return render._indent( + render._render_cmd_body(up_or_down_op, autogen_context) + ) + + +def _render_migration_diffs( + context: MigrationContext, template_args: Dict[Any, Any] +) -> None: + """legacy, used by test_autogen_composition at the moment""" + + autogen_context = AutogenContext(context) + + upgrade_ops = ops.UpgradeOps([]) + compare._produce_net_changes(autogen_context, upgrade_ops) + + migration_script = ops.MigrationScript( + rev_id=None, + upgrade_ops=upgrade_ops, + downgrade_ops=upgrade_ops.reverse(), + ) + + render._render_python_into_templatevars( + autogen_context, migration_script, template_args + ) + + +class AutogenContext: + """Maintains configuration and state that's specific to an + autogenerate operation.""" + + metadata: Union[MetaData, Sequence[MetaData], None] = None + """The :class:`~sqlalchemy.schema.MetaData` object + representing the destination. + + This object is the one that is passed within ``env.py`` + to the :paramref:`.EnvironmentContext.configure.target_metadata` + parameter. It represents the structure of :class:`.Table` and other + objects as stated in the current database model, and represents the + destination structure for the database being examined. + + While the :class:`~sqlalchemy.schema.MetaData` object is primarily + known as a collection of :class:`~sqlalchemy.schema.Table` objects, + it also has an :attr:`~sqlalchemy.schema.MetaData.info` dictionary + that may be used by end-user schemes to store additional schema-level + objects that are to be compared in custom autogeneration schemes. + + """ + + connection: Optional[Connection] = None + """The :class:`~sqlalchemy.engine.base.Connection` object currently + connected to the database backend being compared. + + This is obtained from the :attr:`.MigrationContext.bind` and is + ultimately set up in the ``env.py`` script. + + """ + + dialect: Dialect + """The :class:`~sqlalchemy.engine.Dialect` object currently in use. + + This is normally obtained from the + :attr:`~sqlalchemy.engine.base.Connection.dialect` attribute. + + """ + + imports: Set[str] = None # type: ignore[assignment] + """A ``set()`` which contains string Python import directives. + + The directives are to be rendered into the ``${imports}`` section + of a script template. The set is normally empty and can be modified + within hooks such as the + :paramref:`.EnvironmentContext.configure.render_item` hook. + + .. seealso:: + + :ref:`autogen_render_types` + + """ + + migration_context: MigrationContext + """The :class:`.MigrationContext` established by the ``env.py`` script.""" + + comparators: PriorityDispatcher + + def __init__( + self, + migration_context: MigrationContext, + metadata: Union[MetaData, Sequence[MetaData], None] = None, + opts: Optional[Dict[str, Any]] = None, + autogenerate: bool = True, + ) -> None: + if ( + autogenerate + and migration_context is not None + and migration_context.as_sql + ): + raise util.CommandError( + "autogenerate can't use as_sql=True as it prevents querying " + "the database for schema information" + ) + + # branch off from the "global" comparators. This collection + # is empty in Alembic except that it is populated by third party + # extensions that don't use the plugin system. so we will build + # off of whatever is in there. + if autogenerate: + self.comparators = compare.comparators.branch() + Plugin.populate_autogenerate_priority_dispatch( + self.comparators, + include_plugins=migration_context.opts.get( + "autogenerate_plugins", ["alembic.autogenerate.*"] + ), + ) + + if opts is None: + opts = migration_context.opts + + self.metadata = metadata = ( + opts.get("target_metadata", None) if metadata is None else metadata + ) + + if ( + autogenerate + and metadata is None + and migration_context is not None + and migration_context.script is not None + ): + raise util.CommandError( + "Can't proceed with --autogenerate option; environment " + "script %s does not provide " + "a MetaData object or sequence of objects to the context." + % (migration_context.script.env_py_location) + ) + + include_object = opts.get("include_object", None) + include_name = opts.get("include_name", None) + + object_filters = [] + name_filters = [] + if include_object: + object_filters.append(include_object) + if include_name: + name_filters.append(include_name) + + self._object_filters = object_filters + self._name_filters = name_filters + + self.migration_context = migration_context + self.connection = self.migration_context.bind + self.dialect = self.migration_context.dialect + + self.imports = set() + self.opts: Dict[str, Any] = opts + self._has_batch: bool = False + + @util.memoized_property + def inspector(self) -> Inspector: + if self.connection is None: + raise TypeError( + "can't return inspector as this " + "AutogenContext has no database connection" + ) + return inspect(self.connection) + + @contextlib.contextmanager + def _within_batch(self) -> Iterator[None]: + self._has_batch = True + yield + self._has_batch = False + + def run_name_filters( + self, + name: Optional[str], + type_: NameFilterType, + parent_names: NameFilterParentNames, + ) -> bool: + """Run the context's name filters and return True if the targets + should be part of the autogenerate operation. + + This method should be run for every kind of name encountered within the + reflection side of an autogenerate operation, giving the environment + the chance to filter what names should be reflected as database + objects. The filters here are produced directly via the + :paramref:`.EnvironmentContext.configure.include_name` parameter. + + """ + if "schema_name" in parent_names: + if type_ == "table": + table_name = name + else: + table_name = parent_names.get("table_name", None) + if table_name: + schema_name = parent_names["schema_name"] + if schema_name: + parent_names["schema_qualified_table_name"] = "%s.%s" % ( + schema_name, + table_name, + ) + else: + parent_names["schema_qualified_table_name"] = table_name + + for fn in self._name_filters: + if not fn(name, type_, parent_names): + return False + else: + return True + + def run_object_filters( + self, + object_: SchemaItem, + name: sqla_compat._ConstraintName, + type_: NameFilterType, + reflected: bool, + compare_to: Optional[SchemaItem], + ) -> bool: + """Run the context's object filters and return True if the targets + should be part of the autogenerate operation. + + This method should be run for every kind of object encountered within + an autogenerate operation, giving the environment the chance + to filter what objects should be included in the comparison. + The filters here are produced directly via the + :paramref:`.EnvironmentContext.configure.include_object` parameter. + + """ + for fn in self._object_filters: + if not fn(object_, name, type_, reflected, compare_to): + return False + else: + return True + + run_filters = run_object_filters + + @util.memoized_property + def sorted_tables(self) -> List[Table]: + """Return an aggregate of the :attr:`.MetaData.sorted_tables` + collection(s). + + For a sequence of :class:`.MetaData` objects, this + concatenates the :attr:`.MetaData.sorted_tables` collection + for each individual :class:`.MetaData` in the order of the + sequence. It does **not** collate the sorted tables collections. + + """ + result = [] + for m in util.to_list(self.metadata): + result.extend(m.sorted_tables) + return result + + @util.memoized_property + def table_key_to_table(self) -> Dict[str, Table]: + """Return an aggregate of the :attr:`.MetaData.tables` dictionaries. + + The :attr:`.MetaData.tables` collection is a dictionary of table key + to :class:`.Table`; this method aggregates the dictionary across + multiple :class:`.MetaData` objects into one dictionary. + + Duplicate table keys are **not** supported; if two :class:`.MetaData` + objects contain the same table key, an exception is raised. + + """ + result: Dict[str, Table] = {} + for m in util.to_list(self.metadata): + intersect = set(result).intersection(set(m.tables)) + if intersect: + raise ValueError( + "Duplicate table keys across multiple " + "MetaData objects: %s" + % (", ".join('"%s"' % key for key in sorted(intersect))) + ) + + result.update(m.tables) + return result + + +class RevisionContext: + """Maintains configuration and state that's specific to a revision + file generation operation.""" + + generated_revisions: List[MigrationScript] + process_revision_directives: Optional[ProcessRevisionDirectiveFn] + + def __init__( + self, + config: Config, + script_directory: ScriptDirectory, + command_args: Dict[str, Any], + process_revision_directives: Optional[ + ProcessRevisionDirectiveFn + ] = None, + ) -> None: + self.config = config + self.script_directory = script_directory + self.command_args = command_args + self.process_revision_directives = process_revision_directives + self.template_args = { + "config": config # Let templates use config for + # e.g. multiple databases + } + self.generated_revisions = [self._default_revision()] + + def _to_script( + self, migration_script: MigrationScript + ) -> Optional[Script]: + template_args: Dict[str, Any] = self.template_args.copy() + + if getattr(migration_script, "_needs_render", False): + autogen_context = self._last_autogen_context + + # clear out existing imports if we are doing multiple + # renders + autogen_context.imports = set() + if migration_script.imports: + autogen_context.imports.update(migration_script.imports) + render._render_python_into_templatevars( + autogen_context, migration_script, template_args + ) + + assert migration_script.rev_id is not None + return self.script_directory.generate_revision( + migration_script.rev_id, + migration_script.message, + refresh=True, + head=migration_script.head, + splice=migration_script.splice, + branch_labels=migration_script.branch_label, + version_path=migration_script.version_path, + depends_on=migration_script.depends_on, + **template_args, + ) + + def run_autogenerate( + self, rev: _GetRevArg, migration_context: MigrationContext + ) -> None: + self._run_environment(rev, migration_context, True) + + def run_no_autogenerate( + self, rev: _GetRevArg, migration_context: MigrationContext + ) -> None: + self._run_environment(rev, migration_context, False) + + def _run_environment( + self, + rev: _GetRevArg, + migration_context: MigrationContext, + autogenerate: bool, + ) -> None: + if autogenerate: + if self.command_args["sql"]: + raise util.CommandError( + "Using --sql with --autogenerate does not make any sense" + ) + if set(self.script_directory.get_revisions(rev)) != set( + self.script_directory.get_revisions("heads") + ): + raise util.CommandError("Target database is not up to date.") + + upgrade_token = migration_context.opts["upgrade_token"] + downgrade_token = migration_context.opts["downgrade_token"] + + migration_script = self.generated_revisions[-1] + if not getattr(migration_script, "_needs_render", False): + migration_script.upgrade_ops_list[-1].upgrade_token = upgrade_token + migration_script.downgrade_ops_list[-1].downgrade_token = ( + downgrade_token + ) + migration_script._needs_render = True + else: + migration_script._upgrade_ops.append( + ops.UpgradeOps([], upgrade_token=upgrade_token) + ) + migration_script._downgrade_ops.append( + ops.DowngradeOps([], downgrade_token=downgrade_token) + ) + + autogen_context = AutogenContext( + migration_context, autogenerate=autogenerate + ) + self._last_autogen_context: AutogenContext = autogen_context + + if autogenerate: + compare._populate_migration_script( + autogen_context, migration_script + ) + + if self.process_revision_directives: + self.process_revision_directives( + migration_context, rev, self.generated_revisions + ) + + hook = migration_context.opts["process_revision_directives"] + if hook: + hook(migration_context, rev, self.generated_revisions) + + for migration_script in self.generated_revisions: + migration_script._needs_render = True + + def _default_revision(self) -> MigrationScript: + command_args: Dict[str, Any] = self.command_args + op = ops.MigrationScript( + rev_id=command_args["rev_id"] or util.rev_id(), + message=command_args["message"], + upgrade_ops=ops.UpgradeOps([]), + downgrade_ops=ops.DowngradeOps([]), + head=command_args["head"], + splice=command_args["splice"], + branch_label=command_args["branch_label"], + version_path=command_args["version_path"], + depends_on=command_args["depends_on"], + ) + return op + + def generate_scripts(self) -> Iterator[Optional[Script]]: + for generated_revision in self.generated_revisions: + yield self._to_script(generated_revision) diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__init__.py b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__init__.py new file mode 100644 index 00000000..a49640cf --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__init__.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import logging +from typing import TYPE_CHECKING + +from . import comments +from . import constraints +from . import schema +from . import server_defaults +from . import tables +from . import types +from ... import util +from ...runtime.plugins import Plugin + +if TYPE_CHECKING: + from ..api import AutogenContext + from ...operations.ops import MigrationScript + from ...operations.ops import UpgradeOps + + +log = logging.getLogger(__name__) + +comparators = util.PriorityDispatcher() +"""global registry which alembic keeps empty, but copies when creating +a new AutogenContext. + +This is to support a variety of third party plugins that hook their autogen +functionality onto this collection. + +""" + + +def _populate_migration_script( + autogen_context: AutogenContext, migration_script: MigrationScript +) -> None: + upgrade_ops = migration_script.upgrade_ops_list[-1] + downgrade_ops = migration_script.downgrade_ops_list[-1] + + _produce_net_changes(autogen_context, upgrade_ops) + upgrade_ops.reverse_into(downgrade_ops) + + +def _produce_net_changes( + autogen_context: AutogenContext, upgrade_ops: UpgradeOps +) -> None: + assert autogen_context.dialect is not None + + autogen_context.comparators.dispatch( + "autogenerate", qualifier=autogen_context.dialect.name + )(autogen_context, upgrade_ops) + + +Plugin.setup_plugin_from_module(schema, "alembic.autogenerate.schemas") +Plugin.setup_plugin_from_module(tables, "alembic.autogenerate.tables") +Plugin.setup_plugin_from_module(types, "alembic.autogenerate.types") +Plugin.setup_plugin_from_module( + constraints, "alembic.autogenerate.constraints" +) +Plugin.setup_plugin_from_module( + server_defaults, "alembic.autogenerate.defaults" +) +Plugin.setup_plugin_from_module(comments, "alembic.autogenerate.comments") diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0552f59ca17c6bb046c5cb3af11b034d58a51f8 GIT binary patch literal 2431 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__pycache__/types.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/__pycache__/types.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f5295ada99cb33fa023c3ae19017a695b39bc13 GIT binary patch literal 4371 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 PriorityDispatchResult: + assert autogen_context.dialect is not None + if not autogen_context.dialect.supports_comments: + return PriorityDispatchResult.CONTINUE + + metadata_comment = metadata_col.comment + conn_col_comment = conn_col.comment + if conn_col_comment is None and metadata_comment is None: + return PriorityDispatchResult.CONTINUE + + alter_column_op.existing_comment = conn_col_comment + + if conn_col_comment != metadata_comment: + alter_column_op.modify_comment = metadata_comment + log.info("Detected column comment '%s.%s'", tname, cname) + + return PriorityDispatchResult.STOP + else: + return PriorityDispatchResult.CONTINUE + + +def _compare_table_comment( + autogen_context: AutogenContext, + modify_table_ops: ModifyTableOps, + schema: Optional[str], + tname: Union[quoted_name, str], + conn_table: Optional[Table], + metadata_table: Optional[Table], +) -> PriorityDispatchResult: + assert autogen_context.dialect is not None + if not autogen_context.dialect.supports_comments: + return PriorityDispatchResult.CONTINUE + + # if we're doing CREATE TABLE, comments will be created inline + # with the create_table op. + if conn_table is None or metadata_table is None: + return PriorityDispatchResult.CONTINUE + + if conn_table.comment is None and metadata_table.comment is None: + return PriorityDispatchResult.CONTINUE + + if metadata_table.comment is None and conn_table.comment is not None: + modify_table_ops.ops.append( + ops.DropTableCommentOp( + tname, existing_comment=conn_table.comment, schema=schema + ) + ) + return PriorityDispatchResult.STOP + elif metadata_table.comment != conn_table.comment: + modify_table_ops.ops.append( + ops.CreateTableCommentOp( + tname, + metadata_table.comment, + existing_comment=conn_table.comment, + schema=schema, + ) + ) + return PriorityDispatchResult.STOP + + return PriorityDispatchResult.CONTINUE + + +def setup(plugin: Plugin) -> None: + plugin.add_autogenerate_comparator( + _compare_column_comment, + "column", + "comments", + ) + plugin.add_autogenerate_comparator( + _compare_table_comment, + "table", + "comments", + ) diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/constraints.py b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/constraints.py new file mode 100644 index 00000000..ae1f20e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/constraints.py @@ -0,0 +1,812 @@ +# mypy: allow-untyped-defs, allow-untyped-calls, allow-incomplete-defs + +from __future__ import annotations + +import logging +from typing import Any +from typing import cast +from typing import Collection +from typing import Dict +from typing import Mapping +from typing import Optional +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +from sqlalchemy import schema as sa_schema +from sqlalchemy import text +from sqlalchemy.sql import expression +from sqlalchemy.sql.schema import ForeignKeyConstraint +from sqlalchemy.sql.schema import Index +from sqlalchemy.sql.schema import UniqueConstraint + +from .util import _InspectorConv +from ... import util +from ...ddl._autogen import is_index_sig +from ...ddl._autogen import is_uq_sig +from ...operations import ops +from ...util import PriorityDispatchResult +from ...util import sqla_compat + +if TYPE_CHECKING: + from sqlalchemy.engine.interfaces import ReflectedForeignKeyConstraint + from sqlalchemy.engine.interfaces import ReflectedIndex + from sqlalchemy.engine.interfaces import ReflectedUniqueConstraint + from sqlalchemy.sql.elements import quoted_name + from sqlalchemy.sql.elements import TextClause + from sqlalchemy.sql.schema import Column + from sqlalchemy.sql.schema import Table + + from ...autogenerate.api import AutogenContext + from ...ddl._autogen import _constraint_sig + from ...ddl.impl import DefaultImpl + from ...operations.ops import AlterColumnOp + from ...operations.ops import ModifyTableOps + from ...runtime.plugins import Plugin + +_C = TypeVar("_C", bound=Union[UniqueConstraint, ForeignKeyConstraint, Index]) + + +log = logging.getLogger(__name__) + + +def _compare_indexes_and_uniques( + autogen_context: AutogenContext, + modify_ops: ModifyTableOps, + schema: Optional[str], + tname: Union[quoted_name, str], + conn_table: Optional[Table], + metadata_table: Optional[Table], +) -> PriorityDispatchResult: + inspector = autogen_context.inspector + is_create_table = conn_table is None + is_drop_table = metadata_table is None + impl = autogen_context.migration_context.impl + + # 1a. get raw indexes and unique constraints from metadata ... + if metadata_table is not None: + metadata_unique_constraints = { + uq + for uq in metadata_table.constraints + if isinstance(uq, sa_schema.UniqueConstraint) + } + metadata_indexes = set(metadata_table.indexes) + else: + metadata_unique_constraints = set() + metadata_indexes = set() + + conn_uniques: Collection[UniqueConstraint] = frozenset() + conn_indexes: Collection[Index] = frozenset() + + supports_unique_constraints = False + + unique_constraints_duplicate_unique_indexes = False + + if conn_table is not None: + conn_uniques_reflected: Collection[ReflectedUniqueConstraint] = ( + frozenset() + ) + conn_indexes_reflected: Collection[ReflectedIndex] = frozenset() + + # 1b. ... and from connection, if the table exists + try: + conn_uniques_reflected = _InspectorConv( + inspector + ).get_unique_constraints(tname, schema=schema) + + supports_unique_constraints = True + except NotImplementedError: + pass + except TypeError: + # number of arguments is off for the base + # method in SQLAlchemy due to the cache decorator + # not being present + pass + else: + conn_uniques_reflected = [ + uq + for uq in conn_uniques_reflected + if autogen_context.run_name_filters( + uq["name"], + "unique_constraint", + {"table_name": tname, "schema_name": schema}, + ) + ] + for uq in conn_uniques_reflected: + if uq.get("duplicates_index"): + unique_constraints_duplicate_unique_indexes = True + try: + conn_indexes_reflected = _InspectorConv(inspector).get_indexes( + tname, schema=schema + ) + except NotImplementedError: + pass + else: + conn_indexes_reflected = [ + ix + for ix in conn_indexes_reflected + if autogen_context.run_name_filters( + ix["name"], + "index", + {"table_name": tname, "schema_name": schema}, + ) + ] + + # 2. convert conn-level objects from raw inspector records + # into schema objects + if is_drop_table: + # for DROP TABLE uniques are inline, don't need them + conn_uniques = set() + else: + conn_uniques = { + _make_unique_constraint(impl, uq_def, conn_table) + for uq_def in conn_uniques_reflected + } + + conn_indexes = { + index + for index in ( + _make_index(impl, ix, conn_table) + for ix in conn_indexes_reflected + ) + if index is not None + } + + # 2a. if the dialect dupes unique indexes as unique constraints + # (mysql and oracle), correct for that + + if unique_constraints_duplicate_unique_indexes: + _correct_for_uq_duplicates_uix( + conn_uniques, + conn_indexes, + metadata_unique_constraints, + metadata_indexes, + autogen_context.dialect, + impl, + ) + + # 3. give the dialect a chance to omit indexes and constraints that + # we know are either added implicitly by the DB or that the DB + # can't accurately report on + impl.correct_for_autogen_constraints( + conn_uniques, # type: ignore[arg-type] + conn_indexes, # type: ignore[arg-type] + metadata_unique_constraints, + metadata_indexes, + ) + + # 4. organize the constraints into "signature" collections, the + # _constraint_sig() objects provide a consistent facade over both + # Index and UniqueConstraint so we can easily work with them + # interchangeably + metadata_unique_constraints_sig = { + impl._create_metadata_constraint_sig(uq) + for uq in metadata_unique_constraints + } + + metadata_indexes_sig = { + impl._create_metadata_constraint_sig(ix) for ix in metadata_indexes + } + + conn_unique_constraints = { + impl._create_reflected_constraint_sig(uq) for uq in conn_uniques + } + + conn_indexes_sig = { + impl._create_reflected_constraint_sig(ix) for ix in conn_indexes + } + + # 5. index things by name, for those objects that have names + metadata_names = { + cast(str, c.md_name_to_sql_name(autogen_context)): c + for c in metadata_unique_constraints_sig.union(metadata_indexes_sig) + if c.is_named + } + + conn_uniques_by_name: Dict[ + sqla_compat._ConstraintName, + _constraint_sig[sa_schema.UniqueConstraint], + ] + conn_indexes_by_name: Dict[ + sqla_compat._ConstraintName, _constraint_sig[sa_schema.Index] + ] + + conn_uniques_by_name = {c.name: c for c in conn_unique_constraints} + conn_indexes_by_name = {c.name: c for c in conn_indexes_sig} + conn_names = { + c.name: c + for c in conn_unique_constraints.union(conn_indexes_sig) + if sqla_compat.constraint_name_string(c.name) + } + + doubled_constraints = { + name: (conn_uniques_by_name[name], conn_indexes_by_name[name]) + for name in set(conn_uniques_by_name).intersection( + conn_indexes_by_name + ) + } + + # 6. index things by "column signature", to help with unnamed unique + # constraints. + conn_uniques_by_sig = {uq.unnamed: uq for uq in conn_unique_constraints} + metadata_uniques_by_sig = { + uq.unnamed: uq for uq in metadata_unique_constraints_sig + } + unnamed_metadata_uniques = { + uq.unnamed: uq + for uq in metadata_unique_constraints_sig + if not sqla_compat._constraint_is_named( + uq.const, autogen_context.dialect + ) + } + + # assumptions: + # 1. a unique constraint or an index from the connection *always* + # has a name. + # 2. an index on the metadata side *always* has a name. + # 3. a unique constraint on the metadata side *might* have a name. + # 4. The backend may double up indexes as unique constraints and + # vice versa (e.g. MySQL, Postgresql) + + def obj_added( + obj: ( + _constraint_sig[sa_schema.UniqueConstraint] + | _constraint_sig[sa_schema.Index] + ), + ): + if is_index_sig(obj): + if autogen_context.run_object_filters( + obj.const, obj.name, "index", False, None + ): + modify_ops.ops.append(ops.CreateIndexOp.from_index(obj.const)) + log.info( + "Detected added index %r on '%s'", + obj.name, + obj.column_names, + ) + elif is_uq_sig(obj): + if not supports_unique_constraints: + # can't report unique indexes as added if we don't + # detect them + return + if is_create_table or is_drop_table: + # unique constraints are created inline with table defs + return + if autogen_context.run_object_filters( + obj.const, obj.name, "unique_constraint", False, None + ): + modify_ops.ops.append( + ops.AddConstraintOp.from_constraint(obj.const) + ) + log.info( + "Detected added unique constraint %r on '%s'", + obj.name, + obj.column_names, + ) + else: + assert False + + def obj_removed( + obj: ( + _constraint_sig[sa_schema.UniqueConstraint] + | _constraint_sig[sa_schema.Index] + ), + ): + if is_index_sig(obj): + if obj.is_unique and not supports_unique_constraints: + # many databases double up unique constraints + # as unique indexes. without that list we can't + # be sure what we're doing here + return + + if autogen_context.run_object_filters( + obj.const, obj.name, "index", True, None + ): + modify_ops.ops.append(ops.DropIndexOp.from_index(obj.const)) + log.info("Detected removed index %r on %r", obj.name, tname) + elif is_uq_sig(obj): + if is_create_table or is_drop_table: + # if the whole table is being dropped, we don't need to + # consider unique constraint separately + return + if autogen_context.run_object_filters( + obj.const, obj.name, "unique_constraint", True, None + ): + modify_ops.ops.append( + ops.DropConstraintOp.from_constraint(obj.const) + ) + log.info( + "Detected removed unique constraint %r on %r", + obj.name, + tname, + ) + else: + assert False + + def obj_changed( + old: ( + _constraint_sig[sa_schema.UniqueConstraint] + | _constraint_sig[sa_schema.Index] + ), + new: ( + _constraint_sig[sa_schema.UniqueConstraint] + | _constraint_sig[sa_schema.Index] + ), + msg: str, + ): + if is_index_sig(old): + assert is_index_sig(new) + + if autogen_context.run_object_filters( + new.const, new.name, "index", False, old.const + ): + log.info( + "Detected changed index %r on %r: %s", old.name, tname, msg + ) + modify_ops.ops.append(ops.DropIndexOp.from_index(old.const)) + modify_ops.ops.append(ops.CreateIndexOp.from_index(new.const)) + elif is_uq_sig(old): + assert is_uq_sig(new) + + if autogen_context.run_object_filters( + new.const, new.name, "unique_constraint", False, old.const + ): + log.info( + "Detected changed unique constraint %r on %r: %s", + old.name, + tname, + msg, + ) + modify_ops.ops.append( + ops.DropConstraintOp.from_constraint(old.const) + ) + modify_ops.ops.append( + ops.AddConstraintOp.from_constraint(new.const) + ) + else: + assert False + + for removed_name in sorted(set(conn_names).difference(metadata_names)): + conn_obj = conn_names[removed_name] + if ( + is_uq_sig(conn_obj) + and conn_obj.unnamed in unnamed_metadata_uniques + ): + continue + elif removed_name in doubled_constraints: + conn_uq, conn_idx = doubled_constraints[removed_name] + if ( + all( + conn_idx.unnamed != meta_idx.unnamed + for meta_idx in metadata_indexes_sig + ) + and conn_uq.unnamed not in metadata_uniques_by_sig + ): + obj_removed(conn_uq) + obj_removed(conn_idx) + else: + obj_removed(conn_obj) + + for existing_name in sorted(set(metadata_names).intersection(conn_names)): + metadata_obj = metadata_names[existing_name] + + if existing_name in doubled_constraints: + conn_uq, conn_idx = doubled_constraints[existing_name] + if is_index_sig(metadata_obj): + conn_obj = conn_idx + else: + conn_obj = conn_uq + else: + conn_obj = conn_names[existing_name] + + if type(conn_obj) != type(metadata_obj): + obj_removed(conn_obj) + obj_added(metadata_obj) + else: + # TODO: for plugins, let's do is_index_sig / is_uq_sig + # here so we know index or unique, then + # do a sub-dispatch, + # autogen_context.comparators.dispatch("index") + # or + # autogen_context.comparators.dispatch("unique_constraint") + # + comparison = metadata_obj.compare_to_reflected(conn_obj) + + if comparison.is_different: + # constraint are different + obj_changed(conn_obj, metadata_obj, comparison.message) + elif comparison.is_skip: + # constraint cannot be compared, skip them + thing = ( + "index" if is_index_sig(conn_obj) else "unique constraint" + ) + log.info( + "Cannot compare %s %r, assuming equal and skipping. %s", + thing, + conn_obj.name, + comparison.message, + ) + else: + # constraint are equal + assert comparison.is_equal + + for added_name in sorted(set(metadata_names).difference(conn_names)): + obj = metadata_names[added_name] + obj_added(obj) + + for uq_sig in unnamed_metadata_uniques: + if uq_sig not in conn_uniques_by_sig: + obj_added(unnamed_metadata_uniques[uq_sig]) + + return PriorityDispatchResult.CONTINUE + + +def _correct_for_uq_duplicates_uix( + conn_unique_constraints, + conn_indexes, + metadata_unique_constraints, + metadata_indexes, + dialect, + impl, +): + # dedupe unique indexes vs. constraints, since MySQL / Oracle + # doesn't really have unique constraints as a separate construct. + # but look in the metadata and try to maintain constructs + # that already seem to be defined one way or the other + # on that side. This logic was formerly local to MySQL dialect, + # generalized to Oracle and others. See #276 + + # resolve final rendered name for unique constraints defined in the + # metadata. this includes truncation of long names. naming convention + # names currently should already be set as cons.name, however leave this + # to the sqla_compat to decide. + metadata_cons_names = [ + (sqla_compat._get_constraint_final_name(cons, dialect), cons) + for cons in metadata_unique_constraints + ] + + metadata_uq_names = { + name for name, cons in metadata_cons_names if name is not None + } + + unnamed_metadata_uqs = { + impl._create_metadata_constraint_sig(cons).unnamed + for name, cons in metadata_cons_names + if name is None + } + + metadata_ix_names = { + sqla_compat._get_constraint_final_name(cons, dialect) + for cons in metadata_indexes + if cons.unique + } + + # for reflection side, names are in their final database form + # already since they're from the database + conn_ix_names = {cons.name: cons for cons in conn_indexes if cons.unique} + + uqs_dupe_indexes = { + cons.name: cons + for cons in conn_unique_constraints + if cons.info["duplicates_index"] + } + + for overlap in uqs_dupe_indexes: + if overlap not in metadata_uq_names: + if ( + impl._create_reflected_constraint_sig( + uqs_dupe_indexes[overlap] + ).unnamed + not in unnamed_metadata_uqs + ): + conn_unique_constraints.discard(uqs_dupe_indexes[overlap]) + elif overlap not in metadata_ix_names: + conn_indexes.discard(conn_ix_names[overlap]) + + +_IndexColumnSortingOps: Mapping[str, Any] = util.immutabledict( + { + "asc": expression.asc, + "desc": expression.desc, + "nulls_first": expression.nullsfirst, + "nulls_last": expression.nullslast, + "nullsfirst": expression.nullsfirst, # 1_3 name + "nullslast": expression.nullslast, # 1_3 name + } +) + + +def _make_index( + impl: DefaultImpl, params: ReflectedIndex, conn_table: Table +) -> Optional[Index]: + exprs: list[Union[Column[Any], TextClause]] = [] + sorting = params.get("column_sorting") + + for num, col_name in enumerate(params["column_names"]): + item: Union[Column[Any], TextClause] + if col_name is None: + assert "expressions" in params + name = params["expressions"][num] + item = text(name) + else: + name = col_name + item = conn_table.c[col_name] + if sorting and name in sorting: + for operator in sorting[name]: + if operator in _IndexColumnSortingOps: + item = _IndexColumnSortingOps[operator](item) + exprs.append(item) + ix = sa_schema.Index( + params["name"], + *exprs, + unique=params["unique"], + _table=conn_table, + **impl.adjust_reflected_dialect_options(params, "index"), + ) + if "duplicates_constraint" in params: + ix.info["duplicates_constraint"] = params["duplicates_constraint"] + return ix + + +def _make_unique_constraint( + impl: DefaultImpl, params: ReflectedUniqueConstraint, conn_table: Table +) -> UniqueConstraint: + uq = sa_schema.UniqueConstraint( + *[conn_table.c[cname] for cname in params["column_names"]], + name=params["name"], + **impl.adjust_reflected_dialect_options(params, "unique_constraint"), + ) + if "duplicates_index" in params: + uq.info["duplicates_index"] = params["duplicates_index"] + + return uq + + +def _make_foreign_key( + params: ReflectedForeignKeyConstraint, conn_table: Table +) -> ForeignKeyConstraint: + tname = params["referred_table"] + if params["referred_schema"]: + tname = "%s.%s" % (params["referred_schema"], tname) + + options = params.get("options", {}) + + const = sa_schema.ForeignKeyConstraint( + [conn_table.c[cname] for cname in params["constrained_columns"]], + ["%s.%s" % (tname, n) for n in params["referred_columns"]], + onupdate=options.get("onupdate"), + ondelete=options.get("ondelete"), + deferrable=options.get("deferrable"), + initially=options.get("initially"), + name=params["name"], + ) + + referred_schema = params["referred_schema"] + referred_table = params["referred_table"] + + remote_table_key = sqla_compat._get_table_key( + referred_table, referred_schema + ) + if remote_table_key not in conn_table.metadata: + # create a placeholder table + sa_schema.Table( + referred_table, + conn_table.metadata, + schema=( + referred_schema + if referred_schema is not None + else sa_schema.BLANK_SCHEMA + ), + *[ + sa_schema.Column(remote, conn_table.c[local].type) + for local, remote in zip( + params["constrained_columns"], params["referred_columns"] + ) + ], + info={"alembic_placeholder": True}, + ) + elif conn_table.metadata.tables[remote_table_key].info.get( + "alembic_placeholder" + ): + # table exists and is a placeholder; ensure needed columns are present + placeholder_table = conn_table.metadata.tables[remote_table_key] + for local, remote in zip( + params["constrained_columns"], params["referred_columns"] + ): + if remote not in placeholder_table.c: + placeholder_table.append_column( + sa_schema.Column(remote, conn_table.c[local].type) + ) + + # needed by 0.7 + conn_table.append_constraint(const) + return const + + +def _compare_foreign_keys( + autogen_context: AutogenContext, + modify_table_ops: ModifyTableOps, + schema: Optional[str], + tname: Union[quoted_name, str], + conn_table: Table, + metadata_table: Table, +) -> PriorityDispatchResult: + # if we're doing CREATE TABLE, all FKs are created + # inline within the table def + + if conn_table is None or metadata_table is None: + return PriorityDispatchResult.CONTINUE + + inspector = autogen_context.inspector + metadata_fks = { + fk + for fk in metadata_table.constraints + if isinstance(fk, sa_schema.ForeignKeyConstraint) + } + + conn_fks_list = [ + fk + for fk in _InspectorConv(inspector).get_foreign_keys( + tname, schema=schema + ) + if autogen_context.run_name_filters( + fk["name"], + "foreign_key_constraint", + {"table_name": tname, "schema_name": schema}, + ) + ] + + conn_fks = { + _make_foreign_key(const, conn_table) for const in conn_fks_list + } + + impl = autogen_context.migration_context.impl + + # give the dialect a chance to correct the FKs to match more + # closely + autogen_context.migration_context.impl.correct_for_autogen_foreignkeys( + conn_fks, metadata_fks + ) + + metadata_fks_sig = { + impl._create_metadata_constraint_sig(fk) for fk in metadata_fks + } + + conn_fks_sig = { + impl._create_reflected_constraint_sig(fk) for fk in conn_fks + } + + # check if reflected FKs include options, indicating the backend + # can reflect FK options + if conn_fks_list and "options" in conn_fks_list[0]: + conn_fks_by_sig = {c.unnamed: c for c in conn_fks_sig} + metadata_fks_by_sig = {c.unnamed: c for c in metadata_fks_sig} + else: + # otherwise compare by sig without options added + conn_fks_by_sig = {c.unnamed_no_options: c for c in conn_fks_sig} + metadata_fks_by_sig = { + c.unnamed_no_options: c for c in metadata_fks_sig + } + + metadata_fks_by_name = { + c.name: c for c in metadata_fks_sig if c.name is not None + } + conn_fks_by_name = {c.name: c for c in conn_fks_sig if c.name is not None} + + def _add_fk(obj, compare_to): + if autogen_context.run_object_filters( + obj.const, obj.name, "foreign_key_constraint", False, compare_to + ): + modify_table_ops.ops.append( + ops.CreateForeignKeyOp.from_constraint(const.const) + ) + + log.info( + "Detected added foreign key (%s)(%s) on table %s%s", + ", ".join(obj.source_columns), + ", ".join(obj.target_columns), + "%s." % obj.source_schema if obj.source_schema else "", + obj.source_table, + ) + + def _remove_fk(obj, compare_to): + if autogen_context.run_object_filters( + obj.const, obj.name, "foreign_key_constraint", True, compare_to + ): + modify_table_ops.ops.append( + ops.DropConstraintOp.from_constraint(obj.const) + ) + log.info( + "Detected removed foreign key (%s)(%s) on table %s%s", + ", ".join(obj.source_columns), + ", ".join(obj.target_columns), + "%s." % obj.source_schema if obj.source_schema else "", + obj.source_table, + ) + + # so far it appears we don't need to do this by name at all. + # SQLite doesn't preserve constraint names anyway + + for removed_sig in set(conn_fks_by_sig).difference(metadata_fks_by_sig): + const = conn_fks_by_sig[removed_sig] + if removed_sig not in metadata_fks_by_sig: + compare_to = ( + metadata_fks_by_name[const.name].const + if const.name and const.name in metadata_fks_by_name + else None + ) + _remove_fk(const, compare_to) + + for added_sig in set(metadata_fks_by_sig).difference(conn_fks_by_sig): + const = metadata_fks_by_sig[added_sig] + if added_sig not in conn_fks_by_sig: + compare_to = ( + conn_fks_by_name[const.name].const + if const.name and const.name in conn_fks_by_name + else None + ) + _add_fk(const, compare_to) + + return PriorityDispatchResult.CONTINUE + + +def _compare_nullable( + autogen_context: AutogenContext, + alter_column_op: AlterColumnOp, + schema: Optional[str], + tname: Union[quoted_name, str], + cname: Union[quoted_name, str], + conn_col: Column[Any], + metadata_col: Column[Any], +) -> PriorityDispatchResult: + metadata_col_nullable = metadata_col.nullable + conn_col_nullable = conn_col.nullable + alter_column_op.existing_nullable = conn_col_nullable + + if conn_col_nullable is not metadata_col_nullable: + if ( + sqla_compat._server_default_is_computed( + metadata_col.server_default, conn_col.server_default + ) + and sqla_compat._nullability_might_be_unset(metadata_col) + or ( + sqla_compat._server_default_is_identity( + metadata_col.server_default, conn_col.server_default + ) + ) + ): + log.info( + "Ignoring nullable change on identity column '%s.%s'", + tname, + cname, + ) + else: + alter_column_op.modify_nullable = metadata_col_nullable + log.info( + "Detected %s on column '%s.%s'", + "NULL" if metadata_col_nullable else "NOT NULL", + tname, + cname, + ) + # column nullablity changed, no further nullable checks needed + return PriorityDispatchResult.STOP + + return PriorityDispatchResult.CONTINUE + + +def setup(plugin: Plugin) -> None: + plugin.add_autogenerate_comparator( + _compare_indexes_and_uniques, + "table", + "indexes", + ) + plugin.add_autogenerate_comparator( + _compare_foreign_keys, + "table", + "foreignkeys", + ) + plugin.add_autogenerate_comparator( + _compare_nullable, + "column", + "nullable", + ) diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/schema.py b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/schema.py new file mode 100644 index 00000000..1f46aff4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/schema.py @@ -0,0 +1,62 @@ +# mypy: allow-untyped-calls + +from __future__ import annotations + +import logging +from typing import Optional +from typing import Set +from typing import TYPE_CHECKING + +from sqlalchemy import inspect + +from ...util import PriorityDispatchResult + +if TYPE_CHECKING: + from sqlalchemy.engine.reflection import Inspector + + from ...autogenerate.api import AutogenContext + from ...operations.ops import UpgradeOps + from ...runtime.plugins import Plugin + + +log = logging.getLogger(__name__) + + +def _produce_net_changes( + autogen_context: AutogenContext, upgrade_ops: UpgradeOps +) -> PriorityDispatchResult: + connection = autogen_context.connection + assert connection is not None + include_schemas = autogen_context.opts.get("include_schemas", False) + + inspector: Inspector = inspect(connection) + + default_schema = connection.dialect.default_schema_name + schemas: Set[Optional[str]] + if include_schemas: + schemas = set(inspector.get_schema_names()) + # replace default schema name with None + schemas.discard("information_schema") + # replace the "default" schema with None + schemas.discard(default_schema) + schemas.add(None) + else: + schemas = {None} + + schemas = { + s for s in schemas if autogen_context.run_name_filters(s, "schema", {}) + } + + assert autogen_context.dialect is not None + autogen_context.comparators.dispatch( + "schema", qualifier=autogen_context.dialect.name + )(autogen_context, upgrade_ops, schemas) + + return PriorityDispatchResult.CONTINUE + + +def setup(plugin: Plugin) -> None: + plugin.add_autogenerate_comparator( + _produce_net_changes, + "autogenerate", + ) diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/server_defaults.py b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/server_defaults.py new file mode 100644 index 00000000..1e09e8e2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/server_defaults.py @@ -0,0 +1,344 @@ +from __future__ import annotations + +import logging +import re +from types import NoneType +from typing import Any +from typing import cast +from typing import Optional +from typing import Sequence +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import schema as sa_schema +from sqlalchemy.sql.schema import DefaultClause + +from ... import util +from ...util import DispatchPriority +from ...util import PriorityDispatchResult +from ...util import sqla_compat + +if TYPE_CHECKING: + from sqlalchemy.sql.elements import quoted_name + from sqlalchemy.sql.schema import Column + + from ...autogenerate.api import AutogenContext + from ...operations.ops import AlterColumnOp + from ...runtime.plugins import Plugin + +log = logging.getLogger(__name__) + + +def _render_server_default_for_compare( + metadata_default: Optional[Any], autogen_context: AutogenContext +) -> Optional[str]: + if isinstance(metadata_default, sa_schema.DefaultClause): + if isinstance(metadata_default.arg, str): + metadata_default = metadata_default.arg + else: + metadata_default = str( + metadata_default.arg.compile( + dialect=autogen_context.dialect, + compile_kwargs={"literal_binds": True}, + ) + ) + if isinstance(metadata_default, str): + return metadata_default + else: + return None + + +def _normalize_computed_default(sqltext: str) -> str: + """we want to warn if a computed sql expression has changed. however + we don't want false positives and the warning is not that critical. + so filter out most forms of variability from the SQL text. + + """ + + return re.sub(r"[ \(\)'\"`\[\]\t\r\n]", "", sqltext).lower() + + +def _compare_computed_default( + autogen_context: AutogenContext, + alter_column_op: AlterColumnOp, + schema: Optional[str], + tname: str, + cname: str, + conn_col: Column[Any], + metadata_col: Column[Any], +) -> PriorityDispatchResult: + + metadata_default = metadata_col.server_default + conn_col_default = conn_col.server_default + if conn_col_default is None and metadata_default is None: + return PriorityDispatchResult.CONTINUE + + if sqla_compat._server_default_is_computed( + conn_col_default + ) and not sqla_compat._server_default_is_computed(metadata_default): + _warn_computed_not_supported(tname, cname) + return PriorityDispatchResult.STOP + + if not sqla_compat._server_default_is_computed(metadata_default): + return PriorityDispatchResult.CONTINUE + + rendered_metadata_default = str( + cast(sa_schema.Computed, metadata_col.server_default).sqltext.compile( + dialect=autogen_context.dialect, + compile_kwargs={"literal_binds": True}, + ) + ) + + # since we cannot change computed columns, we do only a crude comparison + # here where we try to eliminate syntactical differences in order to + # get a minimal comparison just to emit a warning. + + rendered_metadata_default = _normalize_computed_default( + rendered_metadata_default + ) + + if isinstance(conn_col.server_default, sa_schema.Computed): + rendered_conn_default = str( + conn_col.server_default.sqltext.compile( + dialect=autogen_context.dialect, + compile_kwargs={"literal_binds": True}, + ) + ) + rendered_conn_default = _normalize_computed_default( + rendered_conn_default + ) + else: + rendered_conn_default = "" + + if rendered_metadata_default != rendered_conn_default: + _warn_computed_not_supported(tname, cname) + + return PriorityDispatchResult.STOP + + +def _warn_computed_not_supported(tname: str, cname: str) -> None: + util.warn("Computed default on %s.%s cannot be modified" % (tname, cname)) + + +def _compare_identity_default( + autogen_context: AutogenContext, + alter_column_op: AlterColumnOp, + schema: Optional[str], + tname: Union[quoted_name, str], + cname: Union[quoted_name, str], + conn_col: Column[Any], + metadata_col: Column[Any], + skip: Sequence[str] = ( + "order", + "on_null", + "oracle_order", + "oracle_on_null", + ), +) -> PriorityDispatchResult: + + metadata_default = metadata_col.server_default + conn_col_default = conn_col.server_default + if ( + conn_col_default is None + and metadata_default is None + or not sqla_compat._server_default_is_identity( + metadata_default, conn_col_default + ) + ): + return PriorityDispatchResult.CONTINUE + + assert isinstance( + metadata_col.server_default, + (sa_schema.Identity, sa_schema.Sequence, NoneType), + ) + assert isinstance( + conn_col.server_default, + (sa_schema.Identity, sa_schema.Sequence, NoneType), + ) + + impl = autogen_context.migration_context.impl + diff, _, is_alter = impl._compare_identity_default( # type: ignore[no-untyped-call] # noqa: E501 + metadata_col.server_default, conn_col.server_default + ) + + if is_alter: + alter_column_op.modify_server_default = metadata_default + if diff: + log.info( + "Detected server default on column '%s.%s': " + "identity options attributes %s", + tname, + cname, + sorted(diff), + ) + + return PriorityDispatchResult.STOP + + return PriorityDispatchResult.CONTINUE + + +def _user_compare_server_default( + autogen_context: AutogenContext, + alter_column_op: AlterColumnOp, + schema: Optional[str], + tname: Union[quoted_name, str], + cname: Union[quoted_name, str], + conn_col: Column[Any], + metadata_col: Column[Any], +) -> PriorityDispatchResult: + + metadata_default = metadata_col.server_default + conn_col_default = conn_col.server_default + if conn_col_default is None and metadata_default is None: + return PriorityDispatchResult.CONTINUE + + alter_column_op.existing_server_default = conn_col_default + + migration_context = autogen_context.migration_context + + if migration_context._user_compare_server_default is False: + return PriorityDispatchResult.STOP + + if not callable(migration_context._user_compare_server_default): + return PriorityDispatchResult.CONTINUE + + rendered_metadata_default = _render_server_default_for_compare( + metadata_default, autogen_context + ) + rendered_conn_default = ( + cast(Any, conn_col_default).arg.text if conn_col_default else None + ) + + is_diff = migration_context._user_compare_server_default( + migration_context, + conn_col, + metadata_col, + rendered_conn_default, + metadata_col.server_default, + rendered_metadata_default, + ) + if is_diff: + alter_column_op.modify_server_default = metadata_default + log.info( + "User defined function %s detected " + "server default on column '%s.%s'", + migration_context._user_compare_server_default, + tname, + cname, + ) + return PriorityDispatchResult.STOP + elif is_diff is False: + # if user compare server_default returns False and not None, + # it means "dont do any more server_default comparison" + return PriorityDispatchResult.STOP + + return PriorityDispatchResult.CONTINUE + + +def _dialect_impl_compare_server_default( + autogen_context: AutogenContext, + alter_column_op: AlterColumnOp, + schema: Optional[str], + tname: Union[quoted_name, str], + cname: Union[quoted_name, str], + conn_col: Column[Any], + metadata_col: Column[Any], +) -> PriorityDispatchResult: + """use dialect.impl.compare_server_default. + + This would in theory not be needed. however we dont know if any + third party libraries haven't made their own alembic dialect and + implemented this method. + + """ + metadata_default = metadata_col.server_default + conn_col_default = conn_col.server_default + if conn_col_default is None and metadata_default is None: + return PriorityDispatchResult.CONTINUE + + # this is already done by _user_compare_server_default, + # but doing it here also for unit tests that want to call + # _dialect_impl_compare_server_default directly + alter_column_op.existing_server_default = conn_col_default + + if not isinstance( + metadata_default, (DefaultClause, NoneType) + ) or not isinstance(conn_col_default, (DefaultClause, NoneType)): + return PriorityDispatchResult.CONTINUE + + migration_context = autogen_context.migration_context + + rendered_metadata_default = _render_server_default_for_compare( + metadata_default, autogen_context + ) + rendered_conn_default = ( + cast(Any, conn_col_default).arg.text if conn_col_default else None + ) + + is_diff = migration_context.impl.compare_server_default( # type: ignore[no-untyped-call] # noqa: E501 + conn_col, + metadata_col, + rendered_metadata_default, + rendered_conn_default, + ) + if is_diff: + alter_column_op.modify_server_default = metadata_default + log.info( + "Dialect impl %s detected server default on column '%s.%s'", + migration_context.impl, + tname, + cname, + ) + return PriorityDispatchResult.STOP + return PriorityDispatchResult.CONTINUE + + +def _setup_autoincrement( + autogen_context: AutogenContext, + alter_column_op: AlterColumnOp, + schema: Optional[str], + tname: Union[quoted_name, str], + cname: quoted_name, + conn_col: Column[Any], + metadata_col: Column[Any], +) -> PriorityDispatchResult: + if metadata_col.table._autoincrement_column is metadata_col: + alter_column_op.kw["autoincrement"] = True + elif metadata_col.autoincrement is True: + alter_column_op.kw["autoincrement"] = True + elif metadata_col.autoincrement is False: + alter_column_op.kw["autoincrement"] = False + + return PriorityDispatchResult.CONTINUE + + +def setup(plugin: Plugin) -> None: + plugin.add_autogenerate_comparator( + _user_compare_server_default, + "column", + "server_default", + priority=DispatchPriority.FIRST, + ) + plugin.add_autogenerate_comparator( + _compare_computed_default, + "column", + "server_default", + ) + + plugin.add_autogenerate_comparator( + _compare_identity_default, + "column", + "server_default", + ) + + plugin.add_autogenerate_comparator( + _setup_autoincrement, + "column", + "server_default", + ) + plugin.add_autogenerate_comparator( + _dialect_impl_compare_server_default, + "column", + "server_default", + priority=DispatchPriority.LAST, + ) diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/tables.py b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/tables.py new file mode 100644 index 00000000..31eddc6b --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/tables.py @@ -0,0 +1,316 @@ +# mypy: allow-untyped-calls + +from __future__ import annotations + +import contextlib +import logging +from typing import Iterator +from typing import Optional +from typing import Set +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import event +from sqlalchemy import schema as sa_schema +from sqlalchemy.util import OrderedSet + +from .util import _InspectorConv +from ...operations import ops +from ...util import PriorityDispatchResult + +if TYPE_CHECKING: + from sqlalchemy.engine.reflection import Inspector + from sqlalchemy.sql.elements import quoted_name + from sqlalchemy.sql.schema import Table + + from ...autogenerate.api import AutogenContext + from ...operations.ops import ModifyTableOps + from ...operations.ops import UpgradeOps + from ...runtime.plugins import Plugin + + +log = logging.getLogger(__name__) + + +def _autogen_for_tables( + autogen_context: AutogenContext, + upgrade_ops: UpgradeOps, + schemas: Set[Optional[str]], +) -> PriorityDispatchResult: + inspector = autogen_context.inspector + + conn_table_names: Set[Tuple[Optional[str], str]] = set() + + version_table_schema = ( + autogen_context.migration_context.version_table_schema + ) + version_table = autogen_context.migration_context.version_table + + for schema_name in schemas: + tables = available = set(inspector.get_table_names(schema=schema_name)) + if schema_name == version_table_schema: + tables = tables.difference( + [autogen_context.migration_context.version_table] + ) + + tablenames = [ + tname + for tname in tables + if autogen_context.run_name_filters( + tname, "table", {"schema_name": schema_name} + ) + ] + + conn_table_names.update((schema_name, tname) for tname in tablenames) + + inspector = autogen_context.inspector + insp = _InspectorConv(inspector) + insp.pre_cache_tables(schema_name, tablenames, available) + + metadata_table_names = OrderedSet( + [(table.schema, table.name) for table in autogen_context.sorted_tables] + ).difference([(version_table_schema, version_table)]) + + _compare_tables( + conn_table_names, + metadata_table_names, + inspector, + upgrade_ops, + autogen_context, + ) + + return PriorityDispatchResult.CONTINUE + + +def _compare_tables( + conn_table_names: set[tuple[str | None, str]], + metadata_table_names: set[tuple[str | None, str]], + inspector: Inspector, + upgrade_ops: UpgradeOps, + autogen_context: AutogenContext, +) -> None: + default_schema = inspector.bind.dialect.default_schema_name + + # tables coming from the connection will not have "schema" + # set if it matches default_schema_name; so we need a list + # of table names from local metadata that also have "None" if schema + # == default_schema_name. Most setups will be like this anyway but + # some are not (see #170) + metadata_table_names_no_dflt_schema = OrderedSet( + [ + (schema if schema != default_schema else None, tname) + for schema, tname in metadata_table_names + ] + ) + + # to adjust for the MetaData collection storing the tables either + # as "schemaname.tablename" or just "tablename", create a new lookup + # which will match the "non-default-schema" keys to the Table object. + tname_to_table = { + no_dflt_schema: autogen_context.table_key_to_table[ + sa_schema._get_table_key(tname, schema) + ] + for no_dflt_schema, (schema, tname) in zip( + metadata_table_names_no_dflt_schema, metadata_table_names + ) + } + metadata_table_names = metadata_table_names_no_dflt_schema + + for s, tname in metadata_table_names.difference(conn_table_names): + name = "%s.%s" % (s, tname) if s else tname + metadata_table = tname_to_table[(s, tname)] + if autogen_context.run_object_filters( + metadata_table, tname, "table", False, None + ): + upgrade_ops.ops.append( + ops.CreateTableOp.from_table(metadata_table) + ) + log.info("Detected added table %r", name) + modify_table_ops = ops.ModifyTableOps(tname, [], schema=s) + + autogen_context.comparators.dispatch( + "table", qualifier=autogen_context.dialect.name + )( + autogen_context, + modify_table_ops, + s, + tname, + None, + metadata_table, + ) + if not modify_table_ops.is_empty(): + upgrade_ops.ops.append(modify_table_ops) + + removal_metadata = sa_schema.MetaData() + for s, tname in conn_table_names.difference(metadata_table_names): + name = sa_schema._get_table_key(tname, s) + + # a name might be present already if a previous reflection pulled + # this table in via foreign key constraint + exists = name in removal_metadata.tables + t = sa_schema.Table(tname, removal_metadata, schema=s) + + if not exists: + event.listen( + t, + "column_reflect", + # fmt: off + autogen_context.migration_context.impl. + _compat_autogen_column_reflect + (inspector), + # fmt: on + ) + _InspectorConv(inspector).reflect_table(t) + if autogen_context.run_object_filters(t, tname, "table", True, None): + modify_table_ops = ops.ModifyTableOps(tname, [], schema=s) + + autogen_context.comparators.dispatch( + "table", qualifier=autogen_context.dialect.name + )(autogen_context, modify_table_ops, s, tname, t, None) + if not modify_table_ops.is_empty(): + upgrade_ops.ops.append(modify_table_ops) + + upgrade_ops.ops.append(ops.DropTableOp.from_table(t)) + log.info("Detected removed table %r", name) + + existing_tables = conn_table_names.intersection(metadata_table_names) + + existing_metadata = sa_schema.MetaData() + conn_column_info = {} + for s, tname in existing_tables: + name = sa_schema._get_table_key(tname, s) + exists = name in existing_metadata.tables + + # a name might be present already if a previous reflection pulled + # this table in via foreign key constraint + t = sa_schema.Table(tname, existing_metadata, schema=s) + if not exists: + event.listen( + t, + "column_reflect", + # fmt: off + autogen_context.migration_context.impl. + _compat_autogen_column_reflect(inspector), + # fmt: on + ) + _InspectorConv(inspector).reflect_table(t) + + conn_column_info[(s, tname)] = t + + for s, tname in sorted(existing_tables, key=lambda x: (x[0] or "", x[1])): + s = s or None + name = "%s.%s" % (s, tname) if s else tname + metadata_table = tname_to_table[(s, tname)] + conn_table = existing_metadata.tables[name] + + if autogen_context.run_object_filters( + metadata_table, tname, "table", False, conn_table + ): + modify_table_ops = ops.ModifyTableOps(tname, [], schema=s) + with _compare_columns( + s, + tname, + conn_table, + metadata_table, + modify_table_ops, + autogen_context, + inspector, + ): + autogen_context.comparators.dispatch( + "table", qualifier=autogen_context.dialect.name + )( + autogen_context, + modify_table_ops, + s, + tname, + conn_table, + metadata_table, + ) + + if not modify_table_ops.is_empty(): + upgrade_ops.ops.append(modify_table_ops) + + +@contextlib.contextmanager +def _compare_columns( + schema: Optional[str], + tname: Union[quoted_name, str], + conn_table: Table, + metadata_table: Table, + modify_table_ops: ModifyTableOps, + autogen_context: AutogenContext, + inspector: Inspector, +) -> Iterator[None]: + name = "%s.%s" % (schema, tname) if schema else tname + metadata_col_names = OrderedSet( + c.name for c in metadata_table.c if not c.system + ) + metadata_cols_by_name = { + c.name: c for c in metadata_table.c if not c.system + } + + conn_col_names = { + c.name: c + for c in conn_table.c + if autogen_context.run_name_filters( + c.name, "column", {"table_name": tname, "schema_name": schema} + ) + } + + for cname in metadata_col_names.difference(conn_col_names): + if autogen_context.run_object_filters( + metadata_cols_by_name[cname], cname, "column", False, None + ): + modify_table_ops.ops.append( + ops.AddColumnOp.from_column_and_tablename( + schema, tname, metadata_cols_by_name[cname] + ) + ) + log.info("Detected added column '%s.%s'", name, cname) + + for colname in metadata_col_names.intersection(conn_col_names): + metadata_col = metadata_cols_by_name[colname] + conn_col = conn_table.c[colname] + if not autogen_context.run_object_filters( + metadata_col, colname, "column", False, conn_col + ): + continue + alter_column_op = ops.AlterColumnOp(tname, colname, schema=schema) + + autogen_context.comparators.dispatch( + "column", qualifier=autogen_context.dialect.name + )( + autogen_context, + alter_column_op, + schema, + tname, + colname, + conn_col, + metadata_col, + ) + + if alter_column_op.has_changes(): + modify_table_ops.ops.append(alter_column_op) + + yield + + for cname in set(conn_col_names).difference(metadata_col_names): + if autogen_context.run_object_filters( + conn_table.c[cname], cname, "column", True, None + ): + modify_table_ops.ops.append( + ops.DropColumnOp.from_column_and_tablename( + schema, tname, conn_table.c[cname] + ) + ) + log.info("Detected removed column '%s.%s'", name, cname) + + +def setup(plugin: Plugin) -> None: + + plugin.add_autogenerate_comparator( + _autogen_for_tables, + "schema", + "tables", + ) diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/types.py b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/types.py new file mode 100644 index 00000000..1d5d160a --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/types.py @@ -0,0 +1,147 @@ +from __future__ import annotations + +import logging +from typing import Any +from typing import Optional +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import types as sqltypes + +from ...util import DispatchPriority +from ...util import PriorityDispatchResult + +if TYPE_CHECKING: + from sqlalchemy.sql.elements import quoted_name + from sqlalchemy.sql.schema import Column + + from ...autogenerate.api import AutogenContext + from ...operations.ops import AlterColumnOp + from ...runtime.plugins import Plugin + + +log = logging.getLogger(__name__) + + +def _compare_type_setup( + alter_column_op: AlterColumnOp, + tname: Union[quoted_name, str], + cname: Union[quoted_name, str], + conn_col: Column[Any], + metadata_col: Column[Any], +) -> bool: + + conn_type = conn_col.type + alter_column_op.existing_type = conn_type + metadata_type = metadata_col.type + if conn_type._type_affinity is sqltypes.NullType: + log.info( + "Couldn't determine database type for column '%s.%s'", + tname, + cname, + ) + return False + if metadata_type._type_affinity is sqltypes.NullType: + log.info( + "Column '%s.%s' has no type within the model; can't compare", + tname, + cname, + ) + return False + + return True + + +def _user_compare_type( + autogen_context: AutogenContext, + alter_column_op: AlterColumnOp, + schema: Optional[str], + tname: Union[quoted_name, str], + cname: Union[quoted_name, str], + conn_col: Column[Any], + metadata_col: Column[Any], +) -> PriorityDispatchResult: + + migration_context = autogen_context.migration_context + + if migration_context._user_compare_type is False: + return PriorityDispatchResult.STOP + + if not _compare_type_setup( + alter_column_op, tname, cname, conn_col, metadata_col + ): + return PriorityDispatchResult.CONTINUE + + if not callable(migration_context._user_compare_type): + return PriorityDispatchResult.CONTINUE + + is_diff = migration_context._user_compare_type( + migration_context, + conn_col, + metadata_col, + conn_col.type, + metadata_col.type, + ) + if is_diff: + alter_column_op.modify_type = metadata_col.type + log.info( + "Detected type change from %r to %r on '%s.%s'", + conn_col.type, + metadata_col.type, + tname, + cname, + ) + return PriorityDispatchResult.STOP + elif is_diff is False: + # if user compare type returns False and not None, + # it means "dont do any more type comparison" + return PriorityDispatchResult.STOP + + return PriorityDispatchResult.CONTINUE + + +def _dialect_impl_compare_type( + autogen_context: AutogenContext, + alter_column_op: AlterColumnOp, + schema: Optional[str], + tname: Union[quoted_name, str], + cname: Union[quoted_name, str], + conn_col: Column[Any], + metadata_col: Column[Any], +) -> PriorityDispatchResult: + + if not _compare_type_setup( + alter_column_op, tname, cname, conn_col, metadata_col + ): + return PriorityDispatchResult.CONTINUE + + migration_context = autogen_context.migration_context + is_diff = migration_context.impl.compare_type(conn_col, metadata_col) + + if is_diff: + alter_column_op.modify_type = metadata_col.type + log.info( + "Detected type change from %r to %r on '%s.%s'", + conn_col.type, + metadata_col.type, + tname, + cname, + ) + return PriorityDispatchResult.STOP + + return PriorityDispatchResult.CONTINUE + + +def setup(plugin: Plugin) -> None: + plugin.add_autogenerate_comparator( + _user_compare_type, + "column", + "types", + priority=DispatchPriority.FIRST, + ) + plugin.add_autogenerate_comparator( + _dialect_impl_compare_type, + "column", + "types", + priority=DispatchPriority.LAST, + ) diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/util.py b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/util.py new file mode 100644 index 00000000..41829c0e --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/autogenerate/compare/util.py @@ -0,0 +1,314 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics +from __future__ import annotations + +from typing import Any +from typing import cast +from typing import Collection +from typing import TYPE_CHECKING + +from sqlalchemy.sql.elements import conv +from typing_extensions import Self + +from ...util import sqla_compat + +if TYPE_CHECKING: + from sqlalchemy import Table + from sqlalchemy.engine import Inspector + from sqlalchemy.engine.interfaces import ReflectedForeignKeyConstraint + from sqlalchemy.engine.interfaces import ReflectedIndex + from sqlalchemy.engine.interfaces import ReflectedUniqueConstraint + from sqlalchemy.engine.reflection import _ReflectionInfo + +_INSP_KEYS = ( + "columns", + "pk_constraint", + "foreign_keys", + "indexes", + "unique_constraints", + "table_comment", + "check_constraints", + "table_options", +) +_CONSTRAINT_INSP_KEYS = ( + "pk_constraint", + "foreign_keys", + "indexes", + "unique_constraints", + "check_constraints", +) + + +class _InspectorConv: + __slots__ = ("inspector",) + + def __new__(cls, inspector: Inspector) -> Self: + obj: Any + if sqla_compat.sqla_2: + obj = object.__new__(_SQLA2InspectorConv) + _SQLA2InspectorConv.__init__(obj, inspector) + else: + obj = object.__new__(_LegacyInspectorConv) + _LegacyInspectorConv.__init__(obj, inspector) + return cast(Self, obj) + + def __init__(self, inspector: Inspector): + self.inspector = inspector + + def pre_cache_tables( + self, + schema: str | None, + tablenames: list[str], + all_available_tablenames: Collection[str], + ) -> None: + pass + + def get_unique_constraints( + self, tname: str, schema: str | None + ) -> list[ReflectedUniqueConstraint]: + raise NotImplementedError() + + def get_indexes( + self, tname: str, schema: str | None + ) -> list[ReflectedIndex]: + raise NotImplementedError() + + def get_foreign_keys( + self, tname: str, schema: str | None + ) -> list[ReflectedForeignKeyConstraint]: + raise NotImplementedError() + + def reflect_table(self, table: Table) -> None: + raise NotImplementedError() + + +class _LegacyInspectorConv(_InspectorConv): + + def _apply_reflectinfo_conv(self, consts): + if not consts: + return consts + for const in consts: + if const["name"] is not None and not isinstance( + const["name"], conv + ): + const["name"] = conv(const["name"]) + return consts + + def _apply_constraint_conv(self, consts): + if not consts: + return consts + for const in consts: + if const.name is not None and not isinstance(const.name, conv): + const.name = conv(const.name) + return consts + + def get_indexes( + self, tname: str, schema: str | None + ) -> list[ReflectedIndex]: + return self._apply_reflectinfo_conv( + self.inspector.get_indexes(tname, schema=schema) + ) + + def get_unique_constraints( + self, tname: str, schema: str | None + ) -> list[ReflectedUniqueConstraint]: + return self._apply_reflectinfo_conv( + self.inspector.get_unique_constraints(tname, schema=schema) + ) + + def get_foreign_keys( + self, tname: str, schema: str | None + ) -> list[ReflectedForeignKeyConstraint]: + return self._apply_reflectinfo_conv( + self.inspector.get_foreign_keys(tname, schema=schema) + ) + + def reflect_table(self, table: Table) -> None: + self.inspector.reflect_table(table, include_columns=None) + + self._apply_constraint_conv(table.constraints) + self._apply_constraint_conv(table.indexes) + + +class _SQLA2InspectorConv(_InspectorConv): + + def _pre_cache( + self, + schema: str | None, + tablenames: list[str], + all_available_tablenames: Collection[str], + info_key: str, + inspector_method: Any, + ) -> None: + + if info_key in self.inspector.info_cache: + return + + # heuristic vendored from SQLAlchemy 2.0 + # if more than 50% of the tables in the db are in filter_names load all + # the tables, since it's most likely faster to avoid a filter on that + # many tables. also if a dialect doesnt have a "multi" method then + # return the filter names + if tablenames and all_available_tablenames and len(tablenames) > 100: + fraction = len(tablenames) / len(all_available_tablenames) + else: + fraction = None + + if ( + fraction is None + or fraction <= 0.5 + or not self.inspector.dialect._overrides_default( + inspector_method.__name__ + ) + ): + optimized_filter_names = tablenames + else: + optimized_filter_names = None + + try: + elements = inspector_method( + schema=schema, filter_names=optimized_filter_names + ) + except NotImplementedError: + self.inspector.info_cache[info_key] = NotImplementedError + else: + self.inspector.info_cache[info_key] = elements + + def _return_from_cache( + self, + tname: str, + schema: str | None, + info_key: str, + inspector_method: Any, + apply_constraint_conv: bool = False, + optional=True, + ) -> Any: + not_in_cache = object() + + if info_key in self.inspector.info_cache: + cache = self.inspector.info_cache[info_key] + if cache is NotImplementedError: + if optional: + return {} + else: + # maintain NotImplementedError as alembic compare + # uses these to determine classes of construct that it + # should not compare to DB elements + raise NotImplementedError() + + individual = cache.get((schema, tname), not_in_cache) + + if individual is not not_in_cache: + if apply_constraint_conv and individual is not None: + return self._apply_reflectinfo_conv(individual) + else: + return individual + + try: + data = inspector_method(tname, schema=schema) + except NotImplementedError: + if optional: + return {} + else: + raise + + if apply_constraint_conv: + return self._apply_reflectinfo_conv(data) + else: + return data + + def get_unique_constraints( + self, tname: str, schema: str | None + ) -> list[ReflectedUniqueConstraint]: + return self._return_from_cache( + tname, + schema, + "alembic_unique_constraints", + self.inspector.get_unique_constraints, + apply_constraint_conv=True, + optional=False, + ) + + def get_indexes( + self, tname: str, schema: str | None + ) -> list[ReflectedIndex]: + return self._return_from_cache( + tname, + schema, + "alembic_indexes", + self.inspector.get_indexes, + apply_constraint_conv=True, + optional=False, + ) + + def get_foreign_keys( + self, tname: str, schema: str | None + ) -> list[ReflectedForeignKeyConstraint]: + return self._return_from_cache( + tname, + schema, + "alembic_foreign_keys", + self.inspector.get_foreign_keys, + apply_constraint_conv=True, + ) + + def _apply_reflectinfo_conv(self, consts): + if not consts: + return consts + for const in consts if not isinstance(consts, dict) else [consts]: + if const["name"] is not None and not isinstance( + const["name"], conv + ): + const["name"] = conv(const["name"]) + return consts + + def pre_cache_tables( + self, + schema: str | None, + tablenames: list[str], + all_available_tablenames: Collection[str], + ) -> None: + for key in _INSP_KEYS: + keyname = f"alembic_{key}" + meth = getattr(self.inspector, f"get_multi_{key}") + + self._pre_cache( + schema, + tablenames, + all_available_tablenames, + keyname, + meth, + ) + + def _make_reflection_info( + self, tname: str, schema: str | None + ) -> _ReflectionInfo: + from sqlalchemy.engine.reflection import _ReflectionInfo + + table_key = (schema, tname) + + return _ReflectionInfo( + unreflectable={}, + **{ + key: { + table_key: self._return_from_cache( + tname, + schema, + f"alembic_{key}", + getattr(self.inspector, f"get_{key}"), + apply_constraint_conv=(key in _CONSTRAINT_INSP_KEYS), + ) + } + for key in _INSP_KEYS + }, + ) + + def reflect_table(self, table: Table) -> None: + ri = self._make_reflection_info(table.name, table.schema) + + self.inspector.reflect_table( + table, + include_columns=None, + resolve_fks=False, + _reflect_info=ri, + ) diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/render.py b/venv/lib/python3.12/site-packages/alembic/autogenerate/render.py new file mode 100644 index 00000000..7f32838d --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/autogenerate/render.py @@ -0,0 +1,1172 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +from io import StringIO +import re +from typing import Any +from typing import cast +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +from mako.pygen import PythonPrinter +from sqlalchemy import schema as sa_schema +from sqlalchemy import sql +from sqlalchemy import types as sqltypes +from sqlalchemy.sql.base import _DialectArgView +from sqlalchemy.sql.elements import conv +from sqlalchemy.sql.elements import Label +from sqlalchemy.sql.elements import quoted_name + +from .. import util +from ..operations import ops +from ..util import sqla_compat + +if TYPE_CHECKING: + from typing import Literal + + from sqlalchemy import Computed + from sqlalchemy import Identity + from sqlalchemy.sql.elements import ColumnElement + from sqlalchemy.sql.elements import TextClause + from sqlalchemy.sql.schema import CheckConstraint + from sqlalchemy.sql.schema import Column + from sqlalchemy.sql.schema import Constraint + from sqlalchemy.sql.schema import FetchedValue + from sqlalchemy.sql.schema import ForeignKey + from sqlalchemy.sql.schema import ForeignKeyConstraint + from sqlalchemy.sql.schema import Index + from sqlalchemy.sql.schema import MetaData + from sqlalchemy.sql.schema import PrimaryKeyConstraint + from sqlalchemy.sql.schema import UniqueConstraint + from sqlalchemy.sql.sqltypes import ARRAY + from sqlalchemy.sql.type_api import TypeEngine + + from alembic.autogenerate.api import AutogenContext + from alembic.config import Config + from alembic.operations.ops import MigrationScript + from alembic.operations.ops import ModifyTableOps + + +MAX_PYTHON_ARGS = 255 + + +def _render_gen_name( + autogen_context: AutogenContext, + name: sqla_compat._ConstraintName, +) -> Optional[Union[quoted_name, str, _f_name]]: + if isinstance(name, conv): + return _f_name(_alembic_autogenerate_prefix(autogen_context), name) + else: + return sqla_compat.constraint_name_or_none(name) + + +def _indent(text: str) -> str: + text = re.compile(r"^", re.M).sub(" ", text).strip() + text = re.compile(r" +$", re.M).sub("", text) + return text + + +def _render_python_into_templatevars( + autogen_context: AutogenContext, + migration_script: MigrationScript, + template_args: Dict[str, Union[str, Config]], +) -> None: + imports = autogen_context.imports + + for upgrade_ops, downgrade_ops in zip( + migration_script.upgrade_ops_list, migration_script.downgrade_ops_list + ): + template_args[upgrade_ops.upgrade_token] = _indent( + _render_cmd_body(upgrade_ops, autogen_context) + ) + template_args[downgrade_ops.downgrade_token] = _indent( + _render_cmd_body(downgrade_ops, autogen_context) + ) + template_args["imports"] = "\n".join(sorted(imports)) + + +default_renderers = renderers = util.Dispatcher() + + +def _render_cmd_body( + op_container: ops.OpContainer, + autogen_context: AutogenContext, +) -> str: + buf = StringIO() + printer = PythonPrinter(buf) + + printer.writeline( + "# ### commands auto generated by Alembic - please adjust! ###" + ) + + has_lines = False + for op in op_container.ops: + lines = render_op(autogen_context, op) + has_lines = has_lines or bool(lines) + + for line in lines: + printer.writeline(line) + + if not has_lines: + printer.writeline("pass") + + printer.writeline("# ### end Alembic commands ###") + + return buf.getvalue() + + +def render_op( + autogen_context: AutogenContext, op: ops.MigrateOperation +) -> List[str]: + renderer = renderers.dispatch(op) + lines = util.to_list(renderer(autogen_context, op)) + return lines + + +def render_op_text( + autogen_context: AutogenContext, op: ops.MigrateOperation +) -> str: + return "\n".join(render_op(autogen_context, op)) + + +@renderers.dispatch_for(ops.ModifyTableOps) +def _render_modify_table( + autogen_context: AutogenContext, op: ModifyTableOps +) -> List[str]: + opts = autogen_context.opts + render_as_batch = opts.get("render_as_batch", False) + + if op.ops: + lines = [] + if render_as_batch: + with autogen_context._within_batch(): + lines.append( + "with op.batch_alter_table(%r, schema=%r) as batch_op:" + % (op.table_name, op.schema) + ) + for t_op in op.ops: + t_lines = render_op(autogen_context, t_op) + lines.extend(t_lines) + lines.append("") + else: + for t_op in op.ops: + t_lines = render_op(autogen_context, t_op) + lines.extend(t_lines) + + return lines + else: + return [] + + +@renderers.dispatch_for(ops.CreateTableCommentOp) +def _render_create_table_comment( + autogen_context: AutogenContext, op: ops.CreateTableCommentOp +) -> str: + if autogen_context._has_batch: + templ = ( + "{prefix}create_table_comment(\n" + "{indent}{comment},\n" + "{indent}existing_comment={existing}\n" + ")" + ) + else: + templ = ( + "{prefix}create_table_comment(\n" + "{indent}'{tname}',\n" + "{indent}{comment},\n" + "{indent}existing_comment={existing},\n" + "{indent}schema={schema}\n" + ")" + ) + return templ.format( + prefix=_alembic_autogenerate_prefix(autogen_context), + tname=op.table_name, + comment="%r" % op.comment if op.comment is not None else None, + existing=( + "%r" % op.existing_comment + if op.existing_comment is not None + else None + ), + schema="'%s'" % op.schema if op.schema is not None else None, + indent=" ", + ) + + +@renderers.dispatch_for(ops.DropTableCommentOp) +def _render_drop_table_comment( + autogen_context: AutogenContext, op: ops.DropTableCommentOp +) -> str: + if autogen_context._has_batch: + templ = ( + "{prefix}drop_table_comment(\n" + "{indent}existing_comment={existing}\n" + ")" + ) + else: + templ = ( + "{prefix}drop_table_comment(\n" + "{indent}'{tname}',\n" + "{indent}existing_comment={existing},\n" + "{indent}schema={schema}\n" + ")" + ) + return templ.format( + prefix=_alembic_autogenerate_prefix(autogen_context), + tname=op.table_name, + existing=( + "%r" % op.existing_comment + if op.existing_comment is not None + else None + ), + schema="'%s'" % op.schema if op.schema is not None else None, + indent=" ", + ) + + +@renderers.dispatch_for(ops.CreateTableOp) +def _add_table(autogen_context: AutogenContext, op: ops.CreateTableOp) -> str: + table = op.to_table() + + args = [ + col + for col in [ + _render_column(col, autogen_context) for col in table.columns + ] + if col + ] + sorted( + [ + rcons + for rcons in [ + _render_constraint( + cons, autogen_context, op._namespace_metadata + ) + for cons in table.constraints + ] + if rcons is not None + ] + ) + + if len(args) > MAX_PYTHON_ARGS: + args_str = "*[" + ",\n".join(args) + "]" + else: + args_str = ",\n".join(args) + + text = "%(prefix)screate_table(%(tablename)r,\n%(args)s" % { + "tablename": _ident(op.table_name), + "prefix": _alembic_autogenerate_prefix(autogen_context), + "args": args_str, + } + if op.schema: + text += ",\nschema=%r" % _ident(op.schema) + + comment = table.comment + if comment: + text += ",\ncomment=%r" % _ident(comment) + + info = table.info + if info: + text += f",\ninfo={info!r}" + + for k in sorted(op.kw): + text += ",\n%s=%r" % (k.replace(" ", "_"), op.kw[k]) + + if table._prefixes: + prefixes = ", ".join("'%s'" % p for p in table._prefixes) + text += ",\nprefixes=[%s]" % prefixes + + if op.if_not_exists is not None: + text += ",\nif_not_exists=%r" % bool(op.if_not_exists) + + text += "\n)" + return text + + +@renderers.dispatch_for(ops.DropTableOp) +def _drop_table(autogen_context: AutogenContext, op: ops.DropTableOp) -> str: + text = "%(prefix)sdrop_table(%(tname)r" % { + "prefix": _alembic_autogenerate_prefix(autogen_context), + "tname": _ident(op.table_name), + } + if op.schema: + text += ", schema=%r" % _ident(op.schema) + + if op.if_exists is not None: + text += ", if_exists=%r" % bool(op.if_exists) + + text += ")" + return text + + +def _render_dialect_kwargs_items( + autogen_context: AutogenContext, dialect_kwargs: _DialectArgView +) -> list[str]: + return [ + f"{key}={_render_potential_expr(val, autogen_context)}" + for key, val in dialect_kwargs.items() + ] + + +@renderers.dispatch_for(ops.CreateIndexOp) +def _add_index(autogen_context: AutogenContext, op: ops.CreateIndexOp) -> str: + index = op.to_index() + + has_batch = autogen_context._has_batch + + if has_batch: + tmpl = ( + "%(prefix)screate_index(%(name)r, [%(columns)s], " + "unique=%(unique)r%(kwargs)s)" + ) + else: + tmpl = ( + "%(prefix)screate_index(%(name)r, %(table)r, [%(columns)s], " + "unique=%(unique)r%(schema)s%(kwargs)s)" + ) + + assert index.table is not None + + opts = _render_dialect_kwargs_items(autogen_context, index.dialect_kwargs) + if op.if_not_exists is not None: + opts.append("if_not_exists=%r" % bool(op.if_not_exists)) + text = tmpl % { + "prefix": _alembic_autogenerate_prefix(autogen_context), + "name": _render_gen_name(autogen_context, index.name), + "table": _ident(index.table.name), + "columns": ", ".join( + _get_index_rendered_expressions(index, autogen_context) + ), + "unique": index.unique or False, + "schema": ( + (", schema=%r" % _ident(index.table.schema)) + if index.table.schema + else "" + ), + "kwargs": ", " + ", ".join(opts) if opts else "", + } + return text + + +@renderers.dispatch_for(ops.DropIndexOp) +def _drop_index(autogen_context: AutogenContext, op: ops.DropIndexOp) -> str: + index = op.to_index() + + has_batch = autogen_context._has_batch + + if has_batch: + tmpl = "%(prefix)sdrop_index(%(name)r%(kwargs)s)" + else: + tmpl = ( + "%(prefix)sdrop_index(%(name)r, " + "table_name=%(table_name)r%(schema)s%(kwargs)s)" + ) + opts = _render_dialect_kwargs_items(autogen_context, index.dialect_kwargs) + if op.if_exists is not None: + opts.append("if_exists=%r" % bool(op.if_exists)) + text = tmpl % { + "prefix": _alembic_autogenerate_prefix(autogen_context), + "name": _render_gen_name(autogen_context, op.index_name), + "table_name": _ident(op.table_name), + "schema": ((", schema=%r" % _ident(op.schema)) if op.schema else ""), + "kwargs": ", " + ", ".join(opts) if opts else "", + } + return text + + +@renderers.dispatch_for(ops.CreateUniqueConstraintOp) +def _add_unique_constraint( + autogen_context: AutogenContext, op: ops.CreateUniqueConstraintOp +) -> List[str]: + return [_uq_constraint(op.to_constraint(), autogen_context, True)] + + +@renderers.dispatch_for(ops.CreateForeignKeyOp) +def _add_fk_constraint( + autogen_context: AutogenContext, op: ops.CreateForeignKeyOp +) -> str: + constraint = op.to_constraint() + args = [repr(_render_gen_name(autogen_context, op.constraint_name))] + if not autogen_context._has_batch: + args.append(repr(_ident(op.source_table))) + + args.extend( + [ + repr(_ident(op.referent_table)), + repr([_ident(col) for col in op.local_cols]), + repr([_ident(col) for col in op.remote_cols]), + ] + ) + kwargs = [ + "referent_schema", + "onupdate", + "ondelete", + "initially", + "deferrable", + "use_alter", + "match", + ] + if not autogen_context._has_batch: + kwargs.insert(0, "source_schema") + + for k in kwargs: + if k in op.kw: + value = op.kw[k] + if value is not None: + args.append("%s=%r" % (k, value)) + + dialect_kwargs = _render_dialect_kwargs_items( + autogen_context, constraint.dialect_kwargs + ) + + return "%(prefix)screate_foreign_key(%(args)s%(dialect_kwargs)s)" % { + "prefix": _alembic_autogenerate_prefix(autogen_context), + "args": ", ".join(args), + "dialect_kwargs": ( + ", " + ", ".join(dialect_kwargs) if dialect_kwargs else "" + ), + } + + +@renderers.dispatch_for(ops.CreatePrimaryKeyOp) +def _add_pk_constraint(constraint, autogen_context): + raise NotImplementedError() + + +@renderers.dispatch_for(ops.CreateCheckConstraintOp) +def _add_check_constraint(constraint, autogen_context): + raise NotImplementedError() + + +@renderers.dispatch_for(ops.DropConstraintOp) +def _drop_constraint( + autogen_context: AutogenContext, op: ops.DropConstraintOp +) -> str: + prefix = _alembic_autogenerate_prefix(autogen_context) + name = _render_gen_name(autogen_context, op.constraint_name) + schema = _ident(op.schema) if op.schema else None + type_ = _ident(op.constraint_type) if op.constraint_type else None + if_exists = op.if_exists + params_strs = [] + params_strs.append(repr(name)) + if not autogen_context._has_batch: + params_strs.append(repr(_ident(op.table_name))) + if schema is not None: + params_strs.append(f"schema={schema!r}") + if type_ is not None: + params_strs.append(f"type_={type_!r}") + if if_exists is not None: + params_strs.append(f"if_exists={if_exists}") + + return f"{prefix}drop_constraint({', '.join(params_strs)})" + + +@renderers.dispatch_for(ops.AddColumnOp) +def _add_column(autogen_context: AutogenContext, op: ops.AddColumnOp) -> str: + schema, tname, column, if_not_exists = ( + op.schema, + op.table_name, + op.column, + op.if_not_exists, + ) + if autogen_context._has_batch: + template = "%(prefix)sadd_column(%(column)s)" + else: + template = "%(prefix)sadd_column(%(tname)r, %(column)s" + if schema: + template += ", schema=%(schema)r" + if if_not_exists is not None: + template += ", if_not_exists=%(if_not_exists)r" + template += ")" + text = template % { + "prefix": _alembic_autogenerate_prefix(autogen_context), + "tname": tname, + "column": _render_column(column, autogen_context), + "schema": schema, + "if_not_exists": if_not_exists, + } + return text + + +@renderers.dispatch_for(ops.DropColumnOp) +def _drop_column(autogen_context: AutogenContext, op: ops.DropColumnOp) -> str: + schema, tname, column_name, if_exists = ( + op.schema, + op.table_name, + op.column_name, + op.if_exists, + ) + + if autogen_context._has_batch: + template = "%(prefix)sdrop_column(%(cname)r)" + else: + template = "%(prefix)sdrop_column(%(tname)r, %(cname)r" + if schema: + template += ", schema=%(schema)r" + if if_exists is not None: + template += ", if_exists=%(if_exists)r" + template += ")" + + text = template % { + "prefix": _alembic_autogenerate_prefix(autogen_context), + "tname": _ident(tname), + "cname": _ident(column_name), + "schema": _ident(schema), + "if_exists": if_exists, + } + return text + + +@renderers.dispatch_for(ops.AlterColumnOp) +def _alter_column( + autogen_context: AutogenContext, op: ops.AlterColumnOp +) -> str: + tname = op.table_name + cname = op.column_name + server_default = op.modify_server_default + type_ = op.modify_type + nullable = op.modify_nullable + comment = op.modify_comment + newname = op.modify_name + autoincrement = op.kw.get("autoincrement", None) + existing_type = op.existing_type + existing_nullable = op.existing_nullable + existing_comment = op.existing_comment + existing_server_default = op.existing_server_default + schema = op.schema + + indent = " " * 11 + + if autogen_context._has_batch: + template = "%(prefix)salter_column(%(cname)r" + else: + template = "%(prefix)salter_column(%(tname)r, %(cname)r" + + text = template % { + "prefix": _alembic_autogenerate_prefix(autogen_context), + "tname": tname, + "cname": cname, + } + if existing_type is not None: + text += ",\n%sexisting_type=%s" % ( + indent, + _repr_type(existing_type, autogen_context), + ) + if server_default is not False: + rendered = _render_server_default(server_default, autogen_context) + text += ",\n%sserver_default=%s" % (indent, rendered) + + if newname is not None: + text += ",\n%snew_column_name=%r" % (indent, newname) + if type_ is not None: + text += ",\n%stype_=%s" % (indent, _repr_type(type_, autogen_context)) + if nullable is not None: + text += ",\n%snullable=%r" % (indent, nullable) + if comment is not False: + text += ",\n%scomment=%r" % (indent, comment) + if existing_comment is not None: + text += ",\n%sexisting_comment=%r" % (indent, existing_comment) + if nullable is None and existing_nullable is not None: + text += ",\n%sexisting_nullable=%r" % (indent, existing_nullable) + if autoincrement is not None: + text += ",\n%sautoincrement=%r" % (indent, autoincrement) + if server_default is False and existing_server_default: + rendered = _render_server_default( + existing_server_default, autogen_context + ) + text += ",\n%sexisting_server_default=%s" % (indent, rendered) + if schema and not autogen_context._has_batch: + text += ",\n%sschema=%r" % (indent, schema) + text += ")" + return text + + +class _f_name: + def __init__(self, prefix: str, name: conv) -> None: + self.prefix = prefix + self.name = name + + def __repr__(self) -> str: + return "%sf(%r)" % (self.prefix, _ident(self.name)) + + +def _ident(name: Optional[Union[quoted_name, str]]) -> Optional[str]: + """produce a __repr__() object for a string identifier that may + use quoted_name() in SQLAlchemy 0.9 and greater. + + The issue worked around here is that quoted_name() doesn't have + very good repr() behavior by itself when unicode is involved. + + """ + if name is None: + return name + elif isinstance(name, quoted_name): + return str(name) + elif isinstance(name, str): + return name + + +def _render_potential_expr( + value: Any, + autogen_context: AutogenContext, + *, + wrap_in_element: bool = True, + is_server_default: bool = False, + is_index: bool = False, +) -> str: + if isinstance(value, sql.ClauseElement): + sql_text = autogen_context.migration_context.impl.render_ddl_sql_expr( + value, is_server_default=is_server_default, is_index=is_index + ) + if wrap_in_element: + prefix = _sqlalchemy_autogenerate_prefix(autogen_context) + element = "literal_column" if is_index else "text" + value_str = f"{prefix}{element}({sql_text!r})" + if ( + is_index + and isinstance(value, Label) + and type(value.name) is str + ): + return value_str + f".label({value.name!r})" + else: + return value_str + else: + return repr(sql_text) + else: + return repr(value) + + +def _get_index_rendered_expressions( + idx: Index, autogen_context: AutogenContext +) -> List[str]: + return [ + ( + repr(_ident(getattr(exp, "name", None))) + if isinstance(exp, sa_schema.Column) + else _render_potential_expr(exp, autogen_context, is_index=True) + ) + for exp in idx.expressions + ] + + +def _uq_constraint( + constraint: UniqueConstraint, + autogen_context: AutogenContext, + alter: bool, +) -> str: + opts: List[Tuple[str, Any]] = [] + + has_batch = autogen_context._has_batch + + if constraint.deferrable: + opts.append(("deferrable", constraint.deferrable)) + if constraint.initially: + opts.append(("initially", constraint.initially)) + if not has_batch and alter and constraint.table.schema: + opts.append(("schema", _ident(constraint.table.schema))) + if not alter and constraint.name: + opts.append( + ("name", _render_gen_name(autogen_context, constraint.name)) + ) + dialect_options = _render_dialect_kwargs_items( + autogen_context, constraint.dialect_kwargs + ) + + if alter: + args = [repr(_render_gen_name(autogen_context, constraint.name))] + if not has_batch: + args += [repr(_ident(constraint.table.name))] + args.append(repr([_ident(col.name) for col in constraint.columns])) + args.extend(["%s=%r" % (k, v) for k, v in opts]) + args.extend(dialect_options) + return "%(prefix)screate_unique_constraint(%(args)s)" % { + "prefix": _alembic_autogenerate_prefix(autogen_context), + "args": ", ".join(args), + } + else: + args = [repr(_ident(col.name)) for col in constraint.columns] + args.extend(["%s=%r" % (k, v) for k, v in opts]) + args.extend(dialect_options) + return "%(prefix)sUniqueConstraint(%(args)s)" % { + "prefix": _sqlalchemy_autogenerate_prefix(autogen_context), + "args": ", ".join(args), + } + + +def _user_autogenerate_prefix(autogen_context, target): + prefix = autogen_context.opts["user_module_prefix"] + if prefix is None: + return "%s." % target.__module__ + else: + return prefix + + +def _sqlalchemy_autogenerate_prefix(autogen_context: AutogenContext) -> str: + return autogen_context.opts["sqlalchemy_module_prefix"] or "" + + +def _alembic_autogenerate_prefix(autogen_context: AutogenContext) -> str: + if autogen_context._has_batch: + return "batch_op." + else: + return autogen_context.opts["alembic_module_prefix"] or "" + + +def _user_defined_render( + type_: str, object_: Any, autogen_context: AutogenContext +) -> Union[str, Literal[False]]: + if "render_item" in autogen_context.opts: + render = autogen_context.opts["render_item"] + if render: + rendered = render(type_, object_, autogen_context) + if rendered is not False: + return rendered + return False + + +def _render_column( + column: Column[Any], autogen_context: AutogenContext +) -> str: + rendered = _user_defined_render("column", column, autogen_context) + if rendered is not False: + return rendered + + args: List[str] = [] + opts: List[Tuple[str, Any]] = [] + + if column.server_default: + rendered = _render_server_default( # type:ignore[assignment] + column.server_default, autogen_context + ) + if rendered: + if _should_render_server_default_positionally( + column.server_default + ): + args.append(rendered) + else: + opts.append(("server_default", rendered)) + + if ( + column.autoincrement is not None + and column.autoincrement != sqla_compat.AUTOINCREMENT_DEFAULT + ): + opts.append(("autoincrement", column.autoincrement)) + + if column.nullable is not None: + opts.append(("nullable", column.nullable)) + + if column.system: + opts.append(("system", column.system)) + + comment = column.comment + if comment: + opts.append(("comment", "%r" % comment)) + + # TODO: for non-ascii colname, assign a "key" + return "%(prefix)sColumn(%(name)r, %(type)s, %(args)s%(kwargs)s)" % { + "prefix": _sqlalchemy_autogenerate_prefix(autogen_context), + "name": _ident(column.name), + "type": _repr_type(column.type, autogen_context), + "args": ", ".join([str(arg) for arg in args]) + ", " if args else "", + "kwargs": ( + ", ".join( + ["%s=%s" % (kwname, val) for kwname, val in opts] + + [ + "%s=%s" + % (key, _render_potential_expr(val, autogen_context)) + for key, val in column.kwargs.items() + ] + ) + ), + } + + +def _should_render_server_default_positionally(server_default: Any) -> bool: + return sqla_compat._server_default_is_computed( + server_default + ) or sqla_compat._server_default_is_identity(server_default) + + +def _render_server_default( + default: Optional[ + Union[FetchedValue, str, TextClause, ColumnElement[Any]] + ], + autogen_context: AutogenContext, + repr_: bool = True, +) -> Optional[str]: + rendered = _user_defined_render("server_default", default, autogen_context) + if rendered is not False: + return rendered + + if sqla_compat._server_default_is_computed(default): + return _render_computed(cast("Computed", default), autogen_context) + elif sqla_compat._server_default_is_identity(default): + return _render_identity(cast("Identity", default), autogen_context) + elif isinstance(default, sa_schema.DefaultClause): + if isinstance(default.arg, str): + default = default.arg + else: + return _render_potential_expr( + default.arg, autogen_context, is_server_default=True + ) + elif isinstance(default, sa_schema.FetchedValue): + return _render_fetched_value(autogen_context) + + if isinstance(default, str) and repr_: + default = repr(re.sub(r"^'|'$", "", default)) + + return cast(str, default) + + +def _render_computed( + computed: Computed, autogen_context: AutogenContext +) -> str: + text = _render_potential_expr( + computed.sqltext, autogen_context, wrap_in_element=False + ) + + kwargs = {} + if computed.persisted is not None: + kwargs["persisted"] = computed.persisted + return "%(prefix)sComputed(%(text)s, %(kwargs)s)" % { + "prefix": _sqlalchemy_autogenerate_prefix(autogen_context), + "text": text, + "kwargs": (", ".join("%s=%s" % pair for pair in kwargs.items())), + } + + +def _render_identity( + identity: Identity, autogen_context: AutogenContext +) -> str: + kwargs = sqla_compat._get_identity_options_dict( + identity, dialect_kwargs=True + ) + + return "%(prefix)sIdentity(%(kwargs)s)" % { + "prefix": _sqlalchemy_autogenerate_prefix(autogen_context), + "kwargs": (", ".join("%s=%s" % pair for pair in kwargs.items())), + } + + +def _render_fetched_value(autogen_context: AutogenContext) -> str: + return "%(prefix)sFetchedValue()" % { + "prefix": _sqlalchemy_autogenerate_prefix(autogen_context), + } + + +def _repr_type( + type_: TypeEngine, + autogen_context: AutogenContext, + _skip_variants: bool = False, +) -> str: + rendered = _user_defined_render("type", type_, autogen_context) + if rendered is not False: + return rendered + + if hasattr(autogen_context.migration_context, "impl"): + impl_rt = autogen_context.migration_context.impl.render_type( + type_, autogen_context + ) + else: + impl_rt = None + + mod = type(type_).__module__ + imports = autogen_context.imports + + if not _skip_variants and sqla_compat._type_has_variants(type_): + return _render_Variant_type(type_, autogen_context) + elif mod.startswith("sqlalchemy.dialects"): + match = re.match(r"sqlalchemy\.dialects\.(\w+)", mod) + assert match is not None + dname = match.group(1) + if imports is not None: + imports.add("from sqlalchemy.dialects import %s" % dname) + if impl_rt: + return impl_rt + else: + return "%s.%r" % (dname, type_) + elif impl_rt: + return impl_rt + elif mod.startswith("sqlalchemy."): + if "_render_%s_type" % type_.__visit_name__ in globals(): + fn = globals()["_render_%s_type" % type_.__visit_name__] + return fn(type_, autogen_context) + else: + prefix = _sqlalchemy_autogenerate_prefix(autogen_context) + return "%s%r" % (prefix, type_) + else: + prefix = _user_autogenerate_prefix(autogen_context, type_) + return "%s%r" % (prefix, type_) + + +def _render_ARRAY_type(type_: ARRAY, autogen_context: AutogenContext) -> str: + return cast( + str, + _render_type_w_subtype( + type_, autogen_context, "item_type", r"(.+?\()" + ), + ) + + +def _render_Variant_type( + type_: TypeEngine, autogen_context: AutogenContext +) -> str: + base_type, variant_mapping = sqla_compat._get_variant_mapping(type_) + base = _repr_type(base_type, autogen_context, _skip_variants=True) + assert base is not None and base is not False # type: ignore[comparison-overlap] # noqa:E501 + for dialect in sorted(variant_mapping): + typ = variant_mapping[dialect] + base += ".with_variant(%s, %r)" % ( + _repr_type(typ, autogen_context, _skip_variants=True), + dialect, + ) + return base + + +def _render_type_w_subtype( + type_: TypeEngine, + autogen_context: AutogenContext, + attrname: str, + regexp: str, + prefix: Optional[str] = None, +) -> Union[Optional[str], Literal[False]]: + outer_repr = repr(type_) + inner_type = getattr(type_, attrname, None) + if inner_type is None: + return False + + inner_repr = repr(inner_type) + + inner_repr = re.sub(r"([\(\)])", r"\\\1", inner_repr) + sub_type = _repr_type(getattr(type_, attrname), autogen_context) + outer_type = re.sub(regexp + inner_repr, r"\1%s" % sub_type, outer_repr) + + if prefix: + return "%s%s" % (prefix, outer_type) + + mod = type(type_).__module__ + if mod.startswith("sqlalchemy.dialects"): + match = re.match(r"sqlalchemy\.dialects\.(\w+)", mod) + assert match is not None + dname = match.group(1) + return "%s.%s" % (dname, outer_type) + elif mod.startswith("sqlalchemy"): + prefix = _sqlalchemy_autogenerate_prefix(autogen_context) + return "%s%s" % (prefix, outer_type) + else: + return None + + +_constraint_renderers = util.Dispatcher() + + +def _render_constraint( + constraint: Constraint, + autogen_context: AutogenContext, + namespace_metadata: Optional[MetaData], +) -> Optional[str]: + try: + renderer = _constraint_renderers.dispatch(constraint) + except ValueError: + util.warn("No renderer is established for object %r" % constraint) + return "[Unknown Python object %r]" % constraint + else: + return renderer(constraint, autogen_context, namespace_metadata) + + +@_constraint_renderers.dispatch_for(sa_schema.PrimaryKeyConstraint) +def _render_primary_key( + constraint: PrimaryKeyConstraint, + autogen_context: AutogenContext, + namespace_metadata: Optional[MetaData], +) -> Optional[str]: + rendered = _user_defined_render("primary_key", constraint, autogen_context) + if rendered is not False: + return rendered + + if not constraint.columns: + return None + + opts = [] + if constraint.name: + opts.append( + ("name", repr(_render_gen_name(autogen_context, constraint.name))) + ) + return "%(prefix)sPrimaryKeyConstraint(%(args)s)" % { + "prefix": _sqlalchemy_autogenerate_prefix(autogen_context), + "args": ", ".join( + [repr(c.name) for c in constraint.columns] + + ["%s=%s" % (kwname, val) for kwname, val in opts] + ), + } + + +def _fk_colspec( + fk: ForeignKey, + metadata_schema: Optional[str], + namespace_metadata: Optional[MetaData], +) -> str: + """Implement a 'safe' version of ForeignKey._get_colspec() that + won't fail if the remote table can't be resolved. + + """ + colspec = fk._get_colspec() + tokens = colspec.split(".") + tname, colname = tokens[-2:] + + if metadata_schema is not None and len(tokens) == 2: + table_fullname = "%s.%s" % (metadata_schema, tname) + else: + table_fullname = ".".join(tokens[0:-1]) + + if ( + not fk.link_to_name + and fk.parent is not None + and fk.parent.table is not None + ): + # try to resolve the remote table in order to adjust for column.key. + # the FK constraint needs to be rendered in terms of the column + # name. + + if ( + namespace_metadata is not None + and table_fullname in namespace_metadata.tables + ): + col = namespace_metadata.tables[table_fullname].c.get(colname) + if col is not None: + colname = _ident(col.name) # type: ignore[assignment] + + colspec = "%s.%s" % (table_fullname, colname) + + return colspec + + +def _populate_render_fk_opts( + constraint: ForeignKeyConstraint, opts: List[Tuple[str, str]] +) -> None: + if constraint.onupdate: + opts.append(("onupdate", repr(constraint.onupdate))) + if constraint.ondelete: + opts.append(("ondelete", repr(constraint.ondelete))) + if constraint.initially: + opts.append(("initially", repr(constraint.initially))) + if constraint.deferrable: + opts.append(("deferrable", repr(constraint.deferrable))) + if constraint.use_alter: + opts.append(("use_alter", repr(constraint.use_alter))) + if constraint.match: + opts.append(("match", repr(constraint.match))) + + +@_constraint_renderers.dispatch_for(sa_schema.ForeignKeyConstraint) +def _render_foreign_key( + constraint: ForeignKeyConstraint, + autogen_context: AutogenContext, + namespace_metadata: Optional[MetaData], +) -> Optional[str]: + rendered = _user_defined_render("foreign_key", constraint, autogen_context) + if rendered is not False: + return rendered + + opts = [] + if constraint.name: + opts.append( + ("name", repr(_render_gen_name(autogen_context, constraint.name))) + ) + + _populate_render_fk_opts(constraint, opts) + + apply_metadata_schema = ( + namespace_metadata.schema if namespace_metadata is not None else None + ) + return ( + "%(prefix)sForeignKeyConstraint([%(cols)s], " + "[%(refcols)s], %(args)s)" + % { + "prefix": _sqlalchemy_autogenerate_prefix(autogen_context), + "cols": ", ".join( + repr(_ident(f.parent.name)) for f in constraint.elements + ), + "refcols": ", ".join( + repr(_fk_colspec(f, apply_metadata_schema, namespace_metadata)) + for f in constraint.elements + ), + "args": ", ".join( + ["%s=%s" % (kwname, val) for kwname, val in opts] + ), + } + ) + + +@_constraint_renderers.dispatch_for(sa_schema.UniqueConstraint) +def _render_unique_constraint( + constraint: UniqueConstraint, + autogen_context: AutogenContext, + namespace_metadata: Optional[MetaData], +) -> str: + rendered = _user_defined_render("unique", constraint, autogen_context) + if rendered is not False: + return rendered + + return _uq_constraint(constraint, autogen_context, False) + + +@_constraint_renderers.dispatch_for(sa_schema.CheckConstraint) +def _render_check_constraint( + constraint: CheckConstraint, + autogen_context: AutogenContext, + namespace_metadata: Optional[MetaData], +) -> Optional[str]: + rendered = _user_defined_render("check", constraint, autogen_context) + if rendered is not False: + return rendered + + # detect the constraint being part of + # a parent type which is probably in the Table already. + # ideally SQLAlchemy would give us more of a first class + # way to detect this. + if ( + constraint._create_rule + and hasattr(constraint._create_rule, "target") + and isinstance( + constraint._create_rule.target, + sqltypes.TypeEngine, + ) + ): + return None + opts = [] + if constraint.name: + opts.append( + ("name", repr(_render_gen_name(autogen_context, constraint.name))) + ) + return "%(prefix)sCheckConstraint(%(sqltext)s%(opts)s)" % { + "prefix": _sqlalchemy_autogenerate_prefix(autogen_context), + "opts": ( + ", " + (", ".join("%s=%s" % (k, v) for k, v in opts)) + if opts + else "" + ), + "sqltext": _render_potential_expr( + constraint.sqltext, autogen_context, wrap_in_element=False + ), + } + + +@renderers.dispatch_for(ops.ExecuteSQLOp) +def _execute_sql(autogen_context: AutogenContext, op: ops.ExecuteSQLOp) -> str: + if not isinstance(op.sqltext, str): + raise NotImplementedError( + "Autogenerate rendering of SQL Expression language constructs " + "not supported here; please use a plain SQL string" + ) + return "{prefix}execute({sqltext!r})".format( + prefix=_alembic_autogenerate_prefix(autogen_context), + sqltext=op.sqltext, + ) + + +renderers = default_renderers.branch() diff --git a/venv/lib/python3.12/site-packages/alembic/autogenerate/rewriter.py b/venv/lib/python3.12/site-packages/alembic/autogenerate/rewriter.py new file mode 100644 index 00000000..1d44b5c3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/autogenerate/rewriter.py @@ -0,0 +1,240 @@ +from __future__ import annotations + +from typing import Any +from typing import Callable +from typing import Iterator +from typing import List +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +from .. import util +from ..operations import ops + +if TYPE_CHECKING: + from ..operations.ops import AddColumnOp + from ..operations.ops import AlterColumnOp + from ..operations.ops import CreateTableOp + from ..operations.ops import DowngradeOps + from ..operations.ops import MigrateOperation + from ..operations.ops import MigrationScript + from ..operations.ops import ModifyTableOps + from ..operations.ops import OpContainer + from ..operations.ops import UpgradeOps + from ..runtime.migration import MigrationContext + from ..script.revision import _GetRevArg + +ProcessRevisionDirectiveFn = Callable[ + ["MigrationContext", "_GetRevArg", List["MigrationScript"]], None +] + + +class Rewriter: + """A helper object that allows easy 'rewriting' of ops streams. + + The :class:`.Rewriter` object is intended to be passed along + to the + :paramref:`.EnvironmentContext.configure.process_revision_directives` + parameter in an ``env.py`` script. Once constructed, any number + of "rewrites" functions can be associated with it, which will be given + the opportunity to modify the structure without having to have explicit + knowledge of the overall structure. + + The function is passed the :class:`.MigrationContext` object and + ``revision`` tuple that are passed to the :paramref:`.Environment + Context.configure.process_revision_directives` function normally, + and the third argument is an individual directive of the type + noted in the decorator. The function has the choice of returning + a single op directive, which normally can be the directive that + was actually passed, or a new directive to replace it, or a list + of zero or more directives to replace it. + + .. seealso:: + + :ref:`autogen_rewriter` - usage example + + """ + + _traverse = util.Dispatcher() + + _chained: Tuple[Union[ProcessRevisionDirectiveFn, Rewriter], ...] = () + + def __init__(self) -> None: + self.dispatch = util.Dispatcher() + + def chain( + self, + other: Union[ + ProcessRevisionDirectiveFn, + Rewriter, + ], + ) -> Rewriter: + """Produce a "chain" of this :class:`.Rewriter` to another. + + This allows two or more rewriters to operate serially on a stream, + e.g.:: + + writer1 = autogenerate.Rewriter() + writer2 = autogenerate.Rewriter() + + + @writer1.rewrites(ops.AddColumnOp) + def add_column_nullable(context, revision, op): + op.column.nullable = True + return op + + + @writer2.rewrites(ops.AddColumnOp) + def add_column_idx(context, revision, op): + idx_op = ops.CreateIndexOp( + "ixc", op.table_name, [op.column.name] + ) + return [op, idx_op] + + writer = writer1.chain(writer2) + + :param other: a :class:`.Rewriter` instance + :return: a new :class:`.Rewriter` that will run the operations + of this writer, then the "other" writer, in succession. + + """ + wr = self.__class__.__new__(self.__class__) + wr.__dict__.update(self.__dict__) + wr._chained += (other,) + return wr + + def rewrites( + self, + operator: Union[ + Type[AddColumnOp], + Type[MigrateOperation], + Type[AlterColumnOp], + Type[CreateTableOp], + Type[ModifyTableOps], + ], + ) -> Callable[..., Any]: + """Register a function as rewriter for a given type. + + The function should receive three arguments, which are + the :class:`.MigrationContext`, a ``revision`` tuple, and + an op directive of the type indicated. E.g.:: + + @writer1.rewrites(ops.AddColumnOp) + def add_column_nullable(context, revision, op): + op.column.nullable = True + return op + + """ + return self.dispatch.dispatch_for(operator) + + def _rewrite( + self, + context: MigrationContext, + revision: _GetRevArg, + directive: MigrateOperation, + ) -> Iterator[MigrateOperation]: + try: + _rewriter = self.dispatch.dispatch(directive) + except ValueError: + _rewriter = None + yield directive + else: + if self in directive._mutations: + yield directive + else: + for r_directive in util.to_list( + _rewriter(context, revision, directive), [] + ): + r_directive._mutations = r_directive._mutations.union( + [self] + ) + yield r_directive + + def __call__( + self, + context: MigrationContext, + revision: _GetRevArg, + directives: List[MigrationScript], + ) -> None: + self.process_revision_directives(context, revision, directives) + for process_revision_directives in self._chained: + process_revision_directives(context, revision, directives) + + @_traverse.dispatch_for(ops.MigrationScript) + def _traverse_script( + self, + context: MigrationContext, + revision: _GetRevArg, + directive: MigrationScript, + ) -> None: + upgrade_ops_list: List[UpgradeOps] = [] + for upgrade_ops in directive.upgrade_ops_list: + ret = self._traverse_for(context, revision, upgrade_ops) + if len(ret) != 1: + raise ValueError( + "Can only return single object for UpgradeOps traverse" + ) + upgrade_ops_list.append(ret[0]) + + directive.upgrade_ops = upgrade_ops_list + + downgrade_ops_list: List[DowngradeOps] = [] + for downgrade_ops in directive.downgrade_ops_list: + ret = self._traverse_for(context, revision, downgrade_ops) + if len(ret) != 1: + raise ValueError( + "Can only return single object for DowngradeOps traverse" + ) + downgrade_ops_list.append(ret[0]) + directive.downgrade_ops = downgrade_ops_list + + @_traverse.dispatch_for(ops.OpContainer) + def _traverse_op_container( + self, + context: MigrationContext, + revision: _GetRevArg, + directive: OpContainer, + ) -> None: + self._traverse_list(context, revision, directive.ops) + + @_traverse.dispatch_for(ops.MigrateOperation) + def _traverse_any_directive( + self, + context: MigrationContext, + revision: _GetRevArg, + directive: MigrateOperation, + ) -> None: + pass + + def _traverse_for( + self, + context: MigrationContext, + revision: _GetRevArg, + directive: MigrateOperation, + ) -> Any: + directives = list(self._rewrite(context, revision, directive)) + for directive in directives: + traverser = self._traverse.dispatch(directive) + traverser(self, context, revision, directive) + return directives + + def _traverse_list( + self, + context: MigrationContext, + revision: _GetRevArg, + directives: Any, + ) -> None: + dest = [] + for directive in directives: + dest.extend(self._traverse_for(context, revision, directive)) + + directives[:] = dest + + def process_revision_directives( + self, + context: MigrationContext, + revision: _GetRevArg, + directives: List[MigrationScript], + ) -> None: + self._traverse_list(context, revision, directives) diff --git a/venv/lib/python3.12/site-packages/alembic/command.py b/venv/lib/python3.12/site-packages/alembic/command.py new file mode 100644 index 00000000..4897c0d9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/command.py @@ -0,0 +1,848 @@ +# mypy: allow-untyped-defs, allow-untyped-calls + +from __future__ import annotations + +import os +import pathlib +from typing import List +from typing import Optional +from typing import TYPE_CHECKING +from typing import Union + +from . import autogenerate as autogen +from . import util +from .runtime.environment import EnvironmentContext +from .script import ScriptDirectory +from .util import compat + +if TYPE_CHECKING: + from alembic.config import Config + from alembic.script.base import Script + from alembic.script.revision import _RevIdType + from .runtime.environment import ProcessRevisionDirectiveFn + + +def list_templates(config: Config) -> None: + """List available templates. + + :param config: a :class:`.Config` object. + + """ + + config.print_stdout("Available templates:\n") + for tempname in config._get_template_path().iterdir(): + with (tempname / "README").open() as readme: + synopsis = next(readme).rstrip() + config.print_stdout("%s - %s", tempname.name, synopsis) + + config.print_stdout("\nTemplates are used via the 'init' command, e.g.:") + config.print_stdout("\n alembic init --template generic ./scripts") + + +def init( + config: Config, + directory: str, + template: str = "generic", + package: bool = False, +) -> None: + """Initialize a new scripts directory. + + :param config: a :class:`.Config` object. + + :param directory: string path of the target directory. + + :param template: string name of the migration environment template to + use. + + :param package: when True, write ``__init__.py`` files into the + environment location as well as the versions/ location. + + """ + + directory_path = pathlib.Path(directory) + if directory_path.exists() and list(directory_path.iterdir()): + raise util.CommandError( + "Directory %s already exists and is not empty" % directory_path + ) + + template_path = config._get_template_path() / template + + if not template_path.exists(): + raise util.CommandError(f"No such template {template_path}") + + # left as os.access() to suit unit test mocking + if not os.access(directory_path, os.F_OK): + with util.status( + f"Creating directory {directory_path.absolute()}", + **config.messaging_opts, + ): + os.makedirs(directory_path) + + versions = directory_path / "versions" + with util.status( + f"Creating directory {versions.absolute()}", + **config.messaging_opts, + ): + os.makedirs(versions) + + if not directory_path.is_absolute(): + # for non-absolute path, state config file in .ini / pyproject + # as relative to the %(here)s token, which is where the config + # file itself would be + + if config._config_file_path is not None: + rel_dir = compat.path_relative_to( + directory_path.absolute(), + config._config_file_path.absolute().parent, + walk_up=True, + ) + ini_script_location_directory = ("%(here)s" / rel_dir).as_posix() + if config._toml_file_path is not None: + rel_dir = compat.path_relative_to( + directory_path.absolute(), + config._toml_file_path.absolute().parent, + walk_up=True, + ) + toml_script_location_directory = ("%(here)s" / rel_dir).as_posix() + + else: + ini_script_location_directory = directory_path.as_posix() + toml_script_location_directory = directory_path.as_posix() + + script = ScriptDirectory(directory_path) + + has_toml = False + + config_file: pathlib.Path | None = None + + for file_path in template_path.iterdir(): + file_ = file_path.name + if file_ == "alembic.ini.mako": + assert config.config_file_name is not None + config_file = pathlib.Path(config.config_file_name).absolute() + if config_file.exists(): + util.msg( + f"File {config_file} already exists, skipping", + **config.messaging_opts, + ) + else: + script._generate_template( + file_path, + config_file, + script_location=ini_script_location_directory, + ) + elif file_ == "pyproject.toml.mako": + has_toml = True + assert config._toml_file_path is not None + toml_path = config._toml_file_path.absolute() + + if toml_path.exists(): + # left as open() to suit unit test mocking + with open(toml_path, "rb") as f: + toml_data = compat.tomllib.load(f) + if "tool" in toml_data and "alembic" in toml_data["tool"]: + + util.msg( + f"File {toml_path} already exists " + "and already has a [tool.alembic] section, " + "skipping", + ) + continue + script._append_template( + file_path, + toml_path, + script_location=toml_script_location_directory, + ) + else: + script._generate_template( + file_path, + toml_path, + script_location=toml_script_location_directory, + ) + + elif file_path.is_file(): + output_file = directory_path / file_ + script._copy_file(file_path, output_file) + + if package: + for path in [ + directory_path.absolute() / "__init__.py", + versions.absolute() / "__init__.py", + ]: + with util.status(f"Adding {path!s}", **config.messaging_opts): + # left as open() to suit unit test mocking + with open(path, "w"): + pass + + assert config_file is not None + + if has_toml: + util.msg( + f"Please edit configuration settings in {toml_path} and " + "configuration/connection/logging " + f"settings in {config_file} before proceeding.", + **config.messaging_opts, + ) + else: + util.msg( + "Please edit configuration/connection/logging " + f"settings in {config_file} before proceeding.", + **config.messaging_opts, + ) + + +def revision( + config: Config, + message: Optional[str] = None, + autogenerate: bool = False, + sql: bool = False, + head: str = "head", + splice: bool = False, + branch_label: Optional[_RevIdType] = None, + version_path: Union[str, os.PathLike[str], None] = None, + rev_id: Optional[str] = None, + depends_on: Optional[str] = None, + process_revision_directives: Optional[ProcessRevisionDirectiveFn] = None, +) -> Union[Optional[Script], List[Optional[Script]]]: + """Create a new revision file. + + :param config: a :class:`.Config` object. + + :param message: string message to apply to the revision; this is the + ``-m`` option to ``alembic revision``. + + :param autogenerate: whether or not to autogenerate the script from + the database; this is the ``--autogenerate`` option to + ``alembic revision``. + + :param sql: whether to dump the script out as a SQL string; when specified, + the script is dumped to stdout. This is the ``--sql`` option to + ``alembic revision``. + + :param head: head revision to build the new revision upon as a parent; + this is the ``--head`` option to ``alembic revision``. + + :param splice: whether or not the new revision should be made into a + new head of its own; is required when the given ``head`` is not itself + a head. This is the ``--splice`` option to ``alembic revision``. + + :param branch_label: string label to apply to the branch; this is the + ``--branch-label`` option to ``alembic revision``. + + :param version_path: string symbol identifying a specific version path + from the configuration; this is the ``--version-path`` option to + ``alembic revision``. + + :param rev_id: optional revision identifier to use instead of having + one generated; this is the ``--rev-id`` option to ``alembic revision``. + + :param depends_on: optional list of "depends on" identifiers; this is the + ``--depends-on`` option to ``alembic revision``. + + :param process_revision_directives: this is a callable that takes the + same form as the callable described at + :paramref:`.EnvironmentContext.configure.process_revision_directives`; + will be applied to the structure generated by the revision process + where it can be altered programmatically. Note that unlike all + the other parameters, this option is only available via programmatic + use of :func:`.command.revision`. + + """ + + script_directory = ScriptDirectory.from_config(config) + + command_args = dict( + message=message, + autogenerate=autogenerate, + sql=sql, + head=head, + splice=splice, + branch_label=branch_label, + version_path=version_path, + rev_id=rev_id, + depends_on=depends_on, + ) + revision_context = autogen.RevisionContext( + config, + script_directory, + command_args, + process_revision_directives=process_revision_directives, + ) + + environment = util.asbool( + config.get_alembic_option("revision_environment") + ) + + if autogenerate: + environment = True + + if sql: + raise util.CommandError( + "Using --sql with --autogenerate does not make any sense" + ) + + def retrieve_migrations(rev, context): + revision_context.run_autogenerate(rev, context) + return [] + + elif environment: + + def retrieve_migrations(rev, context): + revision_context.run_no_autogenerate(rev, context) + return [] + + elif sql: + raise util.CommandError( + "Using --sql with the revision command when " + "revision_environment is not configured does not make any sense" + ) + + if environment: + with EnvironmentContext( + config, + script_directory, + fn=retrieve_migrations, + as_sql=sql, + template_args=revision_context.template_args, + revision_context=revision_context, + ): + script_directory.run_env() + + # the revision_context now has MigrationScript structure(s) present. + # these could theoretically be further processed / rewritten *here*, + # in addition to the hooks present within each run_migrations() call, + # or at the end of env.py run_migrations_online(). + + scripts = [script for script in revision_context.generate_scripts()] + if len(scripts) == 1: + return scripts[0] + else: + return scripts + + +def check(config: "Config") -> None: + """Check if revision command with autogenerate has pending upgrade ops. + + :param config: a :class:`.Config` object. + + .. versionadded:: 1.9.0 + + """ + + script_directory = ScriptDirectory.from_config(config) + + command_args = dict( + message=None, + autogenerate=True, + sql=False, + head="head", + splice=False, + branch_label=None, + version_path=None, + rev_id=None, + depends_on=None, + ) + revision_context = autogen.RevisionContext( + config, + script_directory, + command_args, + ) + + def retrieve_migrations(rev, context): + revision_context.run_autogenerate(rev, context) + return [] + + with EnvironmentContext( + config, + script_directory, + fn=retrieve_migrations, + as_sql=False, + template_args=revision_context.template_args, + revision_context=revision_context, + ): + script_directory.run_env() + + # the revision_context now has MigrationScript structure(s) present. + + migration_script = revision_context.generated_revisions[-1] + diffs = [] + for upgrade_ops in migration_script.upgrade_ops_list: + diffs.extend(upgrade_ops.as_diffs()) + + if diffs: + raise util.AutogenerateDiffsDetected( + f"New upgrade operations detected: {diffs}", + revision_context=revision_context, + diffs=diffs, + ) + else: + config.print_stdout("No new upgrade operations detected.") + + +def merge( + config: Config, + revisions: _RevIdType, + message: Optional[str] = None, + branch_label: Optional[_RevIdType] = None, + rev_id: Optional[str] = None, +) -> Optional[Script]: + """Merge two revisions together. Creates a new migration file. + + :param config: a :class:`.Config` instance + + :param revisions: The revisions to merge. + + :param message: string message to apply to the revision. + + :param branch_label: string label name to apply to the new revision. + + :param rev_id: hardcoded revision identifier instead of generating a new + one. + + .. seealso:: + + :ref:`branches` + + """ + + script = ScriptDirectory.from_config(config) + template_args = { + "config": config # Let templates use config for + # e.g. multiple databases + } + + environment = util.asbool( + config.get_alembic_option("revision_environment") + ) + + if environment: + + def nothing(rev, context): + return [] + + with EnvironmentContext( + config, + script, + fn=nothing, + as_sql=False, + template_args=template_args, + ): + script.run_env() + + return script.generate_revision( + rev_id or util.rev_id(), + message, + refresh=True, + head=revisions, + branch_labels=branch_label, + **template_args, # type:ignore[arg-type] + ) + + +def upgrade( + config: Config, + revision: str, + sql: bool = False, + tag: Optional[str] = None, +) -> None: + """Upgrade to a later version. + + :param config: a :class:`.Config` instance. + + :param revision: string revision target or range for --sql mode. May be + ``"heads"`` to target the most recent revision(s). + + :param sql: if True, use ``--sql`` mode. + + :param tag: an arbitrary "tag" that can be intercepted by custom + ``env.py`` scripts via the :meth:`.EnvironmentContext.get_tag_argument` + method. + + """ + + script = ScriptDirectory.from_config(config) + + starting_rev = None + if ":" in revision: + if not sql: + raise util.CommandError("Range revision not allowed") + starting_rev, revision = revision.split(":", 2) + + def upgrade(rev, context): + return script._upgrade_revs(revision, rev) + + with EnvironmentContext( + config, + script, + fn=upgrade, + as_sql=sql, + starting_rev=starting_rev, + destination_rev=revision, + tag=tag, + ): + script.run_env() + + +def downgrade( + config: Config, + revision: str, + sql: bool = False, + tag: Optional[str] = None, +) -> None: + """Revert to a previous version. + + :param config: a :class:`.Config` instance. + + :param revision: string revision target or range for --sql mode. May + be ``"base"`` to target the first revision. + + :param sql: if True, use ``--sql`` mode. + + :param tag: an arbitrary "tag" that can be intercepted by custom + ``env.py`` scripts via the :meth:`.EnvironmentContext.get_tag_argument` + method. + + """ + + script = ScriptDirectory.from_config(config) + starting_rev = None + if ":" in revision: + if not sql: + raise util.CommandError("Range revision not allowed") + starting_rev, revision = revision.split(":", 2) + elif sql: + raise util.CommandError( + "downgrade with --sql requires :" + ) + + def downgrade(rev, context): + return script._downgrade_revs(revision, rev) + + with EnvironmentContext( + config, + script, + fn=downgrade, + as_sql=sql, + starting_rev=starting_rev, + destination_rev=revision, + tag=tag, + ): + script.run_env() + + +def show(config: Config, rev: str) -> None: + """Show the revision(s) denoted by the given symbol. + + :param config: a :class:`.Config` instance. + + :param rev: string revision target. May be ``"current"`` to show the + revision(s) currently applied in the database. + + """ + + script = ScriptDirectory.from_config(config) + + if rev == "current": + + def show_current(rev, context): + for sc in script.get_revisions(rev): + config.print_stdout(sc.log_entry) + return [] + + with EnvironmentContext(config, script, fn=show_current): + script.run_env() + else: + for sc in script.get_revisions(rev): + config.print_stdout(sc.log_entry) + + +def history( + config: Config, + rev_range: Optional[str] = None, + verbose: bool = False, + indicate_current: bool = False, +) -> None: + """List changeset scripts in chronological order. + + :param config: a :class:`.Config` instance. + + :param rev_range: string revision range. + + :param verbose: output in verbose mode. + + :param indicate_current: indicate current revision. + + """ + base: Optional[str] + head: Optional[str] + script = ScriptDirectory.from_config(config) + if rev_range is not None: + if ":" not in rev_range: + raise util.CommandError( + "History range requires [start]:[end], " "[start]:, or :[end]" + ) + base, head = rev_range.strip().split(":") + else: + base = head = None + + environment = ( + util.asbool(config.get_alembic_option("revision_environment")) + or indicate_current + ) + + def _display_history(config, script, base, head, currents=()): + for sc in script.walk_revisions( + base=base or "base", head=head or "heads" + ): + if indicate_current: + sc._db_current_indicator = sc.revision in currents + + config.print_stdout( + sc.cmd_format( + verbose=verbose, + include_branches=True, + include_doc=True, + include_parents=True, + ) + ) + + def _display_history_w_current(config, script, base, head): + def _display_current_history(rev, context): + if head == "current": + _display_history(config, script, base, rev, rev) + elif base == "current": + _display_history(config, script, rev, head, rev) + else: + _display_history(config, script, base, head, rev) + return [] + + with EnvironmentContext(config, script, fn=_display_current_history): + script.run_env() + + if base == "current" or head == "current" or environment: + _display_history_w_current(config, script, base, head) + else: + _display_history(config, script, base, head) + + +def heads( + config: Config, verbose: bool = False, resolve_dependencies: bool = False +) -> None: + """Show current available heads in the script directory. + + :param config: a :class:`.Config` instance. + + :param verbose: output in verbose mode. + + :param resolve_dependencies: treat dependency version as down revisions. + + """ + + script = ScriptDirectory.from_config(config) + if resolve_dependencies: + heads = script.get_revisions("heads") + else: + heads = script.get_revisions(script.get_heads()) + + for rev in heads: + config.print_stdout( + rev.cmd_format( + verbose, include_branches=True, tree_indicators=False + ) + ) + + +def branches(config: Config, verbose: bool = False) -> None: + """Show current branch points. + + :param config: a :class:`.Config` instance. + + :param verbose: output in verbose mode. + + """ + script = ScriptDirectory.from_config(config) + for sc in script.walk_revisions(): + if sc.is_branch_point: + config.print_stdout( + "%s\n%s\n", + sc.cmd_format(verbose, include_branches=True), + "\n".join( + "%s -> %s" + % ( + " " * len(str(sc.revision)), + rev_obj.cmd_format( + False, include_branches=True, include_doc=verbose + ), + ) + for rev_obj in ( + script.get_revision(rev) for rev in sc.nextrev + ) + ), + ) + + +def current( + config: Config, check_heads: bool = False, verbose: bool = False +) -> None: + """Display the current revision for a database. + + :param config: a :class:`.Config` instance. + + :param check_heads: Check if all head revisions are applied to the + database. Raises :class:`.DatabaseNotAtHead` if this is not the case. + + .. versionadded:: 1.17.1 + + :param verbose: output in verbose mode. + + """ + + script = ScriptDirectory.from_config(config) + + def display_version(rev, context): + if verbose: + config.print_stdout( + "Current revision(s) for %s:", + util.obfuscate_url_pw(context.connection.engine.url), + ) + if check_heads and ( + set(context.get_current_heads()) != set(script.get_heads()) + ): + raise util.DatabaseNotAtHead( + "Database is not on all head revisions" + ) + for rev in script.get_all_current(rev): + config.print_stdout(rev.cmd_format(verbose)) + + return [] + + with EnvironmentContext( + config, script, fn=display_version, dont_mutate=True + ): + script.run_env() + + +def stamp( + config: Config, + revision: _RevIdType, + sql: bool = False, + tag: Optional[str] = None, + purge: bool = False, +) -> None: + """'stamp' the revision table with the given revision; don't + run any migrations. + + :param config: a :class:`.Config` instance. + + :param revision: target revision or list of revisions. May be a list + to indicate stamping of multiple branch heads; may be ``"base"`` + to remove all revisions from the table or ``"heads"`` to stamp the + most recent revision(s). + + .. note:: this parameter is called "revisions" in the command line + interface. + + :param sql: use ``--sql`` mode + + :param tag: an arbitrary "tag" that can be intercepted by custom + ``env.py`` scripts via the :class:`.EnvironmentContext.get_tag_argument` + method. + + :param purge: delete all entries in the version table before stamping. + + """ + + script = ScriptDirectory.from_config(config) + + if sql: + destination_revs = [] + starting_rev = None + for _revision in util.to_list(revision): + if ":" in _revision: + srev, _revision = _revision.split(":", 2) + + if starting_rev != srev: + if starting_rev is None: + starting_rev = srev + else: + raise util.CommandError( + "Stamp operation with --sql only supports a " + "single starting revision at a time" + ) + destination_revs.append(_revision) + else: + destination_revs = util.to_list(revision) + + def do_stamp(rev, context): + return script._stamp_revs(util.to_tuple(destination_revs), rev) + + with EnvironmentContext( + config, + script, + fn=do_stamp, + as_sql=sql, + starting_rev=starting_rev if sql else None, + destination_rev=util.to_tuple(destination_revs), + tag=tag, + purge=purge, + ): + script.run_env() + + +def edit(config: Config, rev: str) -> None: + """Edit revision script(s) using $EDITOR. + + :param config: a :class:`.Config` instance. + + :param rev: target revision. + + """ + + script = ScriptDirectory.from_config(config) + + if rev == "current": + + def edit_current(rev, context): + if not rev: + raise util.CommandError("No current revisions") + for sc in script.get_revisions(rev): + util.open_in_editor(sc.path) + return [] + + with EnvironmentContext(config, script, fn=edit_current): + script.run_env() + else: + revs = script.get_revisions(rev) + if not revs: + raise util.CommandError( + "No revision files indicated by symbol '%s'" % rev + ) + for sc in revs: + assert sc + util.open_in_editor(sc.path) + + +def ensure_version(config: Config, sql: bool = False) -> None: + """Create the alembic version table if it doesn't exist already . + + :param config: a :class:`.Config` instance. + + :param sql: use ``--sql`` mode. + + .. versionadded:: 1.7.6 + + """ + + script = ScriptDirectory.from_config(config) + + def do_ensure_version(rev, context): + context._ensure_version_table() + return [] + + with EnvironmentContext( + config, + script, + fn=do_ensure_version, + as_sql=sql, + ): + script.run_env() diff --git a/venv/lib/python3.12/site-packages/alembic/config.py b/venv/lib/python3.12/site-packages/alembic/config.py new file mode 100644 index 00000000..121a4459 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/config.py @@ -0,0 +1,1051 @@ +from __future__ import annotations + +from argparse import ArgumentParser +from argparse import Namespace +from configparser import ConfigParser +import inspect +import logging +import os +from pathlib import Path +import re +import sys +from typing import Any +from typing import cast +from typing import Dict +from typing import Mapping +from typing import Optional +from typing import overload +from typing import Protocol +from typing import Sequence +from typing import TextIO +from typing import Union + +from typing_extensions import TypedDict + +from . import __version__ +from . import command +from . import util +from .util import compat +from .util.pyfiles import _preserving_path_as_str + + +log = logging.getLogger(__name__) + + +class Config: + r"""Represent an Alembic configuration. + + Within an ``env.py`` script, this is available + via the :attr:`.EnvironmentContext.config` attribute, + which in turn is available at ``alembic.context``:: + + from alembic import context + + some_param = context.config.get_main_option("my option") + + When invoking Alembic programmatically, a new + :class:`.Config` can be created by passing + the name of an .ini file to the constructor:: + + from alembic.config import Config + alembic_cfg = Config("/path/to/yourapp/alembic.ini") + + With a :class:`.Config` object, you can then + run Alembic commands programmatically using the directives + in :mod:`alembic.command`. + + The :class:`.Config` object can also be constructed without + a filename. Values can be set programmatically, and + new sections will be created as needed:: + + from alembic.config import Config + alembic_cfg = Config() + alembic_cfg.set_main_option("script_location", "myapp:migrations") + alembic_cfg.set_main_option("sqlalchemy.url", "postgresql://foo/bar") + alembic_cfg.set_section_option("mysection", "foo", "bar") + + .. warning:: + + When using programmatic configuration, make sure the + ``env.py`` file in use is compatible with the target configuration; + including that the call to Python ``logging.fileConfig()`` is + omitted if the programmatic configuration doesn't actually include + logging directives. + + For passing non-string values to environments, such as connections and + engines, use the :attr:`.Config.attributes` dictionary:: + + with engine.begin() as connection: + alembic_cfg.attributes['connection'] = connection + command.upgrade(alembic_cfg, "head") + + :param file\_: name of the .ini file to open if an ``alembic.ini`` is + to be used. This should refer to the ``alembic.ini`` file, either as + a filename or a full path to the file. This filename if passed must refer + to an **ini file in ConfigParser format** only. + + :param toml\_file: name of the pyproject.toml file to open if a + ``pyproject.toml`` file is to be used. This should refer to the + ``pyproject.toml`` file, either as a filename or a full path to the file. + This file must be in toml format. Both :paramref:`.Config.file\_` and + :paramref:`.Config.toml\_file` may be passed simultaneously, or + exclusively. + + .. versionadded:: 1.16.0 + + :param ini_section: name of the main Alembic section within the + .ini file + :param output_buffer: optional file-like input buffer which + will be passed to the :class:`.MigrationContext` - used to redirect + the output of "offline generation" when using Alembic programmatically. + :param stdout: buffer where the "print" output of commands will be sent. + Defaults to ``sys.stdout``. + + :param config_args: A dictionary of keys and values that will be used + for substitution in the alembic config file, as well as the pyproject.toml + file, depending on which / both are used. The dictionary as given is + **copied** to two new, independent dictionaries, stored locally under the + attributes ``.config_args`` and ``.toml_args``. Both of these + dictionaries will also be populated with the replacement variable + ``%(here)s``, which refers to the location of the .ini and/or .toml file + as appropriate. + + :param attributes: optional dictionary of arbitrary Python keys/values, + which will be populated into the :attr:`.Config.attributes` dictionary. + + .. seealso:: + + :ref:`connection_sharing` + + """ + + def __init__( + self, + file_: Union[str, os.PathLike[str], None] = None, + toml_file: Union[str, os.PathLike[str], None] = None, + ini_section: str = "alembic", + output_buffer: Optional[TextIO] = None, + stdout: TextIO = sys.stdout, + cmd_opts: Optional[Namespace] = None, + config_args: Mapping[str, Any] = util.immutabledict(), + attributes: Optional[Dict[str, Any]] = None, + ) -> None: + """Construct a new :class:`.Config`""" + self.config_file_name = ( + _preserving_path_as_str(file_) if file_ else None + ) + self.toml_file_name = ( + _preserving_path_as_str(toml_file) if toml_file else None + ) + self.config_ini_section = ini_section + self.output_buffer = output_buffer + self.stdout = stdout + self.cmd_opts = cmd_opts + self.config_args = dict(config_args) + self.toml_args = dict(config_args) + if attributes: + self.attributes.update(attributes) + + cmd_opts: Optional[Namespace] = None + """The command-line options passed to the ``alembic`` script. + + Within an ``env.py`` script this can be accessed via the + :attr:`.EnvironmentContext.config` attribute. + + .. seealso:: + + :meth:`.EnvironmentContext.get_x_argument` + + """ + + config_file_name: Optional[str] = None + """Filesystem path to the .ini file in use.""" + + toml_file_name: Optional[str] = None + """Filesystem path to the pyproject.toml file in use. + + .. versionadded:: 1.16.0 + + """ + + @property + def _config_file_path(self) -> Optional[Path]: + if self.config_file_name is None: + return None + return Path(self.config_file_name) + + @property + def _toml_file_path(self) -> Optional[Path]: + if self.toml_file_name is None: + return None + return Path(self.toml_file_name) + + config_ini_section: str = None # type:ignore[assignment] + """Name of the config file section to read basic configuration + from. Defaults to ``alembic``, that is the ``[alembic]`` section + of the .ini file. This value is modified using the ``-n/--name`` + option to the Alembic runner. + + """ + + @util.memoized_property + def attributes(self) -> Dict[str, Any]: + """A Python dictionary for storage of additional state. + + + This is a utility dictionary which can include not just strings but + engines, connections, schema objects, or anything else. + Use this to pass objects into an env.py script, such as passing + a :class:`sqlalchemy.engine.base.Connection` when calling + commands from :mod:`alembic.command` programmatically. + + .. seealso:: + + :ref:`connection_sharing` + + :paramref:`.Config.attributes` + + """ + return {} + + def print_stdout(self, text: str, *arg: Any) -> None: + """Render a message to standard out. + + When :meth:`.Config.print_stdout` is called with additional args + those arguments will formatted against the provided text, + otherwise we simply output the provided text verbatim. + + This is a no-op when the``quiet`` messaging option is enabled. + + e.g.:: + + >>> config.print_stdout('Some text %s', 'arg') + Some Text arg + + """ + + if arg: + output = str(text) % arg + else: + output = str(text) + + util.write_outstream(self.stdout, output, "\n", **self.messaging_opts) + + @util.memoized_property + def file_config(self) -> ConfigParser: + """Return the underlying ``ConfigParser`` object. + + Dir*-ect access to the .ini file is available here, + though the :meth:`.Config.get_section` and + :meth:`.Config.get_main_option` + methods provide a possibly simpler interface. + + """ + + if self._config_file_path: + here = self._config_file_path.absolute().parent + else: + here = Path() + self.config_args["here"] = here.as_posix() + file_config = ConfigParser(self.config_args) + + verbose = getattr(self.cmd_opts, "verbose", False) + if self._config_file_path: + compat.read_config_parser(file_config, [self._config_file_path]) + if verbose: + log.info( + "Loading config from file: %s", self._config_file_path + ) + else: + file_config.add_section(self.config_ini_section) + if verbose: + log.info( + "No config file provided; using in-memory default config" + ) + return file_config + + @util.memoized_property + def toml_alembic_config(self) -> Mapping[str, Any]: + """Return a dictionary of the [tool.alembic] section from + pyproject.toml""" + + if self._toml_file_path and self._toml_file_path.exists(): + + here = self._toml_file_path.absolute().parent + self.toml_args["here"] = here.as_posix() + + with open(self._toml_file_path, "rb") as f: + toml_data = compat.tomllib.load(f) + data = toml_data.get("tool", {}).get("alembic", {}) + if not isinstance(data, dict): + raise util.CommandError("Incorrect TOML format") + return data + + else: + return {} + + def get_template_directory(self) -> str: + """Return the directory where Alembic setup templates are found. + + This method is used by the alembic ``init`` and ``list_templates`` + commands. + + """ + import alembic + + package_dir = Path(alembic.__file__).absolute().parent + return str(package_dir / "templates") + + def _get_template_path(self) -> Path: + """Return the directory where Alembic setup templates are found. + + This method is used by the alembic ``init`` and ``list_templates`` + commands. + + .. versionadded:: 1.16.0 + + """ + return Path(self.get_template_directory()) + + @overload + def get_section( + self, name: str, default: None = ... + ) -> Optional[Dict[str, str]]: ... + + # "default" here could also be a TypeVar + # _MT = TypeVar("_MT", bound=Mapping[str, str]), + # however mypy wasn't handling that correctly (pyright was) + @overload + def get_section( + self, name: str, default: Dict[str, str] + ) -> Dict[str, str]: ... + + @overload + def get_section( + self, name: str, default: Mapping[str, str] + ) -> Union[Dict[str, str], Mapping[str, str]]: ... + + def get_section( + self, name: str, default: Optional[Mapping[str, str]] = None + ) -> Optional[Mapping[str, str]]: + """Return all the configuration options from a given .ini file section + as a dictionary. + + If the given section does not exist, the value of ``default`` + is returned, which is expected to be a dictionary or other mapping. + + """ + if not self.file_config.has_section(name): + return default + + return dict(self.file_config.items(name)) + + def set_main_option(self, name: str, value: str) -> None: + """Set an option programmatically within the 'main' section. + + This overrides whatever was in the .ini file. + + :param name: name of the value + + :param value: the value. Note that this value is passed to + ``ConfigParser.set``, which supports variable interpolation using + pyformat (e.g. ``%(some_value)s``). A raw percent sign not part of + an interpolation symbol must therefore be escaped, e.g. ``%%``. + The given value may refer to another value already in the file + using the interpolation format. + + """ + self.set_section_option(self.config_ini_section, name, value) + + def remove_main_option(self, name: str) -> None: + self.file_config.remove_option(self.config_ini_section, name) + + def set_section_option(self, section: str, name: str, value: str) -> None: + """Set an option programmatically within the given section. + + The section is created if it doesn't exist already. + The value here will override whatever was in the .ini + file. + + Does **NOT** consume from the pyproject.toml file. + + .. seealso:: + + :meth:`.Config.get_alembic_option` - includes pyproject support + + :param section: name of the section + + :param name: name of the value + + :param value: the value. Note that this value is passed to + ``ConfigParser.set``, which supports variable interpolation using + pyformat (e.g. ``%(some_value)s``). A raw percent sign not part of + an interpolation symbol must therefore be escaped, e.g. ``%%``. + The given value may refer to another value already in the file + using the interpolation format. + + """ + + if not self.file_config.has_section(section): + self.file_config.add_section(section) + self.file_config.set(section, name, value) + + def get_section_option( + self, section: str, name: str, default: Optional[str] = None + ) -> Optional[str]: + """Return an option from the given section of the .ini file.""" + if not self.file_config.has_section(section): + raise util.CommandError( + "No config file %r found, or file has no " + "'[%s]' section" % (self.config_file_name, section) + ) + if self.file_config.has_option(section, name): + return self.file_config.get(section, name) + else: + return default + + @overload + def get_main_option(self, name: str, default: str) -> str: ... + + @overload + def get_main_option( + self, name: str, default: Optional[str] = None + ) -> Optional[str]: ... + + def get_main_option( + self, name: str, default: Optional[str] = None + ) -> Optional[str]: + """Return an option from the 'main' section of the .ini file. + + This defaults to being a key from the ``[alembic]`` + section, unless the ``-n/--name`` flag were used to + indicate a different section. + + Does **NOT** consume from the pyproject.toml file. + + .. seealso:: + + :meth:`.Config.get_alembic_option` - includes pyproject support + + """ + return self.get_section_option(self.config_ini_section, name, default) + + @overload + def get_alembic_option(self, name: str, default: str) -> str: ... + + @overload + def get_alembic_option( + self, name: str, default: Optional[str] = None + ) -> Optional[str]: ... + + def get_alembic_option( + self, name: str, default: Optional[str] = None + ) -> Union[ + None, str, list[str], dict[str, str], list[dict[str, str]], int + ]: + """Return an option from the "[alembic]" or "[tool.alembic]" section + of the configparser-parsed .ini file (e.g. ``alembic.ini``) or + toml-parsed ``pyproject.toml`` file. + + The value returned is expected to be None, string, list of strings, + or dictionary of strings. Within each type of string value, the + ``%(here)s`` token is substituted out with the absolute path of the + ``pyproject.toml`` file, as are other tokens which are extracted from + the :paramref:`.Config.config_args` dictionary. + + Searches always prioritize the configparser namespace first, before + searching in the toml namespace. + + If Alembic was run using the ``-n/--name`` flag to indicate an + alternate main section name, this is taken into account **only** for + the configparser-parsed .ini file. The section name in toml is always + ``[tool.alembic]``. + + + .. versionadded:: 1.16.0 + + """ + + if self.file_config.has_option(self.config_ini_section, name): + return self.file_config.get(self.config_ini_section, name) + else: + return self._get_toml_config_value(name, default=default) + + def get_alembic_boolean_option(self, name: str) -> bool: + if self.file_config.has_option(self.config_ini_section, name): + return ( + self.file_config.get(self.config_ini_section, name) == "true" + ) + else: + value = self.toml_alembic_config.get(name, False) + if not isinstance(value, bool): + raise util.CommandError( + f"boolean value expected for TOML parameter {name!r}" + ) + return value + + def _get_toml_config_value( + self, name: str, default: Optional[Any] = None + ) -> Union[ + None, str, list[str], dict[str, str], list[dict[str, str]], int + ]: + USE_DEFAULT = object() + value: Union[None, str, list[str], dict[str, str], int] = ( + self.toml_alembic_config.get(name, USE_DEFAULT) + ) + if value is USE_DEFAULT: + return default + if value is not None: + if isinstance(value, str): + value = value % (self.toml_args) + elif isinstance(value, list): + if value and isinstance(value[0], dict): + value = [ + {k: v % (self.toml_args) for k, v in dv.items()} + for dv in value + ] + else: + value = cast( + "list[str]", [v % (self.toml_args) for v in value] + ) + elif isinstance(value, dict): + value = cast( + "dict[str, str]", + {k: v % (self.toml_args) for k, v in value.items()}, + ) + elif isinstance(value, int): + return value + else: + raise util.CommandError( + f"unsupported TOML value type for key: {name!r}" + ) + return value + + @util.memoized_property + def messaging_opts(self) -> MessagingOptions: + """The messaging options.""" + return cast( + MessagingOptions, + util.immutabledict( + {"quiet": getattr(self.cmd_opts, "quiet", False)} + ), + ) + + def _get_file_separator_char(self, *names: str) -> Optional[str]: + for name in names: + separator = self.get_main_option(name) + if separator is not None: + break + else: + return None + + split_on_path = { + "space": " ", + "newline": "\n", + "os": os.pathsep, + ":": ":", + ";": ";", + } + + try: + sep = split_on_path[separator] + except KeyError as ke: + raise ValueError( + "'%s' is not a valid value for %s; " + "expected 'space', 'newline', 'os', ':', ';'" + % (separator, name) + ) from ke + else: + if name == "version_path_separator": + util.warn_deprecated( + "The version_path_separator configuration parameter " + "is deprecated; please use path_separator" + ) + return sep + + def get_version_locations_list(self) -> Optional[list[str]]: + + version_locations_str = self.file_config.get( + self.config_ini_section, "version_locations", fallback=None + ) + + if version_locations_str: + split_char = self._get_file_separator_char( + "path_separator", "version_path_separator" + ) + + if split_char is None: + + # legacy behaviour for backwards compatibility + util.warn_deprecated( + "No path_separator found in configuration; " + "falling back to legacy splitting on spaces/commas " + "for version_locations. Consider adding " + "path_separator=os to Alembic config." + ) + + _split_on_space_comma = re.compile(r", *|(?: +)") + return _split_on_space_comma.split(version_locations_str) + else: + return [ + x.strip() + for x in version_locations_str.split(split_char) + if x + ] + else: + return cast( + "list[str]", + self._get_toml_config_value("version_locations", None), + ) + + def get_prepend_sys_paths_list(self) -> Optional[list[str]]: + prepend_sys_path_str = self.file_config.get( + self.config_ini_section, "prepend_sys_path", fallback=None + ) + + if prepend_sys_path_str: + split_char = self._get_file_separator_char("path_separator") + + if split_char is None: + + # legacy behaviour for backwards compatibility + util.warn_deprecated( + "No path_separator found in configuration; " + "falling back to legacy splitting on spaces, commas, " + "and colons for prepend_sys_path. Consider adding " + "path_separator=os to Alembic config." + ) + + _split_on_space_comma_colon = re.compile(r", *|(?: +)|\:") + return _split_on_space_comma_colon.split(prepend_sys_path_str) + else: + return [ + x.strip() + for x in prepend_sys_path_str.split(split_char) + if x + ] + else: + return cast( + "list[str]", + self._get_toml_config_value("prepend_sys_path", None), + ) + + def get_hooks_list(self) -> list[PostWriteHookConfig]: + + hooks: list[PostWriteHookConfig] = [] + + if not self.file_config.has_section("post_write_hooks"): + toml_hook_config = cast( + "list[dict[str, str]]", + self._get_toml_config_value("post_write_hooks", []), + ) + for cfg in toml_hook_config: + opts = dict(cfg) + opts["_hook_name"] = opts.pop("name") + hooks.append(opts) + + else: + _split_on_space_comma = re.compile(r", *|(?: +)") + ini_hook_config = self.get_section("post_write_hooks", {}) + names = _split_on_space_comma.split( + ini_hook_config.get("hooks", "") + ) + + for name in names: + if not name: + continue + opts = { + key[len(name) + 1 :]: ini_hook_config[key] + for key in ini_hook_config + if key.startswith(name + ".") + } + + opts["_hook_name"] = name + hooks.append(opts) + + return hooks + + +PostWriteHookConfig = Mapping[str, str] + + +class MessagingOptions(TypedDict, total=False): + quiet: bool + + +class CommandFunction(Protocol): + """A function that may be registered in the CLI as an alembic command. + It must be a named function and it must accept a :class:`.Config` object + as the first argument. + + .. versionadded:: 1.15.3 + + """ + + __name__: str + + def __call__(self, config: Config, *args: Any, **kwargs: Any) -> Any: ... + + +class CommandLine: + """Provides the command line interface to Alembic.""" + + def __init__(self, prog: Optional[str] = None) -> None: + self._generate_args(prog) + + _KWARGS_OPTS = { + "template": ( + "-t", + "--template", + dict( + default="generic", + type=str, + help="Setup template for use with 'init'", + ), + ), + "message": ( + "-m", + "--message", + dict(type=str, help="Message string to use with 'revision'"), + ), + "sql": ( + "--sql", + dict( + action="store_true", + help="Don't emit SQL to database - dump to " + "standard output/file instead. See docs on " + "offline mode.", + ), + ), + "tag": ( + "--tag", + dict( + type=str, + help="Arbitrary 'tag' name - can be used by " + "custom env.py scripts.", + ), + ), + "head": ( + "--head", + dict( + type=str, + help="Specify head revision or @head " + "to base new revision on.", + ), + ), + "splice": ( + "--splice", + dict( + action="store_true", + help="Allow a non-head revision as the 'head' to splice onto", + ), + ), + "depends_on": ( + "--depends-on", + dict( + action="append", + help="Specify one or more revision identifiers " + "which this revision should depend on.", + ), + ), + "rev_id": ( + "--rev-id", + dict( + type=str, + help="Specify a hardcoded revision id instead of " + "generating one", + ), + ), + "version_path": ( + "--version-path", + dict( + type=str, + help="Specify specific path from config for version file", + ), + ), + "branch_label": ( + "--branch-label", + dict( + type=str, + help="Specify a branch label to apply to the new revision", + ), + ), + "verbose": ( + "-v", + "--verbose", + dict(action="store_true", help="Use more verbose output"), + ), + "resolve_dependencies": ( + "--resolve-dependencies", + dict( + action="store_true", + help="Treat dependency versions as down revisions", + ), + ), + "autogenerate": ( + "--autogenerate", + dict( + action="store_true", + help="Populate revision script with candidate " + "migration operations, based on comparison " + "of database to model.", + ), + ), + "rev_range": ( + "-r", + "--rev-range", + dict( + action="store", + help="Specify a revision range; format is [start]:[end]", + ), + ), + "indicate_current": ( + "-i", + "--indicate-current", + dict( + action="store_true", + help="Indicate the current revision", + ), + ), + "purge": ( + "--purge", + dict( + action="store_true", + help="Unconditionally erase the version table before stamping", + ), + ), + "package": ( + "--package", + dict( + action="store_true", + help="Write empty __init__.py files to the " + "environment and version locations", + ), + ), + "check_heads": ( + "-c", + "--check-heads", + dict( + action="store_true", + help=( + "Check if all head revisions are applied to the database. " + "Exit with an error code if this is not the case." + ), + ), + ), + } + _POSITIONAL_OPTS = { + "directory": dict(help="location of scripts directory"), + "revision": dict( + help="revision identifier", + ), + "revisions": dict( + nargs="+", + help="one or more revisions, or 'heads' for all heads", + ), + } + _POSITIONAL_TRANSLATIONS: dict[Any, dict[str, str]] = { + command.stamp: {"revision": "revisions"} + } + + def _generate_args(self, prog: Optional[str]) -> None: + parser = ArgumentParser(prog=prog) + + parser.add_argument( + "--version", action="version", version="%%(prog)s %s" % __version__ + ) + parser.add_argument( + "-c", + "--config", + action="append", + help="Alternate config file; defaults to value of " + 'ALEMBIC_CONFIG environment variable, or "alembic.ini". ' + "May also refer to pyproject.toml file. May be specified twice " + "to reference both files separately", + ) + parser.add_argument( + "-n", + "--name", + type=str, + default="alembic", + help="Name of section in .ini file to use for Alembic config " + "(only applies to configparser config, not toml)", + ) + parser.add_argument( + "-x", + action="append", + help="Additional arguments consumed by " + "custom env.py scripts, e.g. -x " + "setting1=somesetting -x setting2=somesetting", + ) + parser.add_argument( + "--raiseerr", + action="store_true", + help="Raise a full stack trace on error", + ) + parser.add_argument( + "-q", + "--quiet", + action="store_true", + help="Do not log to std output.", + ) + + self.subparsers = parser.add_subparsers() + alembic_commands = ( + cast(CommandFunction, fn) + for fn in (getattr(command, name) for name in dir(command)) + if ( + inspect.isfunction(fn) + and fn.__name__[0] != "_" + and fn.__module__ == "alembic.command" + ) + ) + + for fn in alembic_commands: + self.register_command(fn) + + self.parser = parser + + def register_command(self, fn: CommandFunction) -> None: + """Registers a function as a CLI subcommand. The subcommand name + matches the function name, the arguments are extracted from the + signature and the help text is read from the docstring. + + .. versionadded:: 1.15.3 + + .. seealso:: + + :ref:`custom_commandline` + """ + + positional, kwarg, help_text = self._inspect_function(fn) + + subparser = self.subparsers.add_parser(fn.__name__, help=help_text) + subparser.set_defaults(cmd=(fn, positional, kwarg)) + + for arg in kwarg: + if arg in self._KWARGS_OPTS: + kwarg_opt = self._KWARGS_OPTS[arg] + args, opts = kwarg_opt[0:-1], kwarg_opt[-1] + subparser.add_argument(*args, **opts) # type:ignore + + for arg in positional: + opts = self._POSITIONAL_OPTS.get(arg, {}) + subparser.add_argument(arg, **opts) # type:ignore + + def _inspect_function(self, fn: CommandFunction) -> tuple[Any, Any, str]: + spec = compat.inspect_getfullargspec(fn) + if spec[3] is not None: + positional = spec[0][1 : -len(spec[3])] + kwarg = spec[0][-len(spec[3]) :] + else: + positional = spec[0][1:] + kwarg = [] + + if fn in self._POSITIONAL_TRANSLATIONS: + positional = [ + self._POSITIONAL_TRANSLATIONS[fn].get(name, name) + for name in positional + ] + + # parse first line(s) of helptext without a line break + help_ = fn.__doc__ + if help_: + help_lines = [] + for line in help_.split("\n"): + if not line.strip(): + break + else: + help_lines.append(line.strip()) + else: + help_lines = [] + + help_text = " ".join(help_lines) + + return positional, kwarg, help_text + + def run_cmd(self, config: Config, options: Namespace) -> None: + fn, positional, kwarg = options.cmd + + try: + fn( + config, + *[getattr(options, k, None) for k in positional], + **{k: getattr(options, k, None) for k in kwarg}, + ) + except util.CommandError as e: + if options.raiseerr: + raise + else: + util.err(str(e), **config.messaging_opts) + + def _inis_from_config(self, options: Namespace) -> tuple[str, str]: + names = options.config + + alembic_config_env = os.environ.get("ALEMBIC_CONFIG") + if ( + alembic_config_env + and os.path.basename(alembic_config_env) == "pyproject.toml" + ): + default_pyproject_toml = alembic_config_env + default_alembic_config = "alembic.ini" + elif alembic_config_env: + default_pyproject_toml = "pyproject.toml" + default_alembic_config = alembic_config_env + else: + default_alembic_config = "alembic.ini" + default_pyproject_toml = "pyproject.toml" + + if not names: + return default_pyproject_toml, default_alembic_config + + toml = ini = None + + for name in names: + if os.path.basename(name) == "pyproject.toml": + if toml is not None: + raise util.CommandError( + "pyproject.toml indicated more than once" + ) + toml = name + else: + if ini is not None: + raise util.CommandError( + "only one ini file may be indicated" + ) + ini = name + + return toml if toml else default_pyproject_toml, ( + ini if ini else default_alembic_config + ) + + def main(self, argv: Optional[Sequence[str]] = None) -> None: + """Executes the command line with the provided arguments.""" + options = self.parser.parse_args(argv) + if not hasattr(options, "cmd"): + # see http://bugs.python.org/issue9253, argparse + # behavior changed incompatibly in py3.3 + self.parser.error("too few arguments") + else: + toml, ini = self._inis_from_config(options) + cfg = Config( + file_=ini, + toml_file=toml, + ini_section=options.name, + cmd_opts=options, + ) + self.run_cmd(cfg, options) + + +def main( + argv: Optional[Sequence[str]] = None, + prog: Optional[str] = None, + **kwargs: Any, +) -> None: + """The console runner function for Alembic.""" + + CommandLine(prog=prog).main(argv=argv) + + +if __name__ == "__main__": + main() diff --git a/venv/lib/python3.12/site-packages/alembic/context.py b/venv/lib/python3.12/site-packages/alembic/context.py new file mode 100644 index 00000000..758fca87 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/context.py @@ -0,0 +1,5 @@ +from .runtime.environment import EnvironmentContext + +# create proxy functions for +# each method on the EnvironmentContext class. +EnvironmentContext.create_module_class_proxy(globals(), locals()) diff --git a/venv/lib/python3.12/site-packages/alembic/context.pyi b/venv/lib/python3.12/site-packages/alembic/context.pyi new file mode 100644 index 00000000..6045d8b3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/context.pyi @@ -0,0 +1,876 @@ +# ### this file stubs are generated by tools/write_pyi.py - do not edit ### +# ### imports are manually managed +from __future__ import annotations + +from typing import Any +from typing import Callable +from typing import Collection +from typing import Dict +from typing import Iterable +from typing import List +from typing import Literal +from typing import Mapping +from typing import MutableMapping +from typing import Optional +from typing import overload +from typing import Sequence +from typing import TextIO +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +from typing_extensions import ContextManager + +if TYPE_CHECKING: + from sqlalchemy.engine.base import Connection + from sqlalchemy.engine.url import URL + from sqlalchemy.sql import Executable + from sqlalchemy.sql.schema import Column + from sqlalchemy.sql.schema import FetchedValue + from sqlalchemy.sql.schema import MetaData + from sqlalchemy.sql.schema import SchemaItem + from sqlalchemy.sql.type_api import TypeEngine + + from .autogenerate.api import AutogenContext + from .config import Config + from .operations.ops import MigrationScript + from .runtime.migration import _ProxyTransaction + from .runtime.migration import MigrationContext + from .runtime.migration import MigrationInfo + from .script import ScriptDirectory + +### end imports ### + +def begin_transaction() -> ( + Union[_ProxyTransaction, ContextManager[None, Optional[bool]]] +): + """Return a context manager that will + enclose an operation within a "transaction", + as defined by the environment's offline + and transactional DDL settings. + + e.g.:: + + with context.begin_transaction(): + context.run_migrations() + + :meth:`.begin_transaction` is intended to + "do the right thing" regardless of + calling context: + + * If :meth:`.is_transactional_ddl` is ``False``, + returns a "do nothing" context manager + which otherwise produces no transactional + state or directives. + * If :meth:`.is_offline_mode` is ``True``, + returns a context manager that will + invoke the :meth:`.DefaultImpl.emit_begin` + and :meth:`.DefaultImpl.emit_commit` + methods, which will produce the string + directives ``BEGIN`` and ``COMMIT`` on + the output stream, as rendered by the + target backend (e.g. SQL Server would + emit ``BEGIN TRANSACTION``). + * Otherwise, calls :meth:`sqlalchemy.engine.Connection.begin` + on the current online connection, which + returns a :class:`sqlalchemy.engine.Transaction` + object. This object demarcates a real + transaction and is itself a context manager, + which will roll back if an exception + is raised. + + Note that a custom ``env.py`` script which + has more specific transactional needs can of course + manipulate the :class:`~sqlalchemy.engine.Connection` + directly to produce transactional state in "online" + mode. + + """ + +config: Config + +def configure( + connection: Optional[Connection] = None, + url: Union[str, URL, None] = None, + dialect_name: Optional[str] = None, + dialect_opts: Optional[Dict[str, Any]] = None, + transactional_ddl: Optional[bool] = None, + transaction_per_migration: bool = False, + output_buffer: Optional[TextIO] = None, + starting_rev: Optional[str] = None, + tag: Optional[str] = None, + template_args: Optional[Dict[str, Any]] = None, + render_as_batch: bool = False, + target_metadata: Union[MetaData, Sequence[MetaData], None] = None, + include_name: Optional[ + Callable[ + [ + Optional[str], + Literal[ + "schema", + "table", + "column", + "index", + "unique_constraint", + "foreign_key_constraint", + ], + MutableMapping[ + Literal[ + "schema_name", + "table_name", + "schema_qualified_table_name", + ], + Optional[str], + ], + ], + bool, + ] + ] = None, + include_object: Optional[ + Callable[ + [ + SchemaItem, + Optional[str], + Literal[ + "schema", + "table", + "column", + "index", + "unique_constraint", + "foreign_key_constraint", + ], + bool, + Optional[SchemaItem], + ], + bool, + ] + ] = None, + include_schemas: bool = False, + process_revision_directives: Optional[ + Callable[ + [ + MigrationContext, + Union[str, Iterable[Optional[str]], Iterable[str]], + List[MigrationScript], + ], + None, + ] + ] = None, + compare_type: Union[ + bool, + Callable[ + [ + MigrationContext, + Column[Any], + Column[Any], + TypeEngine[Any], + TypeEngine[Any], + ], + Optional[bool], + ], + ] = True, + compare_server_default: Union[ + bool, + Callable[ + [ + MigrationContext, + Column[Any], + Column[Any], + Optional[str], + Optional[FetchedValue], + Optional[str], + ], + Optional[bool], + ], + ] = False, + render_item: Optional[ + Callable[[str, Any, AutogenContext], Union[str, Literal[False]]] + ] = None, + literal_binds: bool = False, + upgrade_token: str = "upgrades", + downgrade_token: str = "downgrades", + alembic_module_prefix: str = "op.", + sqlalchemy_module_prefix: str = "sa.", + user_module_prefix: Optional[str] = None, + on_version_apply: Optional[ + Callable[ + [ + MigrationContext, + MigrationInfo, + Collection[Any], + Mapping[str, Any], + ], + None, + ] + ] = None, + autogenerate_plugins: Optional[Sequence[str]] = None, + **kw: Any, +) -> None: + """Configure a :class:`.MigrationContext` within this + :class:`.EnvironmentContext` which will provide database + connectivity and other configuration to a series of + migration scripts. + + Many methods on :class:`.EnvironmentContext` require that + this method has been called in order to function, as they + ultimately need to have database access or at least access + to the dialect in use. Those which do are documented as such. + + The important thing needed by :meth:`.configure` is a + means to determine what kind of database dialect is in use. + An actual connection to that database is needed only if + the :class:`.MigrationContext` is to be used in + "online" mode. + + If the :meth:`.is_offline_mode` function returns ``True``, + then no connection is needed here. Otherwise, the + ``connection`` parameter should be present as an + instance of :class:`sqlalchemy.engine.Connection`. + + This function is typically called from the ``env.py`` + script within a migration environment. It can be called + multiple times for an invocation. The most recent + :class:`~sqlalchemy.engine.Connection` + for which it was called is the one that will be operated upon + by the next call to :meth:`.run_migrations`. + + General parameters: + + :param connection: a :class:`~sqlalchemy.engine.Connection` + to use + for SQL execution in "online" mode. When present, is also + used to determine the type of dialect in use. + :param url: a string database url, or a + :class:`sqlalchemy.engine.url.URL` object. + The type of dialect to be used will be derived from this if + ``connection`` is not passed. + :param dialect_name: string name of a dialect, such as + "postgresql", "mssql", etc. + The type of dialect to be used will be derived from this if + ``connection`` and ``url`` are not passed. + :param dialect_opts: dictionary of options to be passed to dialect + constructor. + :param transactional_ddl: Force the usage of "transactional" + DDL on or off; + this otherwise defaults to whether or not the dialect in + use supports it. + :param transaction_per_migration: if True, nest each migration script + in a transaction rather than the full series of migrations to + run. + :param output_buffer: a file-like object that will be used + for textual output + when the ``--sql`` option is used to generate SQL scripts. + Defaults to + ``sys.stdout`` if not passed here and also not present on + the :class:`.Config` + object. The value here overrides that of the :class:`.Config` + object. + :param output_encoding: when using ``--sql`` to generate SQL + scripts, apply this encoding to the string output. + :param literal_binds: when using ``--sql`` to generate SQL + scripts, pass through the ``literal_binds`` flag to the compiler + so that any literal values that would ordinarily be bound + parameters are converted to plain strings. + + .. warning:: Dialects can typically only handle simple datatypes + like strings and numbers for auto-literal generation. Datatypes + like dates, intervals, and others may still require manual + formatting, typically using :meth:`.Operations.inline_literal`. + + .. note:: the ``literal_binds`` flag is ignored on SQLAlchemy + versions prior to 0.8 where this feature is not supported. + + .. seealso:: + + :meth:`.Operations.inline_literal` + + :param starting_rev: Override the "starting revision" argument + when using ``--sql`` mode. + :param tag: a string tag for usage by custom ``env.py`` scripts. + Set via the ``--tag`` option, can be overridden here. + :param template_args: dictionary of template arguments which + will be added to the template argument environment when + running the "revision" command. Note that the script environment + is only run within the "revision" command if the --autogenerate + option is used, or if the option "revision_environment=true" + is present in the alembic.ini file. + + :param version_table: The name of the Alembic version table. + The default is ``'alembic_version'``. + :param version_table_schema: Optional schema to place version + table within. + :param version_table_pk: boolean, whether the Alembic version table + should use a primary key constraint for the "value" column; this + only takes effect when the table is first created. + Defaults to True; setting to False should not be necessary and is + here for backwards compatibility reasons. + :param on_version_apply: a callable or collection of callables to be + run for each migration step. + The callables will be run in the order they are given, once for + each migration step, after the respective operation has been + applied but before its transaction is finalized. + Each callable accepts no positional arguments and the following + keyword arguments: + + * ``ctx``: the :class:`.MigrationContext` running the migration, + * ``step``: a :class:`.MigrationInfo` representing the + step currently being applied, + * ``heads``: a collection of version strings representing the + current heads, + * ``run_args``: the ``**kwargs`` passed to :meth:`.run_migrations`. + + Parameters specific to the autogenerate feature, when + ``alembic revision`` is run with the ``--autogenerate`` feature: + + :param target_metadata: a :class:`sqlalchemy.schema.MetaData` + object, or a sequence of :class:`~sqlalchemy.schema.MetaData` + objects, that will be consulted during autogeneration. + The tables present in each :class:`~sqlalchemy.schema.MetaData` + will be compared against + what is locally available on the target + :class:`~sqlalchemy.engine.Connection` + to produce candidate upgrade/downgrade operations. + :param compare_type: Indicates type comparison behavior during + an autogenerate + operation. Defaults to ``True`` turning on type comparison, which + has good accuracy on most backends. See :ref:`compare_types` + for an example as well as information on other type + comparison options. Set to ``False`` which disables type + comparison. A callable can also be passed to provide custom type + comparison, see :ref:`compare_types` for additional details. + + .. versionchanged:: 1.12.0 The default value of + :paramref:`.EnvironmentContext.configure.compare_type` has been + changed to ``True``. + + .. seealso:: + + :ref:`compare_types` + + :paramref:`.EnvironmentContext.configure.compare_server_default` + + :param compare_server_default: Indicates server default comparison + behavior during + an autogenerate operation. Defaults to ``False`` which disables + server default + comparison. Set to ``True`` to turn on server default comparison, + which has + varied accuracy depending on backend. + + To customize server default comparison behavior, a callable may + be specified + which can filter server default comparisons during an + autogenerate operation. + defaults during an autogenerate operation. The format of this + callable is:: + + def my_compare_server_default(context, inspected_column, + metadata_column, inspected_default, metadata_default, + rendered_metadata_default): + # return True if the defaults are different, + # False if not, or None to allow the default implementation + # to compare these defaults + return None + + context.configure( + # ... + compare_server_default = my_compare_server_default + ) + + ``inspected_column`` is a dictionary structure as returned by + :meth:`sqlalchemy.engine.reflection.Inspector.get_columns`, whereas + ``metadata_column`` is a :class:`sqlalchemy.schema.Column` from + the local model environment. + + A return value of ``None`` indicates to allow default server default + comparison + to proceed. Note that some backends such as Postgresql actually + execute + the two defaults on the database side to compare for equivalence. + + .. seealso:: + + :paramref:`.EnvironmentContext.configure.compare_type` + + :param include_name: A callable function which is given + the chance to return ``True`` or ``False`` for any database reflected + object based on its name, including database schema names when + the :paramref:`.EnvironmentContext.configure.include_schemas` flag + is set to ``True``. + + The function accepts the following positional arguments: + + * ``name``: the name of the object, such as schema name or table name. + Will be ``None`` when indicating the default schema name of the + database connection. + * ``type``: a string describing the type of object; currently + ``"schema"``, ``"table"``, ``"column"``, ``"index"``, + ``"unique_constraint"``, or ``"foreign_key_constraint"`` + * ``parent_names``: a dictionary of "parent" object names, that are + relative to the name being given. Keys in this dictionary may + include: ``"schema_name"``, ``"table_name"`` or + ``"schema_qualified_table_name"``. + + E.g.:: + + def include_name(name, type_, parent_names): + if type_ == "schema": + return name in ["schema_one", "schema_two"] + else: + return True + + context.configure( + # ... + include_schemas = True, + include_name = include_name + ) + + .. seealso:: + + :ref:`autogenerate_include_hooks` + + :paramref:`.EnvironmentContext.configure.include_object` + + :paramref:`.EnvironmentContext.configure.include_schemas` + + + :param include_object: A callable function which is given + the chance to return ``True`` or ``False`` for any object, + indicating if the given object should be considered in the + autogenerate sweep. + + The function accepts the following positional arguments: + + * ``object``: a :class:`~sqlalchemy.schema.SchemaItem` object such + as a :class:`~sqlalchemy.schema.Table`, + :class:`~sqlalchemy.schema.Column`, + :class:`~sqlalchemy.schema.Index` + :class:`~sqlalchemy.schema.UniqueConstraint`, + or :class:`~sqlalchemy.schema.ForeignKeyConstraint` object + * ``name``: the name of the object. This is typically available + via ``object.name``. + * ``type``: a string describing the type of object; currently + ``"table"``, ``"column"``, ``"index"``, ``"unique_constraint"``, + or ``"foreign_key_constraint"`` + * ``reflected``: ``True`` if the given object was produced based on + table reflection, ``False`` if it's from a local :class:`.MetaData` + object. + * ``compare_to``: the object being compared against, if available, + else ``None``. + + E.g.:: + + def include_object(object, name, type_, reflected, compare_to): + if (type_ == "column" and + not reflected and + object.info.get("skip_autogenerate", False)): + return False + else: + return True + + context.configure( + # ... + include_object = include_object + ) + + For the use case of omitting specific schemas from a target database + when :paramref:`.EnvironmentContext.configure.include_schemas` is + set to ``True``, the :attr:`~sqlalchemy.schema.Table.schema` + attribute can be checked for each :class:`~sqlalchemy.schema.Table` + object passed to the hook, however it is much more efficient + to filter on schemas before reflection of objects takes place + using the :paramref:`.EnvironmentContext.configure.include_name` + hook. + + .. seealso:: + + :ref:`autogenerate_include_hooks` + + :paramref:`.EnvironmentContext.configure.include_name` + + :paramref:`.EnvironmentContext.configure.include_schemas` + + :param render_as_batch: if True, commands which alter elements + within a table will be placed under a ``with batch_alter_table():`` + directive, so that batch migrations will take place. + + .. seealso:: + + :ref:`batch_migrations` + + :param include_schemas: If True, autogenerate will scan across + all schemas located by the SQLAlchemy + :meth:`~sqlalchemy.engine.reflection.Inspector.get_schema_names` + method, and include all differences in tables found across all + those schemas. When using this option, you may want to also + use the :paramref:`.EnvironmentContext.configure.include_name` + parameter to specify a callable which + can filter the tables/schemas that get included. + + .. seealso:: + + :ref:`autogenerate_include_hooks` + + :paramref:`.EnvironmentContext.configure.include_name` + + :paramref:`.EnvironmentContext.configure.include_object` + + :param render_item: Callable that can be used to override how + any schema item, i.e. column, constraint, type, + etc., is rendered for autogenerate. The callable receives a + string describing the type of object, the object, and + the autogen context. If it returns False, the + default rendering method will be used. If it returns None, + the item will not be rendered in the context of a Table + construct, that is, can be used to skip columns or constraints + within op.create_table():: + + def my_render_column(type_, col, autogen_context): + if type_ == "column" and isinstance(col, MySpecialCol): + return repr(col) + else: + return False + + context.configure( + # ... + render_item = my_render_column + ) + + Available values for the type string include: ``"column"``, + ``"primary_key"``, ``"foreign_key"``, ``"unique"``, ``"check"``, + ``"type"``, ``"server_default"``. + + .. seealso:: + + :ref:`autogen_render_types` + + :param upgrade_token: When autogenerate completes, the text of the + candidate upgrade operations will be present in this template + variable when ``script.py.mako`` is rendered. Defaults to + ``upgrades``. + :param downgrade_token: When autogenerate completes, the text of the + candidate downgrade operations will be present in this + template variable when ``script.py.mako`` is rendered. Defaults to + ``downgrades``. + + :param alembic_module_prefix: When autogenerate refers to Alembic + :mod:`alembic.operations` constructs, this prefix will be used + (i.e. ``op.create_table``) Defaults to "``op.``". + Can be ``None`` to indicate no prefix. + + :param sqlalchemy_module_prefix: When autogenerate refers to + SQLAlchemy + :class:`~sqlalchemy.schema.Column` or type classes, this prefix + will be used + (i.e. ``sa.Column("somename", sa.Integer)``) Defaults to "``sa.``". + Can be ``None`` to indicate no prefix. + Note that when dialect-specific types are rendered, autogenerate + will render them using the dialect module name, i.e. ``mssql.BIT()``, + ``postgresql.UUID()``. + + :param user_module_prefix: When autogenerate refers to a SQLAlchemy + type (e.g. :class:`.TypeEngine`) where the module name is not + under the ``sqlalchemy`` namespace, this prefix will be used + within autogenerate. If left at its default of + ``None``, the ``__module__`` attribute of the type is used to + render the import module. It's a good practice to set this + and to have all custom types be available from a fixed module space, + in order to future-proof migration files against reorganizations + in modules. + + .. seealso:: + + :ref:`autogen_module_prefix` + + :param process_revision_directives: a callable function that will + be passed a structure representing the end result of an autogenerate + or plain "revision" operation, which can be manipulated to affect + how the ``alembic revision`` command ultimately outputs new + revision scripts. The structure of the callable is:: + + def process_revision_directives(context, revision, directives): + pass + + The ``directives`` parameter is a Python list containing + a single :class:`.MigrationScript` directive, which represents + the revision file to be generated. This list as well as its + contents may be freely modified to produce any set of commands. + The section :ref:`customizing_revision` shows an example of + doing this. The ``context`` parameter is the + :class:`.MigrationContext` in use, + and ``revision`` is a tuple of revision identifiers representing the + current revision of the database. + + The callable is invoked at all times when the ``--autogenerate`` + option is passed to ``alembic revision``. If ``--autogenerate`` + is not passed, the callable is invoked only if the + ``revision_environment`` variable is set to True in the Alembic + configuration, in which case the given ``directives`` collection + will contain empty :class:`.UpgradeOps` and :class:`.DowngradeOps` + collections for ``.upgrade_ops`` and ``.downgrade_ops``. The + ``--autogenerate`` option itself can be inferred by inspecting + ``context.config.cmd_opts.autogenerate``. + + The callable function may optionally be an instance of + a :class:`.Rewriter` object. This is a helper object that + assists in the production of autogenerate-stream rewriter functions. + + .. seealso:: + + :ref:`customizing_revision` + + :ref:`autogen_rewriter` + + :paramref:`.command.revision.process_revision_directives` + + :param autogenerate_plugins: A list of string names of "plugins" that + should participate in this autogenerate run. Defaults to the list + ``["alembic.autogenerate.*"]``, which indicates that Alembic's default + autogeneration plugins will be used. + + See the section :ref:`plugins_autogenerate` for complete background + on how to use this parameter. + + .. versionadded:: 1.18.0 Added a new plugin system for autogenerate + compare directives. + + .. seealso:: + + :ref:`plugins_autogenerate` - background on enabling/disabling + autogenerate plugins + + :ref:`alembic.plugins.toplevel` - Introduction and documentation + to the plugin system + + Parameters specific to individual backends: + + :param mssql_batch_separator: The "batch separator" which will + be placed between each statement when generating offline SQL Server + migrations. Defaults to ``GO``. Note this is in addition to the + customary semicolon ``;`` at the end of each statement; SQL Server + considers the "batch separator" to denote the end of an + individual statement execution, and cannot group certain + dependent operations in one step. + :param oracle_batch_separator: The "batch separator" which will + be placed between each statement when generating offline + Oracle migrations. Defaults to ``/``. Oracle doesn't add a + semicolon between statements like most other backends. + + """ + +def execute( + sql: Union[Executable, str], + execution_options: Optional[Dict[str, Any]] = None, +) -> None: + """Execute the given SQL using the current change context. + + The behavior of :meth:`.execute` is the same + as that of :meth:`.Operations.execute`. Please see that + function's documentation for full detail including + caveats and limitations. + + This function requires that a :class:`.MigrationContext` has + first been made available via :meth:`.configure`. + + """ + +def get_bind() -> Connection: + """Return the current 'bind'. + + In "online" mode, this is the + :class:`sqlalchemy.engine.Connection` currently being used + to emit SQL to the database. + + This function requires that a :class:`.MigrationContext` + has first been made available via :meth:`.configure`. + + """ + +def get_context() -> MigrationContext: + """Return the current :class:`.MigrationContext` object. + + If :meth:`.EnvironmentContext.configure` has not been + called yet, raises an exception. + + """ + +def get_head_revision() -> Union[str, Tuple[str, ...], None]: + """Return the hex identifier of the 'head' script revision. + + If the script directory has multiple heads, this + method raises a :class:`.CommandError`; + :meth:`.EnvironmentContext.get_head_revisions` should be preferred. + + This function does not require that the :class:`.MigrationContext` + has been configured. + + .. seealso:: :meth:`.EnvironmentContext.get_head_revisions` + + """ + +def get_head_revisions() -> Union[str, Tuple[str, ...], None]: + """Return the hex identifier of the 'heads' script revision(s). + + This returns a tuple containing the version number of all + heads in the script directory. + + This function does not require that the :class:`.MigrationContext` + has been configured. + + """ + +def get_revision_argument() -> Union[str, Tuple[str, ...], None]: + """Get the 'destination' revision argument. + + This is typically the argument passed to the + ``upgrade`` or ``downgrade`` command. + + If it was specified as ``head``, the actual + version number is returned; if specified + as ``base``, ``None`` is returned. + + This function does not require that the :class:`.MigrationContext` + has been configured. + + """ + +def get_starting_revision_argument() -> Union[str, Tuple[str, ...], None]: + """Return the 'starting revision' argument, + if the revision was passed using ``start:end``. + + This is only meaningful in "offline" mode. + Returns ``None`` if no value is available + or was configured. + + This function does not require that the :class:`.MigrationContext` + has been configured. + + """ + +def get_tag_argument() -> Optional[str]: + """Return the value passed for the ``--tag`` argument, if any. + + The ``--tag`` argument is not used directly by Alembic, + but is available for custom ``env.py`` configurations that + wish to use it; particularly for offline generation scripts + that wish to generate tagged filenames. + + This function does not require that the :class:`.MigrationContext` + has been configured. + + .. seealso:: + + :meth:`.EnvironmentContext.get_x_argument` - a newer and more + open ended system of extending ``env.py`` scripts via the command + line. + + """ + +@overload +def get_x_argument(as_dictionary: Literal[False]) -> List[str]: ... +@overload +def get_x_argument(as_dictionary: Literal[True]) -> Dict[str, str]: ... +@overload +def get_x_argument( + as_dictionary: bool = ..., +) -> Union[List[str], Dict[str, str]]: + """Return the value(s) passed for the ``-x`` argument, if any. + + The ``-x`` argument is an open ended flag that allows any user-defined + value or values to be passed on the command line, then available + here for consumption by a custom ``env.py`` script. + + The return value is a list, returned directly from the ``argparse`` + structure. If ``as_dictionary=True`` is passed, the ``x`` arguments + are parsed using ``key=value`` format into a dictionary that is + then returned. If there is no ``=`` in the argument, value is an empty + string. + + .. versionchanged:: 1.13.1 Support ``as_dictionary=True`` when + arguments are passed without the ``=`` symbol. + + For example, to support passing a database URL on the command line, + the standard ``env.py`` script can be modified like this:: + + cmd_line_url = context.get_x_argument( + as_dictionary=True).get('dbname') + if cmd_line_url: + engine = create_engine(cmd_line_url) + else: + engine = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool) + + This then takes effect by running the ``alembic`` script as:: + + alembic -x dbname=postgresql://user:pass@host/dbname upgrade head + + This function does not require that the :class:`.MigrationContext` + has been configured. + + .. seealso:: + + :meth:`.EnvironmentContext.get_tag_argument` + + :attr:`.Config.cmd_opts` + + """ + +def is_offline_mode() -> bool: + """Return True if the current migrations environment + is running in "offline mode". + + This is ``True`` or ``False`` depending + on the ``--sql`` flag passed. + + This function does not require that the :class:`.MigrationContext` + has been configured. + + """ + +def is_transactional_ddl() -> bool: + """Return True if the context is configured to expect a + transactional DDL capable backend. + + This defaults to the type of database in use, and + can be overridden by the ``transactional_ddl`` argument + to :meth:`.configure` + + This function requires that a :class:`.MigrationContext` + has first been made available via :meth:`.configure`. + + """ + +def run_migrations(**kw: Any) -> None: + """Run migrations as determined by the current command line + configuration + as well as versioning information present (or not) in the current + database connection (if one is present). + + The function accepts optional ``**kw`` arguments. If these are + passed, they are sent directly to the ``upgrade()`` and + ``downgrade()`` + functions within each target revision file. By modifying the + ``script.py.mako`` file so that the ``upgrade()`` and ``downgrade()`` + functions accept arguments, parameters can be passed here so that + contextual information, usually information to identify a particular + database in use, can be passed from a custom ``env.py`` script + to the migration functions. + + This function requires that a :class:`.MigrationContext` has + first been made available via :meth:`.configure`. + + """ + +script: ScriptDirectory + +def static_output(text: str) -> None: + """Emit text directly to the "offline" SQL stream. + + Typically this is for emitting comments that + start with --. The statement is not treated + as a SQL execution, no ; or batch separator + is added, etc. + + """ diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/__init__.py b/venv/lib/python3.12/site-packages/alembic/ddl/__init__.py new file mode 100644 index 00000000..f2f72b3d --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/ddl/__init__.py @@ -0,0 +1,6 @@ +from . import mssql +from . import mysql +from . import oracle +from . import postgresql +from . import sqlite +from .impl import DefaultImpl as DefaultImpl diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0124f8300f2310f492c74692c82f8682a037544a GIT binary patch literal 384 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 a6DH8jKnGz?u6h$IjoLQHN)c$xKK~c&(B>Qf literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/impl.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/impl.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b510eeb09e0465347c150c261c449ed2d842a984 GIT binary patch literal 35887 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/mssql.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/mssql.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d9aa0e71ccf51495257272a54dabce6196de7d7 GIT binary patch literal 19401 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/mysql.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/mysql.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..792afbc4640a222d8c9c6371fbfcb0976cb82988 GIT binary patch literal 17009 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/postgresql.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/postgresql.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a7e5145a0e30c97abacfae6507ea0cf5fcc016c GIT binary patch literal 33690 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/sqlite.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/ddl/__pycache__/sqlite.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a0088c5d296a29a5c59d20f843e2d29ae85b956 GIT binary patch literal 8023 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/_autogen.py b/venv/lib/python3.12/site-packages/alembic/ddl/_autogen.py new file mode 100644 index 00000000..74715b18 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/ddl/_autogen.py @@ -0,0 +1,329 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +from typing import Any +from typing import ClassVar +from typing import Dict +from typing import Generic +from typing import NamedTuple +from typing import Optional +from typing import Sequence +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +from sqlalchemy.sql.schema import Constraint +from sqlalchemy.sql.schema import ForeignKeyConstraint +from sqlalchemy.sql.schema import Index +from sqlalchemy.sql.schema import UniqueConstraint +from typing_extensions import TypeGuard + +from .. import util +from ..util import sqla_compat + +if TYPE_CHECKING: + from typing import Literal + + from alembic.autogenerate.api import AutogenContext + from alembic.ddl.impl import DefaultImpl + +CompareConstraintType = Union[Constraint, Index] + +_C = TypeVar("_C", bound=CompareConstraintType) + +_clsreg: Dict[str, Type[_constraint_sig]] = {} + + +class ComparisonResult(NamedTuple): + status: Literal["equal", "different", "skip"] + message: str + + @property + def is_equal(self) -> bool: + return self.status == "equal" + + @property + def is_different(self) -> bool: + return self.status == "different" + + @property + def is_skip(self) -> bool: + return self.status == "skip" + + @classmethod + def Equal(cls) -> ComparisonResult: + """the constraints are equal.""" + return cls("equal", "The two constraints are equal") + + @classmethod + def Different(cls, reason: Union[str, Sequence[str]]) -> ComparisonResult: + """the constraints are different for the provided reason(s).""" + return cls("different", ", ".join(util.to_list(reason))) + + @classmethod + def Skip(cls, reason: Union[str, Sequence[str]]) -> ComparisonResult: + """the constraint cannot be compared for the provided reason(s). + + The message is logged, but the constraints will be otherwise + considered equal, meaning that no migration command will be + generated. + """ + return cls("skip", ", ".join(util.to_list(reason))) + + +class _constraint_sig(Generic[_C]): + const: _C + + _sig: Tuple[Any, ...] + name: Optional[sqla_compat._ConstraintNameDefined] + + impl: DefaultImpl + + _is_index: ClassVar[bool] = False + _is_fk: ClassVar[bool] = False + _is_uq: ClassVar[bool] = False + + _is_metadata: bool + + def __init_subclass__(cls) -> None: + cls._register() + + @classmethod + def _register(cls): + raise NotImplementedError() + + def __init__( + self, is_metadata: bool, impl: DefaultImpl, const: _C + ) -> None: + raise NotImplementedError() + + def compare_to_reflected( + self, other: _constraint_sig[Any] + ) -> ComparisonResult: + assert self.impl is other.impl + assert self._is_metadata + assert not other._is_metadata + + return self._compare_to_reflected(other) + + def _compare_to_reflected( + self, other: _constraint_sig[_C] + ) -> ComparisonResult: + raise NotImplementedError() + + @classmethod + def from_constraint( + cls, is_metadata: bool, impl: DefaultImpl, constraint: _C + ) -> _constraint_sig[_C]: + # these could be cached by constraint/impl, however, if the + # constraint is modified in place, then the sig is wrong. the mysql + # impl currently does this, and if we fixed that we can't be sure + # someone else might do it too, so play it safe. + sig = _clsreg[constraint.__visit_name__](is_metadata, impl, constraint) + return sig + + def md_name_to_sql_name(self, context: AutogenContext) -> Optional[str]: + return sqla_compat._get_constraint_final_name( + self.const, context.dialect + ) + + @util.memoized_property + def is_named(self): + return sqla_compat._constraint_is_named(self.const, self.impl.dialect) + + @util.memoized_property + def unnamed(self) -> Tuple[Any, ...]: + return self._sig + + @util.memoized_property + def unnamed_no_options(self) -> Tuple[Any, ...]: + raise NotImplementedError() + + @util.memoized_property + def _full_sig(self) -> Tuple[Any, ...]: + return (self.name,) + self.unnamed + + def __eq__(self, other) -> bool: + return self._full_sig == other._full_sig + + def __ne__(self, other) -> bool: + return self._full_sig != other._full_sig + + def __hash__(self) -> int: + return hash(self._full_sig) + + +class _uq_constraint_sig(_constraint_sig[UniqueConstraint]): + _is_uq = True + + @classmethod + def _register(cls) -> None: + _clsreg["unique_constraint"] = cls + + is_unique = True + + def __init__( + self, + is_metadata: bool, + impl: DefaultImpl, + const: UniqueConstraint, + ) -> None: + self.impl = impl + self.const = const + self.name = sqla_compat.constraint_name_or_none(const.name) + self._sig = tuple(sorted([col.name for col in const.columns])) + self._is_metadata = is_metadata + + @property + def column_names(self) -> Tuple[str, ...]: + return tuple([col.name for col in self.const.columns]) + + def _compare_to_reflected( + self, other: _constraint_sig[_C] + ) -> ComparisonResult: + assert self._is_metadata + metadata_obj = self + conn_obj = other + + assert is_uq_sig(conn_obj) + return self.impl.compare_unique_constraint( + metadata_obj.const, conn_obj.const + ) + + +class _ix_constraint_sig(_constraint_sig[Index]): + _is_index = True + + name: sqla_compat._ConstraintName + + @classmethod + def _register(cls) -> None: + _clsreg["index"] = cls + + def __init__( + self, is_metadata: bool, impl: DefaultImpl, const: Index + ) -> None: + self.impl = impl + self.const = const + self.name = const.name + self.is_unique = bool(const.unique) + self._is_metadata = is_metadata + + def _compare_to_reflected( + self, other: _constraint_sig[_C] + ) -> ComparisonResult: + assert self._is_metadata + metadata_obj = self + conn_obj = other + + assert is_index_sig(conn_obj) + return self.impl.compare_indexes(metadata_obj.const, conn_obj.const) + + @util.memoized_property + def has_expressions(self): + return sqla_compat.is_expression_index(self.const) + + @util.memoized_property + def column_names(self) -> Tuple[str, ...]: + return tuple([col.name for col in self.const.columns]) + + @util.memoized_property + def column_names_optional(self) -> Tuple[Optional[str], ...]: + return tuple( + [getattr(col, "name", None) for col in self.const.expressions] + ) + + @util.memoized_property + def is_named(self): + return True + + @util.memoized_property + def unnamed(self): + return (self.is_unique,) + self.column_names_optional + + +class _fk_constraint_sig(_constraint_sig[ForeignKeyConstraint]): + _is_fk = True + + @classmethod + def _register(cls) -> None: + _clsreg["foreign_key_constraint"] = cls + + def __init__( + self, + is_metadata: bool, + impl: DefaultImpl, + const: ForeignKeyConstraint, + ) -> None: + self._is_metadata = is_metadata + + self.impl = impl + self.const = const + + self.name = sqla_compat.constraint_name_or_none(const.name) + + ( + self.source_schema, + self.source_table, + self.source_columns, + self.target_schema, + self.target_table, + self.target_columns, + onupdate, + ondelete, + deferrable, + initially, + ) = sqla_compat._fk_spec(const) + + self._sig: Tuple[Any, ...] = ( + self.source_schema, + self.source_table, + tuple(self.source_columns), + self.target_schema, + self.target_table, + tuple(self.target_columns), + ) + ( + ( + (None if onupdate.lower() == "no action" else onupdate.lower()) + if onupdate + else None + ), + ( + (None if ondelete.lower() == "no action" else ondelete.lower()) + if ondelete + else None + ), + # convert initially + deferrable into one three-state value + ( + "initially_deferrable" + if initially and initially.lower() == "deferred" + else "deferrable" if deferrable else "not deferrable" + ), + ) + + @util.memoized_property + def unnamed_no_options(self): + return ( + self.source_schema, + self.source_table, + tuple(self.source_columns), + self.target_schema, + self.target_table, + tuple(self.target_columns), + ) + + +def is_index_sig(sig: _constraint_sig) -> TypeGuard[_ix_constraint_sig]: + return sig._is_index + + +def is_uq_sig(sig: _constraint_sig) -> TypeGuard[_uq_constraint_sig]: + return sig._is_uq + + +def is_fk_sig(sig: _constraint_sig) -> TypeGuard[_fk_constraint_sig]: + return sig._is_fk diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/base.py b/venv/lib/python3.12/site-packages/alembic/ddl/base.py new file mode 100644 index 00000000..30a3a15a --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/ddl/base.py @@ -0,0 +1,406 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +import functools +from typing import Any +from typing import Optional +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import exc +from sqlalchemy import Integer +from sqlalchemy import types as sqltypes +from sqlalchemy.ext.compiler import compiles +from sqlalchemy.schema import Column +from sqlalchemy.schema import DDLElement +from sqlalchemy.sql.elements import ColumnElement +from sqlalchemy.sql.elements import quoted_name +from sqlalchemy.sql.elements import TextClause +from sqlalchemy.sql.schema import FetchedValue + +from ..util.sqla_compat import _columns_for_constraint # noqa +from ..util.sqla_compat import _find_columns # noqa +from ..util.sqla_compat import _fk_spec # noqa +from ..util.sqla_compat import _is_type_bound # noqa +from ..util.sqla_compat import _table_for_constraint # noqa + +if TYPE_CHECKING: + + from sqlalchemy import Computed + from sqlalchemy import Identity + from sqlalchemy.sql.compiler import Compiled + from sqlalchemy.sql.compiler import DDLCompiler + from sqlalchemy.sql.type_api import TypeEngine + + from .impl import DefaultImpl + +_ServerDefaultType = Union[FetchedValue, str, TextClause, ColumnElement[Any]] + + +class AlterTable(DDLElement): + """Represent an ALTER TABLE statement. + + Only the string name and optional schema name of the table + is required, not a full Table object. + + """ + + def __init__( + self, + table_name: str, + schema: Optional[Union[quoted_name, str]] = None, + ) -> None: + self.table_name = table_name + self.schema = schema + + +class RenameTable(AlterTable): + def __init__( + self, + old_table_name: str, + new_table_name: Union[quoted_name, str], + schema: Optional[Union[quoted_name, str]] = None, + ) -> None: + super().__init__(old_table_name, schema=schema) + self.new_table_name = new_table_name + + +class AlterColumn(AlterTable): + def __init__( + self, + name: str, + column_name: str, + schema: Optional[str] = None, + existing_type: Optional[TypeEngine] = None, + existing_nullable: Optional[bool] = None, + existing_server_default: Optional[_ServerDefaultType] = None, + existing_comment: Optional[str] = None, + ) -> None: + super().__init__(name, schema=schema) + self.column_name = column_name + self.existing_type = ( + sqltypes.to_instance(existing_type) + if existing_type is not None + else None + ) + self.existing_nullable = existing_nullable + self.existing_server_default = existing_server_default + self.existing_comment = existing_comment + + +class ColumnNullable(AlterColumn): + def __init__( + self, name: str, column_name: str, nullable: bool, **kw + ) -> None: + super().__init__(name, column_name, **kw) + self.nullable = nullable + + +class ColumnType(AlterColumn): + def __init__( + self, name: str, column_name: str, type_: TypeEngine, **kw + ) -> None: + super().__init__(name, column_name, **kw) + self.type_ = sqltypes.to_instance(type_) + + +class ColumnName(AlterColumn): + def __init__( + self, name: str, column_name: str, newname: str, **kw + ) -> None: + super().__init__(name, column_name, **kw) + self.newname = newname + + +class ColumnDefault(AlterColumn): + def __init__( + self, + name: str, + column_name: str, + default: Optional[_ServerDefaultType], + **kw, + ) -> None: + super().__init__(name, column_name, **kw) + self.default = default + + +class ComputedColumnDefault(AlterColumn): + def __init__( + self, name: str, column_name: str, default: Optional[Computed], **kw + ) -> None: + super().__init__(name, column_name, **kw) + self.default = default + + +class IdentityColumnDefault(AlterColumn): + def __init__( + self, + name: str, + column_name: str, + default: Optional[Identity], + impl: DefaultImpl, + **kw, + ) -> None: + super().__init__(name, column_name, **kw) + self.default = default + self.impl = impl + + +class AddColumn(AlterTable): + def __init__( + self, + name: str, + column: Column[Any], + schema: Optional[Union[quoted_name, str]] = None, + if_not_exists: Optional[bool] = None, + inline_references: Optional[bool] = None, + inline_primary_key: Optional[bool] = None, + ) -> None: + super().__init__(name, schema=schema) + self.column = column + self.if_not_exists = if_not_exists + self.inline_references = inline_references + self.inline_primary_key = inline_primary_key + + +class DropColumn(AlterTable): + def __init__( + self, + name: str, + column: Column[Any], + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + ) -> None: + super().__init__(name, schema=schema) + self.column = column + self.if_exists = if_exists + + +class ColumnComment(AlterColumn): + def __init__( + self, name: str, column_name: str, comment: Optional[str], **kw + ) -> None: + super().__init__(name, column_name, **kw) + self.comment = comment + + +@compiles(RenameTable) +def visit_rename_table( + element: RenameTable, compiler: DDLCompiler, **kw +) -> str: + return "%s RENAME TO %s" % ( + alter_table(compiler, element.table_name, element.schema), + format_table_name(compiler, element.new_table_name, element.schema), + ) + + +@compiles(AddColumn) +def visit_add_column(element: AddColumn, compiler: DDLCompiler, **kw) -> str: + return "%s %s" % ( + alter_table(compiler, element.table_name, element.schema), + add_column( + compiler, + element.column, + if_not_exists=element.if_not_exists, + inline_references=element.inline_references, + inline_primary_key=element.inline_primary_key, + **kw, + ), + ) + + +@compiles(DropColumn) +def visit_drop_column(element: DropColumn, compiler: DDLCompiler, **kw) -> str: + return "%s %s" % ( + alter_table(compiler, element.table_name, element.schema), + drop_column( + compiler, element.column.name, if_exists=element.if_exists, **kw + ), + ) + + +@compiles(ColumnNullable) +def visit_column_nullable( + element: ColumnNullable, compiler: DDLCompiler, **kw +) -> str: + return "%s %s %s" % ( + alter_table(compiler, element.table_name, element.schema), + alter_column(compiler, element.column_name), + "DROP NOT NULL" if element.nullable else "SET NOT NULL", + ) + + +@compiles(ColumnType) +def visit_column_type(element: ColumnType, compiler: DDLCompiler, **kw) -> str: + return "%s %s %s" % ( + alter_table(compiler, element.table_name, element.schema), + alter_column(compiler, element.column_name), + "TYPE %s" % format_type(compiler, element.type_), + ) + + +@compiles(ColumnName) +def visit_column_name(element: ColumnName, compiler: DDLCompiler, **kw) -> str: + return "%s RENAME %s TO %s" % ( + alter_table(compiler, element.table_name, element.schema), + format_column_name(compiler, element.column_name), + format_column_name(compiler, element.newname), + ) + + +@compiles(ColumnDefault) +def visit_column_default( + element: ColumnDefault, compiler: DDLCompiler, **kw +) -> str: + return "%s %s %s" % ( + alter_table(compiler, element.table_name, element.schema), + alter_column(compiler, element.column_name), + ( + "SET DEFAULT %s" % format_server_default(compiler, element.default) + if element.default is not None + else "DROP DEFAULT" + ), + ) + + +@compiles(ComputedColumnDefault) +def visit_computed_column( + element: ComputedColumnDefault, compiler: DDLCompiler, **kw +): + raise exc.CompileError( + 'Adding or removing a "computed" construct, e.g. GENERATED ' + "ALWAYS AS, to or from an existing column is not supported." + ) + + +@compiles(IdentityColumnDefault) +def visit_identity_column( + element: IdentityColumnDefault, compiler: DDLCompiler, **kw +): + raise exc.CompileError( + 'Adding, removing or modifying an "identity" construct, ' + "e.g. GENERATED AS IDENTITY, to or from an existing " + "column is not supported in this dialect." + ) + + +def quote_dotted( + name: Union[quoted_name, str], quote: functools.partial +) -> Union[quoted_name, str]: + """quote the elements of a dotted name""" + + if isinstance(name, quoted_name): + return quote(name) + result = ".".join([quote(x) for x in name.split(".")]) + return result + + +def format_table_name( + compiler: Compiled, + name: Union[quoted_name, str], + schema: Optional[Union[quoted_name, str]], +) -> Union[quoted_name, str]: + quote = functools.partial(compiler.preparer.quote) + if schema: + return quote_dotted(schema, quote) + "." + quote(name) + else: + return quote(name) + + +def format_column_name( + compiler: DDLCompiler, name: Optional[Union[quoted_name, str]] +) -> Union[quoted_name, str]: + return compiler.preparer.quote(name) # type: ignore[arg-type] + + +def format_server_default( + compiler: DDLCompiler, + default: Optional[_ServerDefaultType], +) -> str: + # this can be updated to use compiler.render_default_string + # for SQLAlchemy 2.0 and above; not in 1.4 + default_str = compiler.get_column_default_string( + Column("x", Integer, server_default=default) + ) + assert default_str is not None + return default_str + + +def format_type(compiler: DDLCompiler, type_: TypeEngine) -> str: + return compiler.dialect.type_compiler.process(type_) + + +def alter_table( + compiler: DDLCompiler, + name: str, + schema: Optional[str], +) -> str: + return "ALTER TABLE %s" % format_table_name(compiler, name, schema) + + +def drop_column( + compiler: DDLCompiler, name: str, if_exists: Optional[bool] = None, **kw +) -> str: + return "DROP COLUMN %s%s" % ( + "IF EXISTS " if if_exists else "", + format_column_name(compiler, name), + ) + + +def alter_column(compiler: DDLCompiler, name: str) -> str: + return "ALTER COLUMN %s" % format_column_name(compiler, name) + + +def add_column( + compiler: DDLCompiler, + column: Column[Any], + if_not_exists: Optional[bool] = None, + inline_references: Optional[bool] = None, + inline_primary_key: Optional[bool] = None, + **kw, +) -> str: + text = "ADD COLUMN %s%s" % ( + "IF NOT EXISTS " if if_not_exists else "", + compiler.get_column_specification(column, **kw), + ) + + if inline_primary_key and column.primary_key: + text += " PRIMARY KEY" + + # Handle inline REFERENCES if requested + # Only render inline if there's exactly one foreign key AND the + # ForeignKeyConstraint is single-column, to avoid non-deterministic + # behavior with sets and to ensure proper syntax + if ( + inline_references + and len(column.foreign_keys) == 1 + and (fk := list(column.foreign_keys)[0]) + and fk.constraint is not None + and len(fk.constraint.columns) == 1 + ): + ref_col = fk.column + ref_table = ref_col.table + + # Format with proper quoting + if ref_table.schema: + table_name = "%s.%s" % ( + compiler.preparer.quote_schema(ref_table.schema), + compiler.preparer.quote(ref_table.name), + ) + else: + table_name = compiler.preparer.quote(ref_table.name) + + text += " REFERENCES %s (%s)" % ( + table_name, + compiler.preparer.quote(ref_col.name), + ) + + const = " ".join( + compiler.process(constraint) for constraint in column.constraints + ) + if const: + text += " " + const + + return text diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/impl.py b/venv/lib/python3.12/site-packages/alembic/ddl/impl.py new file mode 100644 index 00000000..964cd1f3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/ddl/impl.py @@ -0,0 +1,921 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +import logging +import re +from typing import Any +from typing import Callable +from typing import Dict +from typing import Iterable +from typing import List +from typing import Mapping +from typing import NamedTuple +from typing import Optional +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import cast +from sqlalchemy import Column +from sqlalchemy import MetaData +from sqlalchemy import PrimaryKeyConstraint +from sqlalchemy import schema +from sqlalchemy import String +from sqlalchemy import Table +from sqlalchemy import text + +from . import _autogen +from . import base +from ._autogen import _constraint_sig as _constraint_sig +from ._autogen import ComparisonResult as ComparisonResult +from .. import util +from ..util import sqla_compat + +if TYPE_CHECKING: + from typing import Literal + from typing import TextIO + + from sqlalchemy.engine import Connection + from sqlalchemy.engine import Dialect + from sqlalchemy.engine.cursor import CursorResult + from sqlalchemy.engine.interfaces import ReflectedForeignKeyConstraint + from sqlalchemy.engine.interfaces import ReflectedIndex + from sqlalchemy.engine.interfaces import ReflectedPrimaryKeyConstraint + from sqlalchemy.engine.interfaces import ReflectedUniqueConstraint + from sqlalchemy.engine.reflection import Inspector + from sqlalchemy.sql import ClauseElement + from sqlalchemy.sql import Executable + from sqlalchemy.sql.elements import quoted_name + from sqlalchemy.sql.schema import Constraint + from sqlalchemy.sql.schema import ForeignKeyConstraint + from sqlalchemy.sql.schema import Index + from sqlalchemy.sql.schema import UniqueConstraint + from sqlalchemy.sql.selectable import TableClause + from sqlalchemy.sql.type_api import TypeEngine + + from .base import _ServerDefaultType + from ..autogenerate.api import AutogenContext + from ..operations.batch import ApplyBatchImpl + from ..operations.batch import BatchOperationsImpl + + _ReflectedConstraint = ( + ReflectedForeignKeyConstraint + | ReflectedPrimaryKeyConstraint + | ReflectedIndex + | ReflectedUniqueConstraint + ) +log = logging.getLogger(__name__) + + +class ImplMeta(type): + def __init__( + cls, + classname: str, + bases: Tuple[Type[DefaultImpl]], + dict_: Dict[str, Any], + ): + newtype = type.__init__(cls, classname, bases, dict_) + if "__dialect__" in dict_: + _impls[dict_["__dialect__"]] = cls # type: ignore[assignment] + return newtype + + +_impls: Dict[str, Type[DefaultImpl]] = {} + + +class DefaultImpl(metaclass=ImplMeta): + """Provide the entrypoint for major migration operations, + including database-specific behavioral variances. + + While individual SQL/DDL constructs already provide + for database-specific implementations, variances here + allow for entirely different sequences of operations + to take place for a particular migration, such as + SQL Server's special 'IDENTITY INSERT' step for + bulk inserts. + + """ + + __dialect__ = "default" + + transactional_ddl = False + command_terminator = ";" + type_synonyms: Tuple[Set[str], ...] = ({"NUMERIC", "DECIMAL"},) + type_arg_extract: Sequence[str] = () + # These attributes are deprecated in SQLAlchemy via #10247. They need to + # be ignored to support older version that did not use dialect kwargs. + # They only apply to Oracle and are replaced by oracle_order, + # oracle_on_null + identity_attrs_ignore: Tuple[str, ...] = ("order", "on_null") + + def __init__( + self, + dialect: Dialect, + connection: Optional[Connection], + as_sql: bool, + transactional_ddl: Optional[bool], + output_buffer: Optional[TextIO], + context_opts: Dict[str, Any], + ) -> None: + self.dialect = dialect + self.connection = connection + self.as_sql = as_sql + self.literal_binds = context_opts.get("literal_binds", False) + + self.output_buffer = output_buffer + self.memo: dict = {} + self.context_opts = context_opts + if transactional_ddl is not None: + self.transactional_ddl = transactional_ddl + + if self.literal_binds: + if not self.as_sql: + raise util.CommandError( + "Can't use literal_binds setting without as_sql mode" + ) + + @classmethod + def get_by_dialect(cls, dialect: Dialect) -> Type[DefaultImpl]: + return _impls[dialect.name] + + def static_output(self, text: str) -> None: + assert self.output_buffer is not None + self.output_buffer.write(text + "\n\n") + self.output_buffer.flush() + + def version_table_impl( + self, + *, + version_table: str, + version_table_schema: Optional[str], + version_table_pk: bool, + **kw: Any, + ) -> Table: + """Generate a :class:`.Table` object which will be used as the + structure for the Alembic version table. + + Third party dialects may override this hook to provide an alternate + structure for this :class:`.Table`; requirements are only that it + be named based on the ``version_table`` parameter and contains + at least a single string-holding column named ``version_num``. + + .. versionadded:: 1.14 + + """ + vt = Table( + version_table, + MetaData(), + Column("version_num", String(32), nullable=False), + schema=version_table_schema, + ) + if version_table_pk: + vt.append_constraint( + PrimaryKeyConstraint( + "version_num", name=f"{version_table}_pkc" + ) + ) + + return vt + + def requires_recreate_in_batch( + self, batch_op: BatchOperationsImpl + ) -> bool: + """Return True if the given :class:`.BatchOperationsImpl` + would need the table to be recreated and copied in order to + proceed. + + Normally, only returns True on SQLite when operations other + than add_column are present. + + """ + return False + + def prep_table_for_batch( + self, batch_impl: ApplyBatchImpl, table: Table + ) -> None: + """perform any operations needed on a table before a new + one is created to replace it in batch mode. + + the PG dialect uses this to drop constraints on the table + before the new one uses those same names. + + """ + + @property + def bind(self) -> Optional[Connection]: + return self.connection + + def _exec( + self, + construct: Union[Executable, str], + execution_options: Optional[Mapping[str, Any]] = None, + multiparams: Optional[Sequence[Mapping[str, Any]]] = None, + params: Mapping[str, Any] = util.immutabledict(), + ) -> Optional[CursorResult]: + if isinstance(construct, str): + construct = text(construct) + if self.as_sql: + if multiparams is not None or params: + raise TypeError("SQL parameters not allowed with as_sql") + + compile_kw: dict[str, Any] + if self.literal_binds and not isinstance( + construct, schema.DDLElement + ): + compile_kw = dict(compile_kwargs={"literal_binds": True}) + else: + compile_kw = {} + + if TYPE_CHECKING: + assert isinstance(construct, ClauseElement) + compiled = construct.compile(dialect=self.dialect, **compile_kw) + self.static_output( + str(compiled).replace("\t", " ").strip() + + self.command_terminator + ) + return None + else: + conn = self.connection + assert conn is not None + if execution_options: + conn = conn.execution_options(**execution_options) + + if params and multiparams is not None: + raise TypeError( + "Can't send params and multiparams at the same time" + ) + + if multiparams: + return conn.execute(construct, multiparams) + else: + return conn.execute(construct, params) + + def execute( + self, + sql: Union[Executable, str], + execution_options: Optional[dict[str, Any]] = None, + ) -> None: + self._exec(sql, execution_options) + + def alter_column( + self, + table_name: str, + column_name: str, + *, + nullable: Optional[bool] = None, + server_default: Optional[ + Union[_ServerDefaultType, Literal[False]] + ] = False, + name: Optional[str] = None, + type_: Optional[TypeEngine] = None, + schema: Optional[str] = None, + autoincrement: Optional[bool] = None, + comment: Optional[Union[str, Literal[False]]] = False, + existing_comment: Optional[str] = None, + existing_type: Optional[TypeEngine] = None, + existing_server_default: Optional[ + Union[_ServerDefaultType, Literal[False]] + ] = None, + existing_nullable: Optional[bool] = None, + existing_autoincrement: Optional[bool] = None, + **kw: Any, + ) -> None: + if autoincrement is not None or existing_autoincrement is not None: + util.warn( + "autoincrement and existing_autoincrement " + "only make sense for MySQL", + stacklevel=3, + ) + if nullable is not None: + self._exec( + base.ColumnNullable( + table_name, + column_name, + nullable, + schema=schema, + existing_type=existing_type, + existing_server_default=existing_server_default, + existing_nullable=existing_nullable, + existing_comment=existing_comment, + ) + ) + if server_default is not False: + kw = {} + cls_: Type[ + Union[ + base.ComputedColumnDefault, + base.IdentityColumnDefault, + base.ColumnDefault, + ] + ] + if sqla_compat._server_default_is_computed( + server_default, existing_server_default + ): + cls_ = base.ComputedColumnDefault + elif sqla_compat._server_default_is_identity( + server_default, existing_server_default + ): + cls_ = base.IdentityColumnDefault + kw["impl"] = self + else: + cls_ = base.ColumnDefault + self._exec( + cls_( + table_name, + column_name, + server_default, # type:ignore[arg-type] + schema=schema, + existing_type=existing_type, + existing_server_default=existing_server_default, + existing_nullable=existing_nullable, + existing_comment=existing_comment, + **kw, + ) + ) + if type_ is not None: + self._exec( + base.ColumnType( + table_name, + column_name, + type_, + schema=schema, + existing_type=existing_type, + existing_server_default=existing_server_default, + existing_nullable=existing_nullable, + existing_comment=existing_comment, + ) + ) + + if comment is not False: + self._exec( + base.ColumnComment( + table_name, + column_name, + comment, + schema=schema, + existing_type=existing_type, + existing_server_default=existing_server_default, + existing_nullable=existing_nullable, + existing_comment=existing_comment, + ) + ) + + # do the new name last ;) + if name is not None: + self._exec( + base.ColumnName( + table_name, + column_name, + name, + schema=schema, + existing_type=existing_type, + existing_server_default=existing_server_default, + existing_nullable=existing_nullable, + ) + ) + + def add_column( + self, + table_name: str, + column: Column[Any], + *, + schema: Optional[Union[str, quoted_name]] = None, + if_not_exists: Optional[bool] = None, + inline_references: Optional[bool] = None, + inline_primary_key: Optional[bool] = None, + ) -> None: + self._exec( + base.AddColumn( + table_name, + column, + schema=schema, + if_not_exists=if_not_exists, + inline_references=inline_references, + inline_primary_key=inline_primary_key, + ) + ) + + def drop_column( + self, + table_name: str, + column: Column[Any], + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + **kw, + ) -> None: + self._exec( + base.DropColumn( + table_name, column, schema=schema, if_exists=if_exists + ) + ) + + def add_constraint(self, const: Any, **kw: Any) -> None: + if const._create_rule is None or const._create_rule(self): + if sqla_compat.sqla_2_1: + # this should be the default already + kw.setdefault("isolate_from_table", True) + self._exec(schema.AddConstraint(const, **kw)) + + def drop_constraint(self, const: Constraint, **kw: Any) -> None: + self._exec(schema.DropConstraint(const, **kw)) + + def rename_table( + self, + old_table_name: str, + new_table_name: Union[str, quoted_name], + schema: Optional[Union[str, quoted_name]] = None, + ) -> None: + self._exec( + base.RenameTable(old_table_name, new_table_name, schema=schema) + ) + + def create_table(self, table: Table, **kw: Any) -> None: + table.dispatch.before_create( + table, self.connection, checkfirst=False, _ddl_runner=self + ) + self._exec(schema.CreateTable(table, **kw)) + table.dispatch.after_create( + table, self.connection, checkfirst=False, _ddl_runner=self + ) + for index in table.indexes: + self._exec(schema.CreateIndex(index)) + + with_comment = ( + self.dialect.supports_comments and not self.dialect.inline_comments + ) + comment = table.comment + if comment and with_comment: + self.create_table_comment(table) + + for column in table.columns: + comment = column.comment + if comment and with_comment: + self.create_column_comment(column) + + def drop_table(self, table: Table, **kw: Any) -> None: + table.dispatch.before_drop( + table, self.connection, checkfirst=False, _ddl_runner=self + ) + self._exec(schema.DropTable(table, **kw)) + table.dispatch.after_drop( + table, self.connection, checkfirst=False, _ddl_runner=self + ) + + def create_index(self, index: Index, **kw: Any) -> None: + self._exec(schema.CreateIndex(index, **kw)) + + def create_table_comment(self, table: Table) -> None: + self._exec(schema.SetTableComment(table)) + + def drop_table_comment(self, table: Table) -> None: + self._exec(schema.DropTableComment(table)) + + def create_column_comment(self, column: Column[Any]) -> None: + self._exec(schema.SetColumnComment(column)) + + def drop_index(self, index: Index, **kw: Any) -> None: + self._exec(schema.DropIndex(index, **kw)) + + def bulk_insert( + self, + table: Union[TableClause, Table], + rows: List[dict], + multiinsert: bool = True, + ) -> None: + if not isinstance(rows, list): + raise TypeError("List expected") + elif rows and not isinstance(rows[0], dict): + raise TypeError("List of dictionaries expected") + if self.as_sql: + for row in rows: + self._exec( + table.insert() + .inline() + .values( + **{ + k: ( + sqla_compat._literal_bindparam( + k, v, type_=table.c[k].type + ) + if not isinstance( + v, sqla_compat._literal_bindparam + ) + else v + ) + for k, v in row.items() + } + ) + ) + else: + if rows: + if multiinsert: + self._exec(table.insert().inline(), multiparams=rows) + else: + for row in rows: + self._exec(table.insert().inline().values(**row)) + + def _tokenize_column_type(self, column: Column) -> Params: + definition: str + definition = self.dialect.type_compiler.process(column.type).lower() + + # tokenize the SQLAlchemy-generated version of a type, so that + # the two can be compared. + # + # examples: + # NUMERIC(10, 5) + # TIMESTAMP WITH TIMEZONE + # INTEGER UNSIGNED + # INTEGER (10) UNSIGNED + # INTEGER(10) UNSIGNED + # varchar character set utf8 + # + + tokens: List[str] = re.findall(r"[\w\-_]+|\(.+?\)", definition) + + term_tokens: List[str] = [] + paren_term = None + + for token in tokens: + if re.match(r"^\(.*\)$", token): + paren_term = token + else: + term_tokens.append(token) + + params = Params(term_tokens[0], term_tokens[1:], [], {}) + + if paren_term: + term: str + for term in re.findall("[^(),]+", paren_term): + if "=" in term: + key, val = term.split("=") + params.kwargs[key.strip()] = val.strip() + else: + params.args.append(term.strip()) + + return params + + def _column_types_match( + self, inspector_params: Params, metadata_params: Params + ) -> bool: + if inspector_params.token0 == metadata_params.token0: + return True + + synonyms = [{t.lower() for t in batch} for batch in self.type_synonyms] + inspector_all_terms = " ".join( + [inspector_params.token0] + inspector_params.tokens + ) + metadata_all_terms = " ".join( + [metadata_params.token0] + metadata_params.tokens + ) + + for batch in synonyms: + if {inspector_all_terms, metadata_all_terms}.issubset(batch) or { + inspector_params.token0, + metadata_params.token0, + }.issubset(batch): + return True + return False + + def _column_args_match( + self, inspected_params: Params, meta_params: Params + ) -> bool: + """We want to compare column parameters. However, we only want + to compare parameters that are set. If they both have `collation`, + we want to make sure they are the same. However, if only one + specifies it, dont flag it for being less specific + """ + + if ( + len(meta_params.tokens) == len(inspected_params.tokens) + and meta_params.tokens != inspected_params.tokens + ): + return False + + if ( + len(meta_params.args) == len(inspected_params.args) + and meta_params.args != inspected_params.args + ): + return False + + insp = " ".join(inspected_params.tokens).lower() + meta = " ".join(meta_params.tokens).lower() + + for reg in self.type_arg_extract: + mi = re.search(reg, insp) + mm = re.search(reg, meta) + + if mi and mm and mi.group(1) != mm.group(1): + return False + + return True + + def compare_type( + self, inspector_column: Column[Any], metadata_column: Column + ) -> bool: + """Returns True if there ARE differences between the types of the two + columns. Takes impl.type_synonyms into account between retrospected + and metadata types + """ + inspector_params = self._tokenize_column_type(inspector_column) + metadata_params = self._tokenize_column_type(metadata_column) + + if not self._column_types_match(inspector_params, metadata_params): + return True + if not self._column_args_match(inspector_params, metadata_params): + return True + return False + + def compare_server_default( + self, + inspector_column, + metadata_column, + rendered_metadata_default, + rendered_inspector_default, + ): + return rendered_inspector_default != rendered_metadata_default + + def correct_for_autogen_constraints( + self, + conn_uniques: Set[UniqueConstraint], + conn_indexes: Set[Index], + metadata_unique_constraints: Set[UniqueConstraint], + metadata_indexes: Set[Index], + ) -> None: + pass + + def cast_for_batch_migrate(self, existing, existing_transfer, new_type): + if existing.type._type_affinity is not new_type._type_affinity: + existing_transfer["expr"] = cast( + existing_transfer["expr"], new_type + ) + + def render_ddl_sql_expr( + self, expr: ClauseElement, is_server_default: bool = False, **kw: Any + ) -> str: + """Render a SQL expression that is typically a server default, + index expression, etc. + + """ + + compile_kw = {"literal_binds": True, "include_table": False} + + return str( + expr.compile(dialect=self.dialect, compile_kwargs=compile_kw) + ) + + def _compat_autogen_column_reflect(self, inspector: Inspector) -> Callable: + return self.autogen_column_reflect + + def correct_for_autogen_foreignkeys( + self, + conn_fks: Set[ForeignKeyConstraint], + metadata_fks: Set[ForeignKeyConstraint], + ) -> None: + pass + + def autogen_column_reflect(self, inspector, table, column_info): + """A hook that is attached to the 'column_reflect' event for when + a Table is reflected from the database during the autogenerate + process. + + Dialects can elect to modify the information gathered here. + + """ + + def start_migrations(self) -> None: + """A hook called when :meth:`.EnvironmentContext.run_migrations` + is called. + + Implementations can set up per-migration-run state here. + + """ + + def emit_begin(self) -> None: + """Emit the string ``BEGIN``, or the backend-specific + equivalent, on the current connection context. + + This is used in offline mode and typically + via :meth:`.EnvironmentContext.begin_transaction`. + + """ + self.static_output("BEGIN" + self.command_terminator) + + def emit_commit(self) -> None: + """Emit the string ``COMMIT``, or the backend-specific + equivalent, on the current connection context. + + This is used in offline mode and typically + via :meth:`.EnvironmentContext.begin_transaction`. + + """ + self.static_output("COMMIT" + self.command_terminator) + + def render_type( + self, type_obj: TypeEngine, autogen_context: AutogenContext + ) -> Union[str, Literal[False]]: + return False + + def _compare_identity_default(self, metadata_identity, inspector_identity): + # ignored contains the attributes that were not considered + # because assumed to their default values in the db. + diff, ignored = _compare_identity_options( + metadata_identity, + inspector_identity, + schema.Identity(), + skip={"always"}, + ) + + meta_always = getattr(metadata_identity, "always", None) + inspector_always = getattr(inspector_identity, "always", None) + # None and False are the same in this comparison + if bool(meta_always) != bool(inspector_always): + diff.add("always") + + diff.difference_update(self.identity_attrs_ignore) + + # returns 3 values: + return ( + # different identity attributes + diff, + # ignored identity attributes + ignored, + # if the two identity should be considered different + bool(diff) or bool(metadata_identity) != bool(inspector_identity), + ) + + def _compare_index_unique( + self, metadata_index: Index, reflected_index: Index + ) -> Optional[str]: + conn_unique = bool(reflected_index.unique) + meta_unique = bool(metadata_index.unique) + if conn_unique != meta_unique: + return f"unique={conn_unique} to unique={meta_unique}" + else: + return None + + def _create_metadata_constraint_sig( + self, constraint: _autogen._C, **opts: Any + ) -> _constraint_sig[_autogen._C]: + return _constraint_sig.from_constraint(True, self, constraint, **opts) + + def _create_reflected_constraint_sig( + self, constraint: _autogen._C, **opts: Any + ) -> _constraint_sig[_autogen._C]: + return _constraint_sig.from_constraint(False, self, constraint, **opts) + + def compare_indexes( + self, + metadata_index: Index, + reflected_index: Index, + ) -> ComparisonResult: + """Compare two indexes by comparing the signature generated by + ``create_index_sig``. + + This method returns a ``ComparisonResult``. + """ + msg: List[str] = [] + unique_msg = self._compare_index_unique( + metadata_index, reflected_index + ) + if unique_msg: + msg.append(unique_msg) + m_sig = self._create_metadata_constraint_sig(metadata_index) + r_sig = self._create_reflected_constraint_sig(reflected_index) + + assert _autogen.is_index_sig(m_sig) + assert _autogen.is_index_sig(r_sig) + + # The assumption is that the index have no expression + for sig in m_sig, r_sig: + if sig.has_expressions: + log.warning( + "Generating approximate signature for index %s. " + "The dialect " + "implementation should either skip expression indexes " + "or provide a custom implementation.", + sig.const, + ) + + if m_sig.column_names != r_sig.column_names: + msg.append( + f"expression {r_sig.column_names} to {m_sig.column_names}" + ) + + if msg: + return ComparisonResult.Different(msg) + else: + return ComparisonResult.Equal() + + def compare_unique_constraint( + self, + metadata_constraint: UniqueConstraint, + reflected_constraint: UniqueConstraint, + ) -> ComparisonResult: + """Compare two unique constraints by comparing the two signatures. + + The arguments are two tuples that contain the unique constraint and + the signatures generated by ``create_unique_constraint_sig``. + + This method returns a ``ComparisonResult``. + """ + metadata_tup = self._create_metadata_constraint_sig( + metadata_constraint + ) + reflected_tup = self._create_reflected_constraint_sig( + reflected_constraint + ) + + meta_sig = metadata_tup.unnamed + conn_sig = reflected_tup.unnamed + if conn_sig != meta_sig: + return ComparisonResult.Different( + f"expression {conn_sig} to {meta_sig}" + ) + else: + return ComparisonResult.Equal() + + def _skip_functional_indexes(self, metadata_indexes, conn_indexes): + conn_indexes_by_name = {c.name: c for c in conn_indexes} + + for idx in list(metadata_indexes): + if idx.name in conn_indexes_by_name: + continue + iex = sqla_compat.is_expression_index(idx) + if iex: + util.warn( + "autogenerate skipping metadata-specified " + "expression-based index " + f"{idx.name!r}; dialect {self.__dialect__!r} under " + f"SQLAlchemy {sqla_compat.sqlalchemy_version} can't " + "reflect these indexes so they can't be compared" + ) + metadata_indexes.discard(idx) + + def adjust_reflected_dialect_options( + self, reflected_object: _ReflectedConstraint, kind: str + ) -> Dict[str, Any]: + return reflected_object.get("dialect_options", {}) # type: ignore[return-value] # noqa: E501 + + +class Params(NamedTuple): + token0: str + tokens: List[str] + args: List[str] + kwargs: Dict[str, str] + + +def _compare_identity_options( + metadata_io: Union[schema.Identity, schema.Sequence, None], + inspector_io: Union[schema.Identity, schema.Sequence, None], + default_io: Union[schema.Identity, schema.Sequence], + skip: Set[str], +): + # this can be used for identity or sequence compare. + # default_io is an instance of IdentityOption with all attributes to the + # default value. + meta_d = sqla_compat._get_identity_options_dict(metadata_io) + insp_d = sqla_compat._get_identity_options_dict(inspector_io) + + diff = set() + ignored_attr = set() + + def check_dicts( + meta_dict: Mapping[str, Any], + insp_dict: Mapping[str, Any], + default_dict: Mapping[str, Any], + attrs: Iterable[str], + ): + for attr in set(attrs).difference(skip): + meta_value = meta_dict.get(attr) + insp_value = insp_dict.get(attr) + if insp_value != meta_value: + default_value = default_dict.get(attr) + if meta_value == default_value: + ignored_attr.add(attr) + else: + diff.add(attr) + + check_dicts( + meta_d, + insp_d, + sqla_compat._get_identity_options_dict(default_io), + set(meta_d).union(insp_d), + ) + if sqla_compat.identity_has_dialect_kwargs: + assert hasattr(default_io, "dialect_kwargs") + # use only the dialect kwargs in inspector_io since metadata_io + # can have options for many backends + check_dicts( + getattr(metadata_io, "dialect_kwargs", {}), + getattr(inspector_io, "dialect_kwargs", {}), + default_io.dialect_kwargs, + getattr(inspector_io, "dialect_kwargs", {}), + ) + + return diff, ignored_attr diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/mssql.py b/venv/lib/python3.12/site-packages/alembic/ddl/mssql.py new file mode 100644 index 00000000..91cd9e42 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/ddl/mssql.py @@ -0,0 +1,523 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +import re +from typing import Any +from typing import Dict +from typing import List +from typing import Optional +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import types as sqltypes +from sqlalchemy.schema import Column +from sqlalchemy.schema import CreateIndex +from sqlalchemy.sql.base import Executable +from sqlalchemy.sql.elements import ClauseElement + +from .base import AddColumn +from .base import alter_column +from .base import alter_table +from .base import ColumnComment +from .base import ColumnDefault +from .base import ColumnName +from .base import ColumnNullable +from .base import ColumnType +from .base import format_column_name +from .base import format_server_default +from .base import format_table_name +from .base import format_type +from .base import RenameTable +from .impl import DefaultImpl +from .. import util +from ..util import sqla_compat +from ..util.sqla_compat import compiles + +if TYPE_CHECKING: + from typing import Literal + + from sqlalchemy.dialects.mssql.base import MSDDLCompiler + from sqlalchemy.dialects.mssql.base import MSSQLCompiler + from sqlalchemy.engine.cursor import CursorResult + from sqlalchemy.sql.schema import Index + from sqlalchemy.sql.schema import Table + from sqlalchemy.sql.selectable import TableClause + from sqlalchemy.sql.type_api import TypeEngine + + from .base import _ServerDefaultType + from .impl import _ReflectedConstraint + + +class MSSQLImpl(DefaultImpl): + __dialect__ = "mssql" + transactional_ddl = True + batch_separator = "GO" + + type_synonyms = DefaultImpl.type_synonyms + ({"VARCHAR", "NVARCHAR"},) + identity_attrs_ignore = DefaultImpl.identity_attrs_ignore + ( + "minvalue", + "maxvalue", + "nominvalue", + "nomaxvalue", + "cycle", + "cache", + ) + + def __init__(self, *arg, **kw) -> None: + super().__init__(*arg, **kw) + self.batch_separator = self.context_opts.get( + "mssql_batch_separator", self.batch_separator + ) + + def _exec(self, construct: Any, *args, **kw) -> Optional[CursorResult]: + result = super()._exec(construct, *args, **kw) + if self.as_sql and self.batch_separator: + self.static_output(self.batch_separator) + return result + + def emit_begin(self) -> None: + self.static_output("BEGIN TRANSACTION" + self.command_terminator) + + def emit_commit(self) -> None: + super().emit_commit() + if self.as_sql and self.batch_separator: + self.static_output(self.batch_separator) + + def alter_column( + self, + table_name: str, + column_name: str, + *, + nullable: Optional[bool] = None, + server_default: Optional[ + Union[_ServerDefaultType, Literal[False]] + ] = False, + name: Optional[str] = None, + type_: Optional[TypeEngine] = None, + schema: Optional[str] = None, + existing_type: Optional[TypeEngine] = None, + existing_server_default: Union[ + _ServerDefaultType, Literal[False], None + ] = None, + existing_nullable: Optional[bool] = None, + **kw: Any, + ) -> None: + if nullable is not None: + if type_ is not None: + # the NULL/NOT NULL alter will handle + # the type alteration + existing_type = type_ + type_ = None + elif existing_type is None: + raise util.CommandError( + "MS-SQL ALTER COLUMN operations " + "with NULL or NOT NULL require the " + "existing_type or a new type_ be passed." + ) + elif existing_nullable is not None and type_ is not None: + nullable = existing_nullable + + # the NULL/NOT NULL alter will handle + # the type alteration + existing_type = type_ + type_ = None + + elif type_ is not None: + util.warn( + "MS-SQL ALTER COLUMN operations that specify type_= " + "should also specify a nullable= or " + "existing_nullable= argument to avoid implicit conversion " + "of NOT NULL columns to NULL." + ) + + used_default = False + if sqla_compat._server_default_is_identity( + server_default, existing_server_default + ) or sqla_compat._server_default_is_computed( + server_default, existing_server_default + ): + used_default = True + kw["server_default"] = server_default + kw["existing_server_default"] = existing_server_default + + # drop existing default constraints before changing type + # or default, see issue #1744 + if ( + server_default is not False + and used_default is False + and ( + existing_server_default is not False or server_default is None + ) + ): + self._exec( + _ExecDropConstraint( + table_name, + column_name, + "sys.default_constraints", + schema, + ) + ) + + # TODO: see why these two alter_columns can't be called + # at once. joining them works but some of the mssql tests + # seem to expect something different + super().alter_column( + table_name, + column_name, + nullable=nullable, + type_=type_, + schema=schema, + existing_type=existing_type, + existing_nullable=existing_nullable, + **kw, + ) + + if server_default is not False and used_default is False: + if server_default is not None: + super().alter_column( + table_name, + column_name, + schema=schema, + server_default=server_default, + ) + + if name is not None: + super().alter_column( + table_name, column_name, schema=schema, name=name + ) + + def create_index(self, index: Index, **kw: Any) -> None: + # this likely defaults to None if not present, so get() + # should normally not return the default value. being + # defensive in any case + mssql_include = index.kwargs.get("mssql_include", None) or () + assert index.table is not None + for col in mssql_include: + if col not in index.table.c: + index.table.append_column(Column(col, sqltypes.NullType)) + self._exec(CreateIndex(index, **kw)) + + def bulk_insert( # type:ignore[override] + self, table: Union[TableClause, Table], rows: List[dict], **kw: Any + ) -> None: + if self.as_sql: + self._exec( + "SET IDENTITY_INSERT %s ON" + % self.dialect.identifier_preparer.format_table(table) + ) + super().bulk_insert(table, rows, **kw) + self._exec( + "SET IDENTITY_INSERT %s OFF" + % self.dialect.identifier_preparer.format_table(table) + ) + else: + super().bulk_insert(table, rows, **kw) + + def drop_column( + self, + table_name: str, + column: Column[Any], + *, + schema: Optional[str] = None, + **kw, + ) -> None: + drop_default = kw.pop("mssql_drop_default", False) + if drop_default: + self._exec( + _ExecDropConstraint( + table_name, column, "sys.default_constraints", schema + ) + ) + drop_check = kw.pop("mssql_drop_check", False) + if drop_check: + self._exec( + _ExecDropConstraint( + table_name, column, "sys.check_constraints", schema + ) + ) + drop_fks = kw.pop("mssql_drop_foreign_key", False) + if drop_fks: + self._exec(_ExecDropFKConstraint(table_name, column, schema)) + super().drop_column(table_name, column, schema=schema, **kw) + + def compare_server_default( + self, + inspector_column, + metadata_column, + rendered_metadata_default, + rendered_inspector_default, + ): + if rendered_metadata_default is not None: + rendered_metadata_default = re.sub( + r"[\(\) \"\']", "", rendered_metadata_default + ) + + if rendered_inspector_default is not None: + # SQL Server collapses whitespace and adds arbitrary parenthesis + # within expressions. our only option is collapse all of it + + rendered_inspector_default = re.sub( + r"[\(\) \"\']", "", rendered_inspector_default + ) + + return rendered_inspector_default != rendered_metadata_default + + def _compare_identity_default(self, metadata_identity, inspector_identity): + diff, ignored, is_alter = super()._compare_identity_default( + metadata_identity, inspector_identity + ) + + if ( + metadata_identity is None + and inspector_identity is not None + and not diff + and inspector_identity.column is not None + and inspector_identity.column.primary_key + ): + # mssql reflect primary keys with autoincrement as identity + # columns. if no different attributes are present ignore them + is_alter = False + + return diff, ignored, is_alter + + def adjust_reflected_dialect_options( + self, reflected_object: _ReflectedConstraint, kind: str + ) -> Dict[str, Any]: + options: Dict[str, Any] + options = reflected_object.get("dialect_options", {}).copy() # type: ignore[attr-defined] # noqa: E501 + if not options.get("mssql_include"): + options.pop("mssql_include", None) + if not options.get("mssql_clustered"): + options.pop("mssql_clustered", None) + return options + + +class _ExecDropConstraint(Executable, ClauseElement): + inherit_cache = False + + def __init__( + self, + tname: str, + colname: Union[Column[Any], str], + type_: str, + schema: Optional[str], + ) -> None: + self.tname = tname + self.colname = colname + self.type_ = type_ + self.schema = schema + + +class _ExecDropFKConstraint(Executable, ClauseElement): + inherit_cache = False + + def __init__( + self, tname: str, colname: Column[Any], schema: Optional[str] + ) -> None: + self.tname = tname + self.colname = colname + self.schema = schema + + +@compiles(_ExecDropConstraint, "mssql") +def _exec_drop_col_constraint( + element: _ExecDropConstraint, compiler: MSSQLCompiler, **kw +) -> str: + schema, tname, colname, type_ = ( + element.schema, + element.tname, + element.colname, + element.type_, + ) + # from http://www.mssqltips.com/sqlservertip/1425/\ + # working-with-default-constraints-in-sql-server/ + return """declare @const_name varchar(256) +select @const_name = QUOTENAME([name]) from %(type)s +where parent_object_id = object_id('%(schema_dot)s%(tname)s') +and col_name(parent_object_id, parent_column_id) = '%(colname)s' +exec('alter table %(tname_quoted)s drop constraint ' + @const_name)""" % { + "type": type_, + "tname": tname, + "colname": colname, + "tname_quoted": format_table_name(compiler, tname, schema), + "schema_dot": schema + "." if schema else "", + } + + +@compiles(_ExecDropFKConstraint, "mssql") +def _exec_drop_col_fk_constraint( + element: _ExecDropFKConstraint, compiler: MSSQLCompiler, **kw +) -> str: + schema, tname, colname = element.schema, element.tname, element.colname + + return """declare @const_name varchar(256) +select @const_name = QUOTENAME([name]) from +sys.foreign_keys fk join sys.foreign_key_columns fkc +on fk.object_id=fkc.constraint_object_id +where fkc.parent_object_id = object_id('%(schema_dot)s%(tname)s') +and col_name(fkc.parent_object_id, fkc.parent_column_id) = '%(colname)s' +exec('alter table %(tname_quoted)s drop constraint ' + @const_name)""" % { + "tname": tname, + "colname": colname, + "tname_quoted": format_table_name(compiler, tname, schema), + "schema_dot": schema + "." if schema else "", + } + + +@compiles(AddColumn, "mssql") +def visit_add_column(element: AddColumn, compiler: MSDDLCompiler, **kw) -> str: + return "%s %s" % ( + alter_table(compiler, element.table_name, element.schema), + mssql_add_column(compiler, element.column, **kw), + ) + + +def mssql_add_column( + compiler: MSDDLCompiler, column: Column[Any], **kw +) -> str: + return "ADD %s" % compiler.get_column_specification(column, **kw) + + +@compiles(ColumnNullable, "mssql") +def visit_column_nullable( + element: ColumnNullable, compiler: MSDDLCompiler, **kw +) -> str: + return "%s %s %s %s" % ( + alter_table(compiler, element.table_name, element.schema), + alter_column(compiler, element.column_name), + format_type(compiler, element.existing_type), # type: ignore[arg-type] + "NULL" if element.nullable else "NOT NULL", + ) + + +@compiles(ColumnDefault, "mssql") +def visit_column_default( + element: ColumnDefault, compiler: MSDDLCompiler, **kw +) -> str: + # TODO: there can also be a named constraint + # with ADD CONSTRAINT here + return "%s ADD DEFAULT %s FOR %s" % ( + alter_table(compiler, element.table_name, element.schema), + format_server_default(compiler, element.default), + format_column_name(compiler, element.column_name), + ) + + +@compiles(ColumnName, "mssql") +def visit_rename_column( + element: ColumnName, compiler: MSDDLCompiler, **kw +) -> str: + return "EXEC sp_rename '%s.%s', %s, 'COLUMN'" % ( + format_table_name(compiler, element.table_name, element.schema), + format_column_name(compiler, element.column_name), + format_column_name(compiler, element.newname), + ) + + +@compiles(ColumnType, "mssql") +def visit_column_type( + element: ColumnType, compiler: MSDDLCompiler, **kw +) -> str: + return "%s %s %s" % ( + alter_table(compiler, element.table_name, element.schema), + alter_column(compiler, element.column_name), + format_type(compiler, element.type_), + ) + + +@compiles(RenameTable, "mssql") +def visit_rename_table( + element: RenameTable, compiler: MSDDLCompiler, **kw +) -> str: + return "EXEC sp_rename '%s', %s" % ( + format_table_name(compiler, element.table_name, element.schema), + format_table_name(compiler, element.new_table_name, None), + ) + + +def _add_column_comment( + compiler: MSDDLCompiler, + schema: Optional[str], + tname: str, + cname: str, + comment: str, +) -> str: + schema_name = schema if schema else compiler.dialect.default_schema_name + assert schema_name + return ( + "exec sp_addextendedproperty 'MS_Description', {}, " + "'schema', {}, 'table', {}, 'column', {}".format( + compiler.sql_compiler.render_literal_value( + comment, sqltypes.NVARCHAR() + ), + compiler.preparer.quote_schema(schema_name), + compiler.preparer.quote(tname), + compiler.preparer.quote(cname), + ) + ) + + +def _update_column_comment( + compiler: MSDDLCompiler, + schema: Optional[str], + tname: str, + cname: str, + comment: str, +) -> str: + schema_name = schema if schema else compiler.dialect.default_schema_name + assert schema_name + return ( + "exec sp_updateextendedproperty 'MS_Description', {}, " + "'schema', {}, 'table', {}, 'column', {}".format( + compiler.sql_compiler.render_literal_value( + comment, sqltypes.NVARCHAR() + ), + compiler.preparer.quote_schema(schema_name), + compiler.preparer.quote(tname), + compiler.preparer.quote(cname), + ) + ) + + +def _drop_column_comment( + compiler: MSDDLCompiler, schema: Optional[str], tname: str, cname: str +) -> str: + schema_name = schema if schema else compiler.dialect.default_schema_name + assert schema_name + return ( + "exec sp_dropextendedproperty 'MS_Description', " + "'schema', {}, 'table', {}, 'column', {}".format( + compiler.preparer.quote_schema(schema_name), + compiler.preparer.quote(tname), + compiler.preparer.quote(cname), + ) + ) + + +@compiles(ColumnComment, "mssql") +def visit_column_comment( + element: ColumnComment, compiler: MSDDLCompiler, **kw: Any +) -> str: + if element.comment is not None: + if element.existing_comment is not None: + return _update_column_comment( + compiler, + element.schema, + element.table_name, + element.column_name, + element.comment, + ) + else: + return _add_column_comment( + compiler, + element.schema, + element.table_name, + element.column_name, + element.comment, + ) + else: + return _drop_column_comment( + compiler, element.schema, element.table_name, element.column_name + ) diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/mysql.py b/venv/lib/python3.12/site-packages/alembic/ddl/mysql.py new file mode 100644 index 00000000..27f808b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/ddl/mysql.py @@ -0,0 +1,526 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +import re +from typing import Any +from typing import Optional +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import schema +from sqlalchemy import types as sqltypes +from sqlalchemy.sql import elements +from sqlalchemy.sql import functions +from sqlalchemy.sql import operators + +from .base import alter_table +from .base import AlterColumn +from .base import ColumnDefault +from .base import ColumnName +from .base import ColumnNullable +from .base import ColumnType +from .base import format_column_name +from .base import format_server_default +from .impl import DefaultImpl +from .. import util +from ..util import sqla_compat +from ..util.sqla_compat import _is_type_bound +from ..util.sqla_compat import compiles + +if TYPE_CHECKING: + from typing import Literal + + from sqlalchemy.dialects.mysql.base import MySQLDDLCompiler + from sqlalchemy.sql.ddl import DropConstraint + from sqlalchemy.sql.elements import ClauseElement + from sqlalchemy.sql.schema import Constraint + from sqlalchemy.sql.type_api import TypeEngine + + from .base import _ServerDefaultType + + +class MySQLImpl(DefaultImpl): + __dialect__ = "mysql" + + transactional_ddl = False + type_synonyms = DefaultImpl.type_synonyms + ( + {"BOOL", "TINYINT"}, + {"JSON", "LONGTEXT"}, + ) + type_arg_extract = [r"character set ([\w\-_]+)", r"collate ([\w\-_]+)"] + + def render_ddl_sql_expr( + self, + expr: ClauseElement, + is_server_default: bool = False, + is_index: bool = False, + **kw: Any, + ) -> str: + # apply Grouping to index expressions; + # see https://github.com/sqlalchemy/sqlalchemy/blob/ + # 36da2eaf3e23269f2cf28420ae73674beafd0661/ + # lib/sqlalchemy/dialects/mysql/base.py#L2191 + if is_index and ( + isinstance(expr, elements.BinaryExpression) + or ( + isinstance(expr, elements.UnaryExpression) + and expr.modifier not in (operators.desc_op, operators.asc_op) + ) + or isinstance(expr, functions.FunctionElement) + ): + expr = elements.Grouping(expr) + + return super().render_ddl_sql_expr( + expr, is_server_default=is_server_default, is_index=is_index, **kw + ) + + def alter_column( + self, + table_name: str, + column_name: str, + *, + nullable: Optional[bool] = None, + server_default: Optional[ + Union[_ServerDefaultType, Literal[False]] + ] = False, + name: Optional[str] = None, + type_: Optional[TypeEngine] = None, + schema: Optional[str] = None, + existing_type: Optional[TypeEngine] = None, + existing_server_default: Optional[ + Union[_ServerDefaultType, Literal[False]] + ] = None, + existing_nullable: Optional[bool] = None, + autoincrement: Optional[bool] = None, + existing_autoincrement: Optional[bool] = None, + comment: Optional[Union[str, Literal[False]]] = False, + existing_comment: Optional[str] = None, + **kw: Any, + ) -> None: + if sqla_compat._server_default_is_identity( + server_default, existing_server_default + ) or sqla_compat._server_default_is_computed( + server_default, existing_server_default + ): + # modifying computed or identity columns is not supported + # the default will raise + super().alter_column( + table_name, + column_name, + nullable=nullable, + type_=type_, + schema=schema, + existing_type=existing_type, + existing_nullable=existing_nullable, + server_default=server_default, + existing_server_default=existing_server_default, + **kw, + ) + if name is not None or self._is_mysql_allowed_functional_default( + type_ if type_ is not None else existing_type, server_default + ): + self._exec( + MySQLChangeColumn( + table_name, + column_name, + schema=schema, + newname=name if name is not None else column_name, + nullable=( + nullable + if nullable is not None + else ( + existing_nullable + if existing_nullable is not None + else True + ) + ), + type_=type_ if type_ is not None else existing_type, + default=( + server_default + if server_default is not False + else existing_server_default + ), + autoincrement=( + autoincrement + if autoincrement is not None + else existing_autoincrement + ), + comment=( + comment if comment is not False else existing_comment + ), + ) + ) + elif ( + nullable is not None + or type_ is not None + or autoincrement is not None + or comment is not False + ): + self._exec( + MySQLModifyColumn( + table_name, + column_name, + schema=schema, + newname=name if name is not None else column_name, + nullable=( + nullable + if nullable is not None + else ( + existing_nullable + if existing_nullable is not None + else True + ) + ), + type_=type_ if type_ is not None else existing_type, + default=( + server_default + if server_default is not False + else existing_server_default + ), + autoincrement=( + autoincrement + if autoincrement is not None + else existing_autoincrement + ), + comment=( + comment if comment is not False else existing_comment + ), + ) + ) + elif server_default is not False: + self._exec( + MySQLAlterDefault( + table_name, column_name, server_default, schema=schema + ) + ) + + def drop_constraint( + self, + const: Constraint, + **kw: Any, + ) -> None: + if isinstance(const, schema.CheckConstraint) and _is_type_bound(const): + return + + super().drop_constraint(const) + + def _is_mysql_allowed_functional_default( + self, + type_: Optional[TypeEngine], + server_default: Optional[Union[_ServerDefaultType, Literal[False]]], + ) -> bool: + return ( + type_ is not None + and type_._type_affinity is sqltypes.DateTime + and server_default is not None + ) + + def compare_server_default( + self, + inspector_column, + metadata_column, + rendered_metadata_default, + rendered_inspector_default, + ): + # partially a workaround for SQLAlchemy issue #3023; if the + # column were created without "NOT NULL", MySQL may have added + # an implicit default of '0' which we need to skip + # TODO: this is not really covered anymore ? + if ( + metadata_column.type._type_affinity is sqltypes.Integer + and inspector_column.primary_key + and not inspector_column.autoincrement + and not rendered_metadata_default + and rendered_inspector_default == "'0'" + ): + return False + elif ( + rendered_inspector_default + and inspector_column.type._type_affinity is sqltypes.Integer + ): + rendered_inspector_default = ( + re.sub(r"^'|'$", "", rendered_inspector_default) + if rendered_inspector_default is not None + else None + ) + return rendered_inspector_default != rendered_metadata_default + elif ( + rendered_metadata_default + and metadata_column.type._type_affinity is sqltypes.String + ): + metadata_default = re.sub(r"^'|'$", "", rendered_metadata_default) + return rendered_inspector_default != f"'{metadata_default}'" + elif rendered_inspector_default and rendered_metadata_default: + # adjust for "function()" vs. "FUNCTION" as can occur particularly + # for the CURRENT_TIMESTAMP function on newer MariaDB versions + + # SQLAlchemy MySQL dialect bundles ON UPDATE into the server + # default; adjust for this possibly being present. + onupdate_ins = re.match( + r"(.*) (on update.*?)(?:\(\))?$", + rendered_inspector_default.lower(), + ) + onupdate_met = re.match( + r"(.*) (on update.*?)(?:\(\))?$", + rendered_metadata_default.lower(), + ) + + if onupdate_ins: + if not onupdate_met: + return True + elif onupdate_ins.group(2) != onupdate_met.group(2): + return True + + rendered_inspector_default = onupdate_ins.group(1) + rendered_metadata_default = onupdate_met.group(1) + + return re.sub( + r"(.*?)(?:\(\))?$", r"\1", rendered_inspector_default.lower() + ) != re.sub( + r"(.*?)(?:\(\))?$", r"\1", rendered_metadata_default.lower() + ) + else: + return rendered_inspector_default != rendered_metadata_default + + def correct_for_autogen_constraints( + self, + conn_unique_constraints, + conn_indexes, + metadata_unique_constraints, + metadata_indexes, + ): + # TODO: if SQLA 1.0, make use of "duplicates_index" + # metadata + removed = set() + for idx in list(conn_indexes): + if idx.unique: + continue + # MySQL puts implicit indexes on FK columns, even if + # composite and even if MyISAM, so can't check this too easily. + # the name of the index may be the column name or it may + # be the name of the FK constraint. + for col in idx.columns: + if idx.name == col.name: + conn_indexes.remove(idx) + removed.add(idx.name) + break + for fk in col.foreign_keys: + if fk.name == idx.name: + conn_indexes.remove(idx) + removed.add(idx.name) + break + if idx.name in removed: + break + + # then remove indexes from the "metadata_indexes" + # that we've removed from reflected, otherwise they come out + # as adds (see #202) + for idx in list(metadata_indexes): + if idx.name in removed: + metadata_indexes.remove(idx) + + def correct_for_autogen_foreignkeys(self, conn_fks, metadata_fks): + conn_fk_by_sig = { + self._create_reflected_constraint_sig(fk).unnamed_no_options: fk + for fk in conn_fks + } + metadata_fk_by_sig = { + self._create_metadata_constraint_sig(fk).unnamed_no_options: fk + for fk in metadata_fks + } + + for sig in set(conn_fk_by_sig).intersection(metadata_fk_by_sig): + mdfk = metadata_fk_by_sig[sig] + cnfk = conn_fk_by_sig[sig] + # MySQL considers RESTRICT to be the default and doesn't + # report on it. if the model has explicit RESTRICT and + # the conn FK has None, set it to RESTRICT + if ( + mdfk.ondelete is not None + and mdfk.ondelete.lower() == "restrict" + and cnfk.ondelete is None + ): + cnfk.ondelete = "RESTRICT" + if ( + mdfk.onupdate is not None + and mdfk.onupdate.lower() == "restrict" + and cnfk.onupdate is None + ): + cnfk.onupdate = "RESTRICT" + + +class MariaDBImpl(MySQLImpl): + __dialect__ = "mariadb" + + +class MySQLAlterDefault(AlterColumn): + def __init__( + self, + name: str, + column_name: str, + default: Optional[_ServerDefaultType], + schema: Optional[str] = None, + ) -> None: + super(AlterColumn, self).__init__(name, schema=schema) + self.column_name = column_name + self.default = default + + +class MySQLChangeColumn(AlterColumn): + def __init__( + self, + name: str, + column_name: str, + schema: Optional[str] = None, + newname: Optional[str] = None, + type_: Optional[TypeEngine] = None, + nullable: Optional[bool] = None, + default: Optional[Union[_ServerDefaultType, Literal[False]]] = False, + autoincrement: Optional[bool] = None, + comment: Optional[Union[str, Literal[False]]] = False, + ) -> None: + super(AlterColumn, self).__init__(name, schema=schema) + self.column_name = column_name + self.nullable = nullable + self.newname = newname + self.default = default + self.autoincrement = autoincrement + self.comment = comment + if type_ is None: + raise util.CommandError( + "All MySQL CHANGE/MODIFY COLUMN operations " + "require the existing type." + ) + + self.type_ = sqltypes.to_instance(type_) + + +class MySQLModifyColumn(MySQLChangeColumn): + pass + + +@compiles(ColumnNullable, "mysql", "mariadb") +@compiles(ColumnName, "mysql", "mariadb") +@compiles(ColumnDefault, "mysql", "mariadb") +@compiles(ColumnType, "mysql", "mariadb") +def _mysql_doesnt_support_individual(element, compiler, **kw): + raise NotImplementedError( + "Individual alter column constructs not supported by MySQL" + ) + + +@compiles(MySQLAlterDefault, "mysql", "mariadb") +def _mysql_alter_default( + element: MySQLAlterDefault, compiler: MySQLDDLCompiler, **kw +) -> str: + return "%s ALTER COLUMN %s %s" % ( + alter_table(compiler, element.table_name, element.schema), + format_column_name(compiler, element.column_name), + ( + "SET DEFAULT %s" % format_server_default(compiler, element.default) + if element.default is not None + else "DROP DEFAULT" + ), + ) + + +@compiles(MySQLModifyColumn, "mysql", "mariadb") +def _mysql_modify_column( + element: MySQLModifyColumn, compiler: MySQLDDLCompiler, **kw +) -> str: + return "%s MODIFY %s %s" % ( + alter_table(compiler, element.table_name, element.schema), + format_column_name(compiler, element.column_name), + _mysql_colspec( + compiler, + nullable=element.nullable, + server_default=element.default, + type_=element.type_, + autoincrement=element.autoincrement, + comment=element.comment, + ), + ) + + +@compiles(MySQLChangeColumn, "mysql", "mariadb") +def _mysql_change_column( + element: MySQLChangeColumn, compiler: MySQLDDLCompiler, **kw +) -> str: + return "%s CHANGE %s %s %s" % ( + alter_table(compiler, element.table_name, element.schema), + format_column_name(compiler, element.column_name), + format_column_name(compiler, element.newname), + _mysql_colspec( + compiler, + nullable=element.nullable, + server_default=element.default, + type_=element.type_, + autoincrement=element.autoincrement, + comment=element.comment, + ), + ) + + +def _mysql_colspec( + compiler: MySQLDDLCompiler, + nullable: Optional[bool], + server_default: Optional[Union[_ServerDefaultType, Literal[False]]], + type_: TypeEngine, + autoincrement: Optional[bool], + comment: Optional[Union[str, Literal[False]]], +) -> str: + spec = "%s %s" % ( + compiler.dialect.type_compiler.process(type_), + "NULL" if nullable else "NOT NULL", + ) + if autoincrement: + spec += " AUTO_INCREMENT" + if server_default is not False and server_default is not None: + spec += " DEFAULT %s" % format_server_default(compiler, server_default) + if comment: + spec += " COMMENT %s" % compiler.sql_compiler.render_literal_value( + comment, sqltypes.String() + ) + + return spec + + +@compiles(schema.DropConstraint, "mysql", "mariadb") +def _mysql_drop_constraint( + element: DropConstraint, compiler: MySQLDDLCompiler, **kw +) -> str: + """Redefine SQLAlchemy's drop constraint to + raise errors for invalid constraint type.""" + + constraint = element.element + if isinstance( + constraint, + ( + schema.ForeignKeyConstraint, + schema.PrimaryKeyConstraint, + schema.UniqueConstraint, + ), + ): + assert not kw + return compiler.visit_drop_constraint(element) + elif isinstance(constraint, schema.CheckConstraint): + # note that SQLAlchemy as of 1.2 does not yet support + # DROP CONSTRAINT for MySQL/MariaDB, so we implement fully + # here. + if compiler.dialect.is_mariadb: + return "ALTER TABLE %s DROP CONSTRAINT %s" % ( + compiler.preparer.format_table(constraint.table), + compiler.preparer.format_constraint(constraint), + ) + else: + return "ALTER TABLE %s DROP CHECK %s" % ( + compiler.preparer.format_table(constraint.table), + compiler.preparer.format_constraint(constraint), + ) + else: + raise NotImplementedError( + "No generic 'DROP CONSTRAINT' in MySQL - " + "please specify constraint type" + ) diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/oracle.py b/venv/lib/python3.12/site-packages/alembic/ddl/oracle.py new file mode 100644 index 00000000..eac99124 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/ddl/oracle.py @@ -0,0 +1,202 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +import re +from typing import Any +from typing import Optional +from typing import TYPE_CHECKING + +from sqlalchemy.sql import sqltypes + +from .base import AddColumn +from .base import alter_table +from .base import ColumnComment +from .base import ColumnDefault +from .base import ColumnName +from .base import ColumnNullable +from .base import ColumnType +from .base import format_column_name +from .base import format_server_default +from .base import format_table_name +from .base import format_type +from .base import IdentityColumnDefault +from .base import RenameTable +from .impl import DefaultImpl +from ..util.sqla_compat import compiles + +if TYPE_CHECKING: + from sqlalchemy.dialects.oracle.base import OracleDDLCompiler + from sqlalchemy.engine.cursor import CursorResult + from sqlalchemy.sql.schema import Column + + +class OracleImpl(DefaultImpl): + __dialect__ = "oracle" + transactional_ddl = False + batch_separator = "/" + command_terminator = "" + type_synonyms = DefaultImpl.type_synonyms + ( + {"VARCHAR", "VARCHAR2"}, + {"BIGINT", "INTEGER", "SMALLINT", "DECIMAL", "NUMERIC", "NUMBER"}, + {"DOUBLE", "FLOAT", "DOUBLE_PRECISION"}, + ) + identity_attrs_ignore = () + + def __init__(self, *arg, **kw) -> None: + super().__init__(*arg, **kw) + self.batch_separator = self.context_opts.get( + "oracle_batch_separator", self.batch_separator + ) + + def _exec(self, construct: Any, *args, **kw) -> Optional[CursorResult]: + result = super()._exec(construct, *args, **kw) + if self.as_sql and self.batch_separator: + self.static_output(self.batch_separator) + return result + + def compare_server_default( + self, + inspector_column, + metadata_column, + rendered_metadata_default, + rendered_inspector_default, + ): + if rendered_metadata_default is not None: + rendered_metadata_default = re.sub( + r"^\((.+)\)$", r"\1", rendered_metadata_default + ) + + rendered_metadata_default = re.sub( + r"^\"?'(.+)'\"?$", r"\1", rendered_metadata_default + ) + + if rendered_inspector_default is not None: + rendered_inspector_default = re.sub( + r"^\((.+)\)$", r"\1", rendered_inspector_default + ) + + rendered_inspector_default = re.sub( + r"^\"?'(.+)'\"?$", r"\1", rendered_inspector_default + ) + + rendered_inspector_default = rendered_inspector_default.strip() + return rendered_inspector_default != rendered_metadata_default + + def emit_begin(self) -> None: + self._exec("SET TRANSACTION READ WRITE") + + def emit_commit(self) -> None: + self._exec("COMMIT") + + +@compiles(AddColumn, "oracle") +def visit_add_column( + element: AddColumn, compiler: OracleDDLCompiler, **kw +) -> str: + return "%s %s" % ( + alter_table(compiler, element.table_name, element.schema), + add_column(compiler, element.column, **kw), + ) + + +@compiles(ColumnNullable, "oracle") +def visit_column_nullable( + element: ColumnNullable, compiler: OracleDDLCompiler, **kw +) -> str: + return "%s %s %s" % ( + alter_table(compiler, element.table_name, element.schema), + alter_column(compiler, element.column_name), + "NULL" if element.nullable else "NOT NULL", + ) + + +@compiles(ColumnType, "oracle") +def visit_column_type( + element: ColumnType, compiler: OracleDDLCompiler, **kw +) -> str: + return "%s %s %s" % ( + alter_table(compiler, element.table_name, element.schema), + alter_column(compiler, element.column_name), + "%s" % format_type(compiler, element.type_), + ) + + +@compiles(ColumnName, "oracle") +def visit_column_name( + element: ColumnName, compiler: OracleDDLCompiler, **kw +) -> str: + return "%s RENAME COLUMN %s TO %s" % ( + alter_table(compiler, element.table_name, element.schema), + format_column_name(compiler, element.column_name), + format_column_name(compiler, element.newname), + ) + + +@compiles(ColumnDefault, "oracle") +def visit_column_default( + element: ColumnDefault, compiler: OracleDDLCompiler, **kw +) -> str: + return "%s %s %s" % ( + alter_table(compiler, element.table_name, element.schema), + alter_column(compiler, element.column_name), + ( + "DEFAULT %s" % format_server_default(compiler, element.default) + if element.default is not None + else "DEFAULT NULL" + ), + ) + + +@compiles(ColumnComment, "oracle") +def visit_column_comment( + element: ColumnComment, compiler: OracleDDLCompiler, **kw +) -> str: + ddl = "COMMENT ON COLUMN {table_name}.{column_name} IS {comment}" + + comment = compiler.sql_compiler.render_literal_value( + (element.comment if element.comment is not None else ""), + sqltypes.String(), + ) + + return ddl.format( + table_name=element.table_name, + column_name=element.column_name, + comment=comment, + ) + + +@compiles(RenameTable, "oracle") +def visit_rename_table( + element: RenameTable, compiler: OracleDDLCompiler, **kw +) -> str: + return "%s RENAME TO %s" % ( + alter_table(compiler, element.table_name, element.schema), + format_table_name(compiler, element.new_table_name, None), + ) + + +def alter_column(compiler: OracleDDLCompiler, name: str) -> str: + return "MODIFY %s" % format_column_name(compiler, name) + + +def add_column(compiler: OracleDDLCompiler, column: Column[Any], **kw) -> str: + return "ADD %s" % compiler.get_column_specification(column, **kw) + + +@compiles(IdentityColumnDefault, "oracle") +def visit_identity_column( + element: IdentityColumnDefault, compiler: OracleDDLCompiler, **kw +): + text = "%s %s " % ( + alter_table(compiler, element.table_name, element.schema), + alter_column(compiler, element.column_name), + ) + if element.default is None: + # drop identity + text += "DROP IDENTITY" + return text + else: + text += compiler.visit_identity_column(element.default) + return text diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/postgresql.py b/venv/lib/python3.12/site-packages/alembic/ddl/postgresql.py new file mode 100644 index 00000000..cc03f453 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/ddl/postgresql.py @@ -0,0 +1,864 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +import logging +import re +from typing import Any +from typing import cast +from typing import Dict +from typing import List +from typing import Optional +from typing import Sequence +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import Column +from sqlalchemy import Float +from sqlalchemy import Identity +from sqlalchemy import literal_column +from sqlalchemy import Numeric +from sqlalchemy import select +from sqlalchemy import text +from sqlalchemy import types as sqltypes +from sqlalchemy.dialects.postgresql import BIGINT +from sqlalchemy.dialects.postgresql import ExcludeConstraint +from sqlalchemy.dialects.postgresql import INTEGER +from sqlalchemy.schema import CreateIndex +from sqlalchemy.sql.elements import ColumnClause +from sqlalchemy.sql.elements import TextClause +from sqlalchemy.sql.functions import FunctionElement +from sqlalchemy.types import NULLTYPE + +from .base import alter_column +from .base import alter_table +from .base import AlterColumn +from .base import ColumnComment +from .base import format_column_name +from .base import format_table_name +from .base import format_type +from .base import IdentityColumnDefault +from .base import RenameTable +from .impl import ComparisonResult +from .impl import DefaultImpl +from .. import util +from ..autogenerate import render +from ..operations import ops +from ..operations import schemaobj +from ..operations.base import BatchOperations +from ..operations.base import Operations +from ..util import sqla_compat +from ..util.sqla_compat import compiles + + +if TYPE_CHECKING: + from typing import Literal + + from sqlalchemy import Index + from sqlalchemy import UniqueConstraint + from sqlalchemy.dialects.postgresql.array import ARRAY + from sqlalchemy.dialects.postgresql.base import PGDDLCompiler + from sqlalchemy.dialects.postgresql.hstore import HSTORE + from sqlalchemy.dialects.postgresql.json import JSON + from sqlalchemy.dialects.postgresql.json import JSONB + from sqlalchemy.sql.elements import ClauseElement + from sqlalchemy.sql.elements import ColumnElement + from sqlalchemy.sql.elements import quoted_name + from sqlalchemy.sql.schema import MetaData + from sqlalchemy.sql.schema import Table + from sqlalchemy.sql.type_api import TypeEngine + + from .base import _ServerDefaultType + from .impl import _ReflectedConstraint + from ..autogenerate.api import AutogenContext + from ..autogenerate.render import _f_name + from ..runtime.migration import MigrationContext + +log = logging.getLogger(__name__) + + +class PostgresqlImpl(DefaultImpl): + __dialect__ = "postgresql" + transactional_ddl = True + type_synonyms = DefaultImpl.type_synonyms + ( + {"FLOAT", "DOUBLE PRECISION"}, + ) + + def create_index(self, index: Index, **kw: Any) -> None: + # this likely defaults to None if not present, so get() + # should normally not return the default value. being + # defensive in any case + postgresql_include = index.kwargs.get("postgresql_include", None) or () + for col in postgresql_include: + if col not in index.table.c: # type: ignore[union-attr] + index.table.append_column( # type: ignore[union-attr] + Column(col, sqltypes.NullType) + ) + self._exec(CreateIndex(index, **kw)) + + def prep_table_for_batch(self, batch_impl, table): + for constraint in table.constraints: + if ( + constraint.name is not None + and constraint.name in batch_impl.named_constraints + ): + self.drop_constraint(constraint) + + def compare_server_default( + self, + inspector_column, + metadata_column, + rendered_metadata_default, + rendered_inspector_default, + ): + + # don't do defaults for SERIAL columns + if ( + metadata_column.primary_key + and metadata_column is metadata_column.table._autoincrement_column + ): + return False + + conn_col_default = rendered_inspector_default + + if conn_col_default and re.match( + r"nextval\('(.+?)'::regclass\)", conn_col_default + ): + conn_col_default = conn_col_default.replace("::regclass", "") + + defaults_equal = conn_col_default == rendered_metadata_default + if defaults_equal: + return False + + if None in ( + conn_col_default, + rendered_metadata_default, + metadata_column.server_default, + ): + return not defaults_equal + + metadata_default = metadata_column.server_default.arg + + if isinstance(metadata_default, str): + if not isinstance(inspector_column.type, (Numeric, Float)): + metadata_default = re.sub(r"^'|'$", "", metadata_default) + metadata_default = f"'{metadata_default}'" + + metadata_default = literal_column(metadata_default) + + # run a real compare against the server + # TODO: this seems quite a bad idea for a default that's a SQL + # function! SQL functions are not deterministic! + conn = self.connection + assert conn is not None + return not conn.scalar( + select(literal_column(conn_col_default) == metadata_default) + ) + + def alter_column( + self, + table_name: str, + column_name: str, + *, + nullable: Optional[bool] = None, + server_default: Optional[ + Union[_ServerDefaultType, Literal[False]] + ] = False, + name: Optional[str] = None, + type_: Optional[TypeEngine] = None, + schema: Optional[str] = None, + autoincrement: Optional[bool] = None, + existing_type: Optional[TypeEngine] = None, + existing_server_default: Optional[ + Union[_ServerDefaultType, Literal[False]] + ] = None, + existing_nullable: Optional[bool] = None, + existing_autoincrement: Optional[bool] = None, + **kw: Any, + ) -> None: + using = kw.pop("postgresql_using", None) + + if using is not None and type_ is None: + raise util.CommandError( + "postgresql_using must be used with the type_ parameter" + ) + + if type_ is not None: + self._exec( + PostgresqlColumnType( + table_name, + column_name, + type_, + schema=schema, + using=using, + existing_type=existing_type, + existing_server_default=existing_server_default, + existing_nullable=existing_nullable, + ) + ) + + super().alter_column( + table_name, + column_name, + nullable=nullable, + server_default=server_default, + name=name, + schema=schema, + autoincrement=autoincrement, + existing_type=existing_type, + existing_server_default=existing_server_default, + existing_nullable=existing_nullable, + existing_autoincrement=existing_autoincrement, + **kw, + ) + + def autogen_column_reflect(self, inspector, table, column_info): + if column_info.get("default") and isinstance( + column_info["type"], (INTEGER, BIGINT) + ): + seq_match = re.match( + r"nextval\('(.+?)'::regclass\)", column_info["default"] + ) + if seq_match: + info = sqla_compat._exec_on_inspector( + inspector, + text( + "select c.relname, a.attname " + "from pg_class as c join " + "pg_depend d on d.objid=c.oid and " + "d.classid='pg_class'::regclass and " + "d.refclassid='pg_class'::regclass " + "join pg_class t on t.oid=d.refobjid " + "join pg_attribute a on a.attrelid=t.oid and " + "a.attnum=d.refobjsubid " + "where c.relkind='S' and " + "c.oid=cast(:seqname as regclass)" + ), + seqname=seq_match.group(1), + ).first() + if info: + seqname, colname = info + if colname == column_info["name"]: + log.info( + "Detected sequence named '%s' as " + "owned by integer column '%s(%s)', " + "assuming SERIAL and omitting", + seqname, + table.name, + colname, + ) + # sequence, and the owner is this column, + # its a SERIAL - whack it! + del column_info["default"] + + def correct_for_autogen_constraints( + self, + conn_unique_constraints, + conn_indexes, + metadata_unique_constraints, + metadata_indexes, + ): + doubled_constraints = { + index + for index in conn_indexes + if index.info.get("duplicates_constraint") + } + + for ix in doubled_constraints: + conn_indexes.remove(ix) + + if not sqla_compat.sqla_2: + self._skip_functional_indexes(metadata_indexes, conn_indexes) + + # pg behavior regarding modifiers + # | # | compiled sql | returned sql | regexp. group is removed | + # | - | ---------------- | -----------------| ------------------------ | + # | 1 | nulls first | nulls first | - | + # | 2 | nulls last | | (? str: + expr = expr.lower().replace('"', "").replace("'", "") + if index.table is not None: + # should not be needed, since include_table=False is in compile + expr = expr.replace(f"{index.table.name.lower()}.", "") + + if "::" in expr: + # strip :: cast. types can have spaces in them + expr = re.sub(r"(::[\w ]+\w)", "", expr) + + while expr and expr[0] == "(" and expr[-1] == ")": + expr = expr[1:-1] + + # NOTE: when parsing the connection expression this cleanup could + # be skipped + for rs in self._default_modifiers_re: + if match := rs.search(expr): + start, end = match.span(1) + expr = expr[:start] + expr[end:] + break + + while expr and expr[0] == "(" and expr[-1] == ")": + expr = expr[1:-1] + + # strip casts + cast_re = re.compile(r"cast\s*\(") + if cast_re.match(expr): + expr = cast_re.sub("", expr) + # remove the as type + expr = re.sub(r"as\s+[^)]+\)", "", expr) + # remove spaces + expr = expr.replace(" ", "") + return expr + + def _dialect_options( + self, item: Union[Index, UniqueConstraint] + ) -> Tuple[Any, ...]: + # only the positive case is returned by sqlalchemy reflection so + # None and False are treated the same + if item.dialect_kwargs.get("postgresql_nulls_not_distinct"): + return ("nulls_not_distinct",) + return () + + def compare_indexes( + self, + metadata_index: Index, + reflected_index: Index, + ) -> ComparisonResult: + msg = [] + unique_msg = self._compare_index_unique( + metadata_index, reflected_index + ) + if unique_msg: + msg.append(unique_msg) + m_exprs = metadata_index.expressions + r_exprs = reflected_index.expressions + if len(m_exprs) != len(r_exprs): + msg.append(f"expression number {len(r_exprs)} to {len(m_exprs)}") + if msg: + # no point going further, return early + return ComparisonResult.Different(msg) + skip = [] + for pos, (m_e, r_e) in enumerate(zip(m_exprs, r_exprs), 1): + m_compile = self._compile_element(m_e) + m_text = self._cleanup_index_expr(metadata_index, m_compile) + # print(f"META ORIG: {m_compile!r} CLEANUP: {m_text!r}") + r_compile = self._compile_element(r_e) + r_text = self._cleanup_index_expr(metadata_index, r_compile) + # print(f"CONN ORIG: {r_compile!r} CLEANUP: {r_text!r}") + if m_text == r_text: + continue # expressions these are equal + elif m_compile.strip().endswith("_ops") and ( + " " in m_compile or ")" in m_compile # is an expression + ): + skip.append( + f"expression #{pos} {m_compile!r} detected " + "as including operator clause." + ) + util.warn( + f"Expression #{pos} {m_compile!r} in index " + f"{reflected_index.name!r} detected to include " + "an operator clause. Expression compare cannot proceed. " + "Please move the operator clause to the " + "``postgresql_ops`` dict to enable proper compare " + "of the index expressions: " + "https://docs.sqlalchemy.org/en/latest/dialects/postgresql.html#operator-classes", # noqa: E501 + ) + else: + msg.append(f"expression #{pos} {r_compile!r} to {m_compile!r}") + + m_options = self._dialect_options(metadata_index) + r_options = self._dialect_options(reflected_index) + if m_options != r_options: + msg.extend(f"options {r_options} to {m_options}") + + if msg: + return ComparisonResult.Different(msg) + elif skip: + # if there are other changes detected don't skip the index + return ComparisonResult.Skip(skip) + else: + return ComparisonResult.Equal() + + def compare_unique_constraint( + self, + metadata_constraint: UniqueConstraint, + reflected_constraint: UniqueConstraint, + ) -> ComparisonResult: + metadata_tup = self._create_metadata_constraint_sig( + metadata_constraint + ) + reflected_tup = self._create_reflected_constraint_sig( + reflected_constraint + ) + + meta_sig = metadata_tup.unnamed + conn_sig = reflected_tup.unnamed + if conn_sig != meta_sig: + return ComparisonResult.Different( + f"expression {conn_sig} to {meta_sig}" + ) + + metadata_do = self._dialect_options(metadata_tup.const) + conn_do = self._dialect_options(reflected_tup.const) + if metadata_do != conn_do: + return ComparisonResult.Different( + f"expression {conn_do} to {metadata_do}" + ) + + return ComparisonResult.Equal() + + def adjust_reflected_dialect_options( + self, reflected_object: _ReflectedConstraint, kind: str + ) -> Dict[str, Any]: + options: Dict[str, Any] + options = reflected_object.get("dialect_options", {}).copy() # type: ignore[attr-defined] # noqa: E501 + if not options.get("postgresql_include"): + options.pop("postgresql_include", None) + return options + + def _compile_element(self, element: Union[ClauseElement, str]) -> str: + if isinstance(element, str): + return element + return element.compile( + dialect=self.dialect, + compile_kwargs={"literal_binds": True, "include_table": False}, + ).string + + def render_ddl_sql_expr( + self, + expr: ClauseElement, + is_server_default: bool = False, + is_index: bool = False, + **kw: Any, + ) -> str: + """Render a SQL expression that is typically a server default, + index expression, etc. + + """ + + # apply self_group to index expressions; + # see https://github.com/sqlalchemy/sqlalchemy/blob/ + # 82fa95cfce070fab401d020c6e6e4a6a96cc2578/ + # lib/sqlalchemy/dialects/postgresql/base.py#L2261 + if is_index and not isinstance(expr, ColumnClause): + expr = expr.self_group() + + return super().render_ddl_sql_expr( + expr, is_server_default=is_server_default, is_index=is_index, **kw + ) + + def render_type( + self, type_: TypeEngine, autogen_context: AutogenContext + ) -> Union[str, Literal[False]]: + mod = type(type_).__module__ + if not mod.startswith("sqlalchemy.dialects.postgresql"): + return False + + if hasattr(self, "_render_%s_type" % type_.__visit_name__): + meth = getattr(self, "_render_%s_type" % type_.__visit_name__) + return meth(type_, autogen_context) + + return False + + def _render_HSTORE_type( + self, type_: HSTORE, autogen_context: AutogenContext + ) -> str: + return cast( + str, + render._render_type_w_subtype( + type_, autogen_context, "text_type", r"(.+?\(.*text_type=)" + ), + ) + + def _render_ARRAY_type( + self, type_: ARRAY, autogen_context: AutogenContext + ) -> str: + return cast( + str, + render._render_type_w_subtype( + type_, autogen_context, "item_type", r"(.+?\()" + ), + ) + + def _render_JSON_type( + self, type_: JSON, autogen_context: AutogenContext + ) -> str: + return cast( + str, + render._render_type_w_subtype( + type_, autogen_context, "astext_type", r"(.+?\(.*astext_type=)" + ), + ) + + def _render_JSONB_type( + self, type_: JSONB, autogen_context: AutogenContext + ) -> str: + return cast( + str, + render._render_type_w_subtype( + type_, autogen_context, "astext_type", r"(.+?\(.*astext_type=)" + ), + ) + + +class PostgresqlColumnType(AlterColumn): + def __init__( + self, name: str, column_name: str, type_: TypeEngine, **kw + ) -> None: + using = kw.pop("using", None) + super().__init__(name, column_name, **kw) + self.type_ = sqltypes.to_instance(type_) + self.using = using + + +@compiles(RenameTable, "postgresql") +def visit_rename_table( + element: RenameTable, compiler: PGDDLCompiler, **kw +) -> str: + return "%s RENAME TO %s" % ( + alter_table(compiler, element.table_name, element.schema), + format_table_name(compiler, element.new_table_name, None), + ) + + +@compiles(PostgresqlColumnType, "postgresql") +def visit_column_type( + element: PostgresqlColumnType, compiler: PGDDLCompiler, **kw +) -> str: + return "%s %s %s %s" % ( + alter_table(compiler, element.table_name, element.schema), + alter_column(compiler, element.column_name), + "TYPE %s" % format_type(compiler, element.type_), + "USING %s" % element.using if element.using else "", + ) + + +@compiles(ColumnComment, "postgresql") +def visit_column_comment( + element: ColumnComment, compiler: PGDDLCompiler, **kw +) -> str: + ddl = "COMMENT ON COLUMN {table_name}.{column_name} IS {comment}" + comment = ( + compiler.sql_compiler.render_literal_value( + element.comment, sqltypes.String() + ) + if element.comment is not None + else "NULL" + ) + + return ddl.format( + table_name=format_table_name( + compiler, element.table_name, element.schema + ), + column_name=format_column_name(compiler, element.column_name), + comment=comment, + ) + + +@compiles(IdentityColumnDefault, "postgresql") +def visit_identity_column( + element: IdentityColumnDefault, compiler: PGDDLCompiler, **kw +): + text = "%s %s " % ( + alter_table(compiler, element.table_name, element.schema), + alter_column(compiler, element.column_name), + ) + if element.default is None: + # drop identity + text += "DROP IDENTITY" + return text + elif element.existing_server_default is None: + # add identity options + text += "ADD " + text += compiler.visit_identity_column(element.default) + return text + else: + # alter identity + diff, _, _ = element.impl._compare_identity_default( + element.default, element.existing_server_default + ) + identity = element.default + for attr in sorted(diff): + if attr == "always": + text += "SET GENERATED %s " % ( + "ALWAYS" if identity.always else "BY DEFAULT" + ) + else: + text += "SET %s " % compiler.get_identity_options( + Identity(**{attr: getattr(identity, attr)}) + ) + return text + + +@Operations.register_operation("create_exclude_constraint") +@BatchOperations.register_operation( + "create_exclude_constraint", "batch_create_exclude_constraint" +) +@ops.AddConstraintOp.register_add_constraint("exclude_constraint") +class CreateExcludeConstraintOp(ops.AddConstraintOp): + """Represent a create exclude constraint operation.""" + + constraint_type = "exclude" + + def __init__( + self, + constraint_name: sqla_compat._ConstraintName, + table_name: Union[str, quoted_name], + elements: Union[ + Sequence[Tuple[str, str]], + Sequence[Tuple[ColumnClause[Any], str]], + ], + where: Optional[Union[ColumnElement[bool], str]] = None, + schema: Optional[str] = None, + _orig_constraint: Optional[ExcludeConstraint] = None, + **kw, + ) -> None: + self.constraint_name = constraint_name + self.table_name = table_name + self.elements = elements + self.where = where + self.schema = schema + self._orig_constraint = _orig_constraint + self.kw = kw + + @classmethod + def from_constraint( # type:ignore[override] + cls, constraint: ExcludeConstraint + ) -> CreateExcludeConstraintOp: + constraint_table = sqla_compat._table_for_constraint(constraint) + return cls( + constraint.name, + constraint_table.name, + [ # type: ignore + (expr, op) for expr, name, op in constraint._render_exprs + ], + where=cast("ColumnElement[bool] | None", constraint.where), + schema=constraint_table.schema, + _orig_constraint=constraint, + deferrable=constraint.deferrable, + initially=constraint.initially, + using=constraint.using, + ) + + def to_constraint( + self, migration_context: Optional[MigrationContext] = None + ) -> ExcludeConstraint: + if self._orig_constraint is not None: + return self._orig_constraint + schema_obj = schemaobj.SchemaObjects(migration_context) + t = schema_obj.table(self.table_name, schema=self.schema) + excl = ExcludeConstraint( + *self.elements, + name=self.constraint_name, + where=self.where, + **self.kw, + ) + for ( + expr, + name, + oper, + ) in excl._render_exprs: + t.append_column(Column(name, NULLTYPE)) + t.append_constraint(excl) + return excl + + @classmethod + def create_exclude_constraint( + cls, + operations: Operations, + constraint_name: str, + table_name: str, + *elements: Any, + **kw: Any, + ) -> Optional[Table]: + """Issue an alter to create an EXCLUDE constraint using the + current migration context. + + .. note:: This method is Postgresql specific, and additionally + requires at least SQLAlchemy 1.0. + + e.g.:: + + from alembic import op + + op.create_exclude_constraint( + "user_excl", + "user", + ("period", "&&"), + ("group", "="), + where=("group != 'some group'"), + ) + + Note that the expressions work the same way as that of + the ``ExcludeConstraint`` object itself; if plain strings are + passed, quoting rules must be applied manually. + + :param name: Name of the constraint. + :param table_name: String name of the source table. + :param elements: exclude conditions. + :param where: SQL expression or SQL string with optional WHERE + clause. + :param deferrable: optional bool. If set, emit DEFERRABLE or + NOT DEFERRABLE when issuing DDL for this constraint. + :param initially: optional string. If set, emit INITIALLY + when issuing DDL for this constraint. + :param schema: Optional schema name to operate within. + + """ + op = cls(constraint_name, table_name, elements, **kw) + return operations.invoke(op) + + @classmethod + def batch_create_exclude_constraint( + cls, + operations: BatchOperations, + constraint_name: str, + *elements: Any, + **kw: Any, + ) -> Optional[Table]: + """Issue a "create exclude constraint" instruction using the + current batch migration context. + + .. note:: This method is Postgresql specific, and additionally + requires at least SQLAlchemy 1.0. + + .. seealso:: + + :meth:`.Operations.create_exclude_constraint` + + """ + kw["schema"] = operations.impl.schema + op = cls(constraint_name, operations.impl.table_name, elements, **kw) + return operations.invoke(op) + + +@render.renderers.dispatch_for(CreateExcludeConstraintOp) +def _add_exclude_constraint( + autogen_context: AutogenContext, op: CreateExcludeConstraintOp +) -> str: + return _exclude_constraint(op.to_constraint(), autogen_context, alter=True) + + +@render._constraint_renderers.dispatch_for(ExcludeConstraint) +def _render_inline_exclude_constraint( + constraint: ExcludeConstraint, + autogen_context: AutogenContext, + namespace_metadata: MetaData, +) -> str: + rendered = render._user_defined_render( + "exclude", constraint, autogen_context + ) + if rendered is not False: + return rendered + + return _exclude_constraint(constraint, autogen_context, False) + + +def _postgresql_autogenerate_prefix(autogen_context: AutogenContext) -> str: + imports = autogen_context.imports + if imports is not None: + imports.add("from sqlalchemy.dialects import postgresql") + return "postgresql." + + +def _exclude_constraint( + constraint: ExcludeConstraint, + autogen_context: AutogenContext, + alter: bool, +) -> str: + opts: List[Tuple[str, Union[quoted_name, str, _f_name, None]]] = [] + + has_batch = autogen_context._has_batch + + if constraint.deferrable: + opts.append(("deferrable", str(constraint.deferrable))) + if constraint.initially: + opts.append(("initially", str(constraint.initially))) + if constraint.using: + opts.append(("using", str(constraint.using))) + if not has_batch and alter and constraint.table.schema: + opts.append(("schema", render._ident(constraint.table.schema))) + if not alter and constraint.name: + opts.append( + ("name", render._render_gen_name(autogen_context, constraint.name)) + ) + + def do_expr_where_opts(): + args = [ + "(%s, %r)" + % ( + _render_potential_column( + sqltext, # type:ignore[arg-type] + autogen_context, + ), + opstring, + ) + for sqltext, name, opstring in constraint._render_exprs + ] + if constraint.where is not None: + args.append( + "where=%s" + % render._render_potential_expr( + constraint.where, autogen_context + ) + ) + args.extend(["%s=%r" % (k, v) for k, v in opts]) + return args + + if alter: + args = [ + repr(render._render_gen_name(autogen_context, constraint.name)) + ] + if not has_batch: + args += [repr(render._ident(constraint.table.name))] + args.extend(do_expr_where_opts()) + return "%(prefix)screate_exclude_constraint(%(args)s)" % { + "prefix": render._alembic_autogenerate_prefix(autogen_context), + "args": ", ".join(args), + } + else: + args = do_expr_where_opts() + return "%(prefix)sExcludeConstraint(%(args)s)" % { + "prefix": _postgresql_autogenerate_prefix(autogen_context), + "args": ", ".join(args), + } + + +def _render_potential_column( + value: Union[ + ColumnClause[Any], Column[Any], TextClause, FunctionElement[Any] + ], + autogen_context: AutogenContext, +) -> str: + if isinstance(value, ColumnClause): + if value.is_literal: + # like literal_column("int8range(from, to)") in ExcludeConstraint + template = "%(prefix)sliteral_column(%(name)r)" + else: + template = "%(prefix)scolumn(%(name)r)" + + return template % { + "prefix": render._sqlalchemy_autogenerate_prefix(autogen_context), + "name": value.name, + } + else: + return render._render_potential_expr( + value, + autogen_context, + wrap_in_element=isinstance(value, (TextClause, FunctionElement)), + ) diff --git a/venv/lib/python3.12/site-packages/alembic/ddl/sqlite.py b/venv/lib/python3.12/site-packages/alembic/ddl/sqlite.py new file mode 100644 index 00000000..c260d53f --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/ddl/sqlite.py @@ -0,0 +1,237 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +import re +from typing import Any +from typing import Dict +from typing import Optional +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import cast +from sqlalchemy import Computed +from sqlalchemy import JSON +from sqlalchemy import schema +from sqlalchemy import sql + +from .base import alter_table +from .base import ColumnName +from .base import format_column_name +from .base import format_table_name +from .base import RenameTable +from .impl import DefaultImpl +from .. import util +from ..util.sqla_compat import compiles + +if TYPE_CHECKING: + from sqlalchemy.engine.reflection import Inspector + from sqlalchemy.sql.compiler import DDLCompiler + from sqlalchemy.sql.elements import Cast + from sqlalchemy.sql.elements import ClauseElement + from sqlalchemy.sql.schema import Column + from sqlalchemy.sql.schema import Constraint + from sqlalchemy.sql.schema import Table + from sqlalchemy.sql.type_api import TypeEngine + + from ..operations.batch import BatchOperationsImpl + + +class SQLiteImpl(DefaultImpl): + __dialect__ = "sqlite" + + transactional_ddl = False + """SQLite supports transactional DDL, but pysqlite does not: + see: http://bugs.python.org/issue10740 + """ + + def requires_recreate_in_batch( + self, batch_op: BatchOperationsImpl + ) -> bool: + """Return True if the given :class:`.BatchOperationsImpl` + would need the table to be recreated and copied in order to + proceed. + + Normally, only returns True on SQLite when operations other + than add_column are present. + + """ + for op in batch_op.batch: + if op[0] == "add_column": + col = op[1][1] + if isinstance( + col.server_default, schema.DefaultClause + ) and isinstance(col.server_default.arg, sql.ClauseElement): + return True + elif ( + isinstance(col.server_default, Computed) + and col.server_default.persisted + ): + return True + elif op[0] not in ("create_index", "drop_index"): + return True + else: + return False + + def add_constraint(self, const: Constraint, **kw: Any): + # attempt to distinguish between an + # auto-gen constraint and an explicit one + if const._create_rule is None: + raise NotImplementedError( + "No support for ALTER of constraints in SQLite dialect. " + "Please refer to the batch mode feature which allows for " + "SQLite migrations using a copy-and-move strategy." + ) + elif const._create_rule(self): + util.warn( + "Skipping unsupported ALTER for " + "creation of implicit constraint. " + "Please refer to the batch mode feature which allows for " + "SQLite migrations using a copy-and-move strategy." + ) + + def drop_constraint(self, const: Constraint, **kw: Any): + if const._create_rule is None: + raise NotImplementedError( + "No support for ALTER of constraints in SQLite dialect. " + "Please refer to the batch mode feature which allows for " + "SQLite migrations using a copy-and-move strategy." + ) + + def compare_server_default( + self, + inspector_column: Column[Any], + metadata_column: Column[Any], + rendered_metadata_default: Optional[str], + rendered_inspector_default: Optional[str], + ) -> bool: + if rendered_metadata_default is not None: + rendered_metadata_default = re.sub( + r"^\((.+)\)$", r"\1", rendered_metadata_default + ) + + rendered_metadata_default = re.sub( + r"^\"?'(.+)'\"?$", r"\1", rendered_metadata_default + ) + + if rendered_inspector_default is not None: + rendered_inspector_default = re.sub( + r"^\((.+)\)$", r"\1", rendered_inspector_default + ) + + rendered_inspector_default = re.sub( + r"^\"?'(.+)'\"?$", r"\1", rendered_inspector_default + ) + + return rendered_inspector_default != rendered_metadata_default + + def _guess_if_default_is_unparenthesized_sql_expr( + self, expr: Optional[str] + ) -> bool: + """Determine if a server default is a SQL expression or a constant. + + There are too many assertions that expect server defaults to round-trip + identically without parenthesis added so we will add parens only in + very specific cases. + + """ + if not expr: + return False + elif re.match(r"^[0-9\.]$", expr): + return False + elif re.match(r"^'.+'$", expr): + return False + elif re.match(r"^\(.+\)$", expr): + return False + else: + return True + + def autogen_column_reflect( + self, + inspector: Inspector, + table: Table, + column_info: Dict[str, Any], + ) -> None: + # SQLite expression defaults require parenthesis when sent + # as DDL + if self._guess_if_default_is_unparenthesized_sql_expr( + column_info.get("default", None) + ): + column_info["default"] = "(%s)" % (column_info["default"],) + + def render_ddl_sql_expr( + self, expr: ClauseElement, is_server_default: bool = False, **kw + ) -> str: + # SQLite expression defaults require parenthesis when sent + # as DDL + str_expr = super().render_ddl_sql_expr( + expr, is_server_default=is_server_default, **kw + ) + + if ( + is_server_default + and self._guess_if_default_is_unparenthesized_sql_expr(str_expr) + ): + str_expr = "(%s)" % (str_expr,) + return str_expr + + def cast_for_batch_migrate( + self, + existing: Column[Any], + existing_transfer: Dict[str, Union[TypeEngine, Cast]], + new_type: TypeEngine, + ) -> None: + if ( + existing.type._type_affinity is not new_type._type_affinity + and not isinstance(new_type, JSON) + ): + existing_transfer["expr"] = cast( + existing_transfer["expr"], new_type + ) + + def correct_for_autogen_constraints( + self, + conn_unique_constraints, + conn_indexes, + metadata_unique_constraints, + metadata_indexes, + ): + self._skip_functional_indexes(metadata_indexes, conn_indexes) + + +@compiles(RenameTable, "sqlite") +def visit_rename_table( + element: RenameTable, compiler: DDLCompiler, **kw +) -> str: + return "%s RENAME TO %s" % ( + alter_table(compiler, element.table_name, element.schema), + format_table_name(compiler, element.new_table_name, None), + ) + + +@compiles(ColumnName, "sqlite") +def visit_column_name(element: ColumnName, compiler: DDLCompiler, **kw) -> str: + return "%s RENAME COLUMN %s TO %s" % ( + alter_table(compiler, element.table_name, element.schema), + format_column_name(compiler, element.column_name), + format_column_name(compiler, element.newname), + ) + + +# @compiles(AddColumn, 'sqlite') +# def visit_add_column(element, compiler, **kw): +# return "%s %s" % ( +# alter_table(compiler, element.table_name, element.schema), +# add_column(compiler, element.column, **kw) +# ) + + +# def add_column(compiler, column, **kw): +# text = "ADD COLUMN %s" % compiler.get_column_specification(column, **kw) +# need to modify SQLAlchemy so that the CHECK associated with a Boolean +# or Enum gets placed as part of the column constraints, not the Table +# see ticket 98 +# for const in column.constraints: +# text += compiler.process(AddConstraint(const)) +# return text diff --git a/venv/lib/python3.12/site-packages/alembic/environment.py b/venv/lib/python3.12/site-packages/alembic/environment.py new file mode 100644 index 00000000..adfc93eb --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/environment.py @@ -0,0 +1 @@ +from .runtime.environment import * # noqa diff --git a/venv/lib/python3.12/site-packages/alembic/migration.py b/venv/lib/python3.12/site-packages/alembic/migration.py new file mode 100644 index 00000000..02626e2c --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/migration.py @@ -0,0 +1 @@ +from .runtime.migration import * # noqa diff --git a/venv/lib/python3.12/site-packages/alembic/op.py b/venv/lib/python3.12/site-packages/alembic/op.py new file mode 100644 index 00000000..f3f5fac0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/op.py @@ -0,0 +1,5 @@ +from .operations.base import Operations + +# create proxy functions for +# each method on the Operations class. +Operations.create_module_class_proxy(globals(), locals()) diff --git a/venv/lib/python3.12/site-packages/alembic/op.pyi b/venv/lib/python3.12/site-packages/alembic/op.pyi new file mode 100644 index 00000000..7fadaf5f --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/op.pyi @@ -0,0 +1,1429 @@ +# ### this file stubs are generated by tools/write_pyi.py - do not edit ### +# ### imports are manually managed +from __future__ import annotations + +from contextlib import contextmanager +from typing import Any +from typing import Awaitable +from typing import Callable +from typing import Dict +from typing import Iterator +from typing import List +from typing import Literal +from typing import Mapping +from typing import Optional +from typing import overload +from typing import Sequence +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +if TYPE_CHECKING: + from sqlalchemy.engine import Connection + from sqlalchemy.sql import Executable + from sqlalchemy.sql.elements import ColumnElement + from sqlalchemy.sql.elements import conv + from sqlalchemy.sql.elements import TextClause + from sqlalchemy.sql.expression import TableClause + from sqlalchemy.sql.schema import Column + from sqlalchemy.sql.schema import SchemaItem + from sqlalchemy.sql.schema import Table + from sqlalchemy.sql.type_api import TypeEngine + from sqlalchemy.util import immutabledict + + from .ddl.base import _ServerDefaultType + from .operations.base import BatchOperations + from .operations.ops import AddColumnOp + from .operations.ops import AddConstraintOp + from .operations.ops import AlterColumnOp + from .operations.ops import AlterTableOp + from .operations.ops import BulkInsertOp + from .operations.ops import CreateIndexOp + from .operations.ops import CreateTableCommentOp + from .operations.ops import CreateTableOp + from .operations.ops import DropColumnOp + from .operations.ops import DropConstraintOp + from .operations.ops import DropIndexOp + from .operations.ops import DropTableCommentOp + from .operations.ops import DropTableOp + from .operations.ops import ExecuteSQLOp + from .operations.ops import MigrateOperation + from .runtime.migration import MigrationContext + from .util.sqla_compat import _literal_bindparam + +_T = TypeVar("_T") +_C = TypeVar("_C", bound=Callable[..., Any]) + +### end imports ### + +def add_column( + table_name: str, + column: Column[Any], + *, + schema: Optional[str] = None, + if_not_exists: Optional[bool] = None, + inline_references: Optional[bool] = None, + inline_primary_key: Optional[bool] = None, +) -> None: + """Issue an "add column" instruction using the current + migration context. + + e.g.:: + + from alembic import op + from sqlalchemy import Column, String + + op.add_column("organization", Column("name", String())) + + The :meth:`.Operations.add_column` method typically corresponds + to the SQL command "ALTER TABLE... ADD COLUMN". Within the scope + of this command, the column's name, datatype, nullability, + and optional server-generated defaults may be indicated. Options + also exist for control of single-column primary key and foreign key + constraints to be generated. + + .. note:: + + Not all contraint types may be indicated with this directive. + NOT NULL, FOREIGN KEY, and CHECK are honored, PRIMARY KEY + is conditionally honored, UNIQUE + is currently not. + + As of 1.18.2, the following :class:`~sqlalchemy.schema.Column` + parameters are **ignored**: + + * :paramref:`~sqlalchemy.schema.Column.unique` - use the + :meth:`.Operations.create_unique_constraint` method + * :paramref:`~sqlalchemy.schema.Column.index` - use the + :meth:`.Operations.create_index` method + + **PRIMARY KEY support** + + The provided :class:`~sqlalchemy.schema.Column` object may include a + ``primary_key=True`` directive, indicating the column intends to be + part of a primary key constraint. However by default, the inline + "PRIMARY KEY" directive is not emitted, and it's assumed that a + separate :meth:`.Operations.create_primary_key` directive will be used + to create this constraint, which may potentially include other columns + as well as have an explicit name. To instead render an inline + "PRIMARY KEY" directive, the + :paramref:`.AddColumnOp.inline_primary_key` parameter may be indicated + at the same time as the ``primary_key`` parameter (both are needed):: + + from alembic import op + from sqlalchemy import Column, INTEGER + + op.add_column( + "organization", + Column("id", INTEGER, primary_key=True), + inline_primary_key=True + ) + + The ``primary_key=True`` parameter on + :class:`~sqlalchemy.schema.Column` also indicates behaviors such as + using the ``SERIAL`` datatype with the PostgreSQL database, which is + why two separate, independent parameters are provided to support all + combinations. + + .. versionadded:: 1.18.4 Added + :paramref:`.AddColumnOp.inline_primary_key` + to control use of the ``PRIMARY KEY`` inline directive. + + **FOREIGN KEY support** + + The provided :class:`~sqlalchemy.schema.Column` object may include a + :class:`~sqlalchemy.schema.ForeignKey` constraint directive, + referencing a remote table name. By default, Alembic will automatically + emit a second ALTER statement in order to add the single-column FOREIGN + KEY constraint separately:: + + from alembic import op + from sqlalchemy import Column, INTEGER, ForeignKey + + op.add_column( + "organization", + Column("account_id", INTEGER, ForeignKey("accounts.id")), + ) + + To render the FOREIGN KEY constraint inline within the ADD COLUMN + directive, use the ``inline_references`` parameter. This can improve + performance on large tables since the constraint is marked as valid + immediately for nullable columns:: + + from alembic import op + from sqlalchemy import Column, INTEGER, ForeignKey + + op.add_column( + "organization", + Column("account_id", INTEGER, ForeignKey("accounts.id")), + inline_references=True, + ) + + **Indicating server side defaults** + + The column argument passed to :meth:`.Operations.add_column` is a + :class:`~sqlalchemy.schema.Column` construct, used in the same way it's + used in SQLAlchemy. In particular, values or functions to be indicated + as producing the column's default value on the database side are + specified using the ``server_default`` parameter, and not ``default`` + which only specifies Python-side defaults:: + + from alembic import op + from sqlalchemy import Column, TIMESTAMP, func + + # specify "DEFAULT NOW" along with the column add + op.add_column( + "account", + Column("timestamp", TIMESTAMP, server_default=func.now()), + ) + + :param table_name: String name of the parent table. + :param column: a :class:`sqlalchemy.schema.Column` object + representing the new column. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_not_exists: If True, adds ``IF NOT EXISTS`` operator + when creating the new column for compatible dialects + + .. versionadded:: 1.16.0 + + :param inline_references: If True, renders ``FOREIGN KEY`` constraints + inline within the ``ADD COLUMN`` directive using ``REFERENCES`` + syntax, rather than as a separate ``ALTER TABLE ADD CONSTRAINT`` + statement. This is supported by PostgreSQL, Oracle, MySQL 5.7+, and + MariaDB 10.5+. + + .. versionadded:: 1.18.2 + + :param inline_primary_key: If True, renders the ``PRIMARY KEY`` phrase + inline within the ``ADD COLUMN`` directive. When not present or + False, ``PRIMARY KEY`` is not emitted; it is assumed that the + migration script will include an additional + :meth:`.Operations.create_primary_key` directive to create a full + primary key constraint. + + .. versionadded:: 1.18.4 + + """ + +def alter_column( + table_name: str, + column_name: str, + *, + nullable: Optional[bool] = None, + comment: Union[str, Literal[False], None] = False, + server_default: Union[_ServerDefaultType, None, Literal[False]] = False, + new_column_name: Optional[str] = None, + type_: Union[TypeEngine[Any], Type[TypeEngine[Any]], None] = None, + existing_type: Union[TypeEngine[Any], Type[TypeEngine[Any]], None] = None, + existing_server_default: Union[ + _ServerDefaultType, None, Literal[False] + ] = False, + existing_nullable: Optional[bool] = None, + existing_comment: Optional[str] = None, + schema: Optional[str] = None, + **kw: Any, +) -> None: + r"""Issue an "alter column" instruction using the + current migration context. + + Generally, only that aspect of the column which + is being changed, i.e. name, type, nullability, + default, needs to be specified. Multiple changes + can also be specified at once and the backend should + "do the right thing", emitting each change either + separately or together as the backend allows. + + MySQL has special requirements here, since MySQL + cannot ALTER a column without a full specification. + When producing MySQL-compatible migration files, + it is recommended that the ``existing_type``, + ``existing_server_default``, and ``existing_nullable`` + parameters be present, if not being altered. + + Type changes which are against the SQLAlchemy + "schema" types :class:`~sqlalchemy.types.Boolean` + and :class:`~sqlalchemy.types.Enum` may also + add or drop constraints which accompany those + types on backends that don't support them natively. + The ``existing_type`` argument is + used in this case to identify and remove a previous + constraint that was bound to the type object. + + :param table_name: string name of the target table. + :param column_name: string name of the target column, + as it exists before the operation begins. + :param nullable: Optional; specify ``True`` or ``False`` + to alter the column's nullability. + :param server_default: Optional; specify a string + SQL expression, :func:`~sqlalchemy.sql.expression.text`, + or :class:`~sqlalchemy.schema.DefaultClause` to indicate + an alteration to the column's default value. + Set to ``None`` to have the default removed. + :param comment: optional string text of a new comment to add to the + column. + :param new_column_name: Optional; specify a string name here to + indicate the new name within a column rename operation. + :param type\_: Optional; a :class:`~sqlalchemy.types.TypeEngine` + type object to specify a change to the column's type. + For SQLAlchemy types that also indicate a constraint (i.e. + :class:`~sqlalchemy.types.Boolean`, :class:`~sqlalchemy.types.Enum`), + the constraint is also generated. + :param autoincrement: set the ``AUTO_INCREMENT`` flag of the column; + currently understood by the MySQL dialect. + :param existing_type: Optional; a + :class:`~sqlalchemy.types.TypeEngine` + type object to specify the previous type. This + is required for all MySQL column alter operations that + don't otherwise specify a new type, as well as for + when nullability is being changed on a SQL Server + column. It is also used if the type is a so-called + SQLAlchemy "schema" type which may define a constraint (i.e. + :class:`~sqlalchemy.types.Boolean`, + :class:`~sqlalchemy.types.Enum`), + so that the constraint can be dropped. + :param existing_server_default: Optional; The existing + default value of the column. Required on MySQL if + an existing default is not being changed; else MySQL + removes the default. + :param existing_nullable: Optional; the existing nullability + of the column. Required on MySQL if the existing nullability + is not being changed; else MySQL sets this to NULL. + :param existing_autoincrement: Optional; the existing autoincrement + of the column. Used for MySQL's system of altering a column + that specifies ``AUTO_INCREMENT``. + :param existing_comment: string text of the existing comment on the + column to be maintained. Required on MySQL if the existing comment + on the column is not being changed. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param postgresql_using: String argument which will indicate a + SQL expression to render within the Postgresql-specific USING clause + within ALTER COLUMN. This string is taken directly as raw SQL which + must explicitly include any necessary quoting or escaping of tokens + within the expression. + + """ + +@contextmanager +def batch_alter_table( + table_name: str, + schema: Optional[str] = None, + recreate: Literal["auto", "always", "never"] = "auto", + partial_reordering: list[tuple[str, ...]] | None = None, + copy_from: Optional[Table] = None, + table_args: Tuple[Any, ...] = (), + table_kwargs: Mapping[str, Any] = immutabledict({}), + reflect_args: Tuple[Any, ...] = (), + reflect_kwargs: Mapping[str, Any] = immutabledict({}), + naming_convention: Optional[Dict[str, str]] = None, +) -> Iterator[BatchOperations]: + """Invoke a series of per-table migrations in batch. + + Batch mode allows a series of operations specific to a table + to be syntactically grouped together, and allows for alternate + modes of table migration, in particular the "recreate" style of + migration required by SQLite. + + "recreate" style is as follows: + + 1. A new table is created with the new specification, based on the + migration directives within the batch, using a temporary name. + + 2. the data copied from the existing table to the new table. + + 3. the existing table is dropped. + + 4. the new table is renamed to the existing table name. + + The directive by default will only use "recreate" style on the + SQLite backend, and only if directives are present which require + this form, e.g. anything other than ``add_column()``. The batch + operation on other backends will proceed using standard ALTER TABLE + operations. + + The method is used as a context manager, which returns an instance + of :class:`.BatchOperations`; this object is the same as + :class:`.Operations` except that table names and schema names + are omitted. E.g.:: + + with op.batch_alter_table("some_table") as batch_op: + batch_op.add_column(Column("foo", Integer)) + batch_op.drop_column("bar") + + The operations within the context manager are invoked at once + when the context is ended. When run against SQLite, if the + migrations include operations not supported by SQLite's ALTER TABLE, + the entire table will be copied to a new one with the new + specification, moving all data across as well. + + The copy operation by default uses reflection to retrieve the current + structure of the table, and therefore :meth:`.batch_alter_table` + in this mode requires that the migration is run in "online" mode. + The ``copy_from`` parameter may be passed which refers to an existing + :class:`.Table` object, which will bypass this reflection step. + + .. note:: The table copy operation will currently not copy + CHECK constraints, and may not copy UNIQUE constraints that are + unnamed, as is possible on SQLite. See the section + :ref:`sqlite_batch_constraints` for workarounds. + + :param table_name: name of table + :param schema: optional schema name. + :param recreate: under what circumstances the table should be + recreated. At its default of ``"auto"``, the SQLite dialect will + recreate the table if any operations other than ``add_column()``, + ``create_index()``, or ``drop_index()`` are + present. Other options include ``"always"`` and ``"never"``. + :param copy_from: optional :class:`~sqlalchemy.schema.Table` object + that will act as the structure of the table being copied. If omitted, + table reflection is used to retrieve the structure of the table. + + .. seealso:: + + :ref:`batch_offline_mode` + + :paramref:`~.Operations.batch_alter_table.reflect_args` + + :paramref:`~.Operations.batch_alter_table.reflect_kwargs` + + :param reflect_args: a sequence of additional positional arguments that + will be applied to the table structure being reflected / copied; + this may be used to pass column and constraint overrides to the + table that will be reflected, in lieu of passing the whole + :class:`~sqlalchemy.schema.Table` using + :paramref:`~.Operations.batch_alter_table.copy_from`. + :param reflect_kwargs: a dictionary of additional keyword arguments + that will be applied to the table structure being copied; this may be + used to pass additional table and reflection options to the table that + will be reflected, in lieu of passing the whole + :class:`~sqlalchemy.schema.Table` using + :paramref:`~.Operations.batch_alter_table.copy_from`. + :param table_args: a sequence of additional positional arguments that + will be applied to the new :class:`~sqlalchemy.schema.Table` when + created, in addition to those copied from the source table. + This may be used to provide additional constraints such as CHECK + constraints that may not be reflected. + :param table_kwargs: a dictionary of additional keyword arguments + that will be applied to the new :class:`~sqlalchemy.schema.Table` + when created, in addition to those copied from the source table. + This may be used to provide for additional table options that may + not be reflected. + :param naming_convention: a naming convention dictionary of the form + described at :ref:`autogen_naming_conventions` which will be applied + to the :class:`~sqlalchemy.schema.MetaData` during the reflection + process. This is typically required if one wants to drop SQLite + constraints, as these constraints will not have names when + reflected on this backend. Requires SQLAlchemy **0.9.4** or greater. + + .. seealso:: + + :ref:`dropping_sqlite_foreign_keys` + + :param partial_reordering: a list of tuples, each suggesting a desired + ordering of two or more columns in the newly created table. Requires + that :paramref:`.batch_alter_table.recreate` is set to ``"always"``. + Examples, given a table with columns "a", "b", "c", and "d": + + Specify the order of all columns:: + + with op.batch_alter_table( + "some_table", + recreate="always", + partial_reordering=[("c", "d", "a", "b")], + ) as batch_op: + pass + + Ensure "d" appears before "c", and "b", appears before "a":: + + with op.batch_alter_table( + "some_table", + recreate="always", + partial_reordering=[("d", "c"), ("b", "a")], + ) as batch_op: + pass + + The ordering of columns not included in the partial_reordering + set is undefined. Therefore it is best to specify the complete + ordering of all columns for best results. + + .. note:: batch mode requires SQLAlchemy 0.8 or above. + + .. seealso:: + + :ref:`batch_migrations` + + """ + +def bulk_insert( + table: Union[Table, TableClause], + rows: List[Dict[str, Any]], + *, + multiinsert: bool = True, +) -> None: + """Issue a "bulk insert" operation using the current + migration context. + + This provides a means of representing an INSERT of multiple rows + which works equally well in the context of executing on a live + connection as well as that of generating a SQL script. In the + case of a SQL script, the values are rendered inline into the + statement. + + e.g.:: + + from alembic import op + from datetime import date + from sqlalchemy.sql import table, column + from sqlalchemy import String, Integer, Date + + # Create an ad-hoc table to use for the insert statement. + accounts_table = table( + "account", + column("id", Integer), + column("name", String), + column("create_date", Date), + ) + + op.bulk_insert( + accounts_table, + [ + { + "id": 1, + "name": "John Smith", + "create_date": date(2010, 10, 5), + }, + { + "id": 2, + "name": "Ed Williams", + "create_date": date(2007, 5, 27), + }, + { + "id": 3, + "name": "Wendy Jones", + "create_date": date(2008, 8, 15), + }, + ], + ) + + When using --sql mode, some datatypes may not render inline + automatically, such as dates and other special types. When this + issue is present, :meth:`.Operations.inline_literal` may be used:: + + op.bulk_insert( + accounts_table, + [ + { + "id": 1, + "name": "John Smith", + "create_date": op.inline_literal("2010-10-05"), + }, + { + "id": 2, + "name": "Ed Williams", + "create_date": op.inline_literal("2007-05-27"), + }, + { + "id": 3, + "name": "Wendy Jones", + "create_date": op.inline_literal("2008-08-15"), + }, + ], + multiinsert=False, + ) + + When using :meth:`.Operations.inline_literal` in conjunction with + :meth:`.Operations.bulk_insert`, in order for the statement to work + in "online" (e.g. non --sql) mode, the + :paramref:`~.Operations.bulk_insert.multiinsert` + flag should be set to ``False``, which will have the effect of + individual INSERT statements being emitted to the database, each + with a distinct VALUES clause, so that the "inline" values can + still be rendered, rather than attempting to pass the values + as bound parameters. + + :param table: a table object which represents the target of the INSERT. + + :param rows: a list of dictionaries indicating rows. + + :param multiinsert: when at its default of True and --sql mode is not + enabled, the INSERT statement will be executed using + "executemany()" style, where all elements in the list of + dictionaries are passed as bound parameters in a single + list. Setting this to False results in individual INSERT + statements being emitted per parameter set, and is needed + in those cases where non-literal values are present in the + parameter sets. + + """ + +def create_check_constraint( + constraint_name: Optional[str], + table_name: str, + condition: Union[str, ColumnElement[bool], TextClause], + *, + schema: Optional[str] = None, + **kw: Any, +) -> None: + """Issue a "create check constraint" instruction using the + current migration context. + + e.g.:: + + from alembic import op + from sqlalchemy.sql import column, func + + op.create_check_constraint( + "ck_user_name_len", + "user", + func.len(column("name")) > 5, + ) + + CHECK constraints are usually against a SQL expression, so ad-hoc + table metadata is usually needed. The function will convert the given + arguments into a :class:`sqlalchemy.schema.CheckConstraint` bound + to an anonymous table in order to emit the CREATE statement. + + :param name: Name of the check constraint. The name is necessary + so that an ALTER statement can be emitted. For setups that + use an automated naming scheme such as that described at + :ref:`sqla:constraint_naming_conventions`, + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param table_name: String name of the source table. + :param condition: SQL expression that's the condition of the + constraint. Can be a string or SQLAlchemy expression language + structure. + :param deferrable: optional bool. If set, emit DEFERRABLE or + NOT DEFERRABLE when issuing DDL for this constraint. + :param initially: optional string. If set, emit INITIALLY + when issuing DDL for this constraint. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + """ + +def create_exclude_constraint( + constraint_name: str, table_name: str, *elements: Any, **kw: Any +) -> Optional[Table]: + """Issue an alter to create an EXCLUDE constraint using the + current migration context. + + .. note:: This method is Postgresql specific, and additionally + requires at least SQLAlchemy 1.0. + + e.g.:: + + from alembic import op + + op.create_exclude_constraint( + "user_excl", + "user", + ("period", "&&"), + ("group", "="), + where=("group != 'some group'"), + ) + + Note that the expressions work the same way as that of + the ``ExcludeConstraint`` object itself; if plain strings are + passed, quoting rules must be applied manually. + + :param name: Name of the constraint. + :param table_name: String name of the source table. + :param elements: exclude conditions. + :param where: SQL expression or SQL string with optional WHERE + clause. + :param deferrable: optional bool. If set, emit DEFERRABLE or + NOT DEFERRABLE when issuing DDL for this constraint. + :param initially: optional string. If set, emit INITIALLY + when issuing DDL for this constraint. + :param schema: Optional schema name to operate within. + + """ + +def create_foreign_key( + constraint_name: Optional[str], + source_table: str, + referent_table: str, + local_cols: List[str], + remote_cols: List[str], + *, + onupdate: Optional[str] = None, + ondelete: Optional[str] = None, + deferrable: Optional[bool] = None, + initially: Optional[str] = None, + match: Optional[str] = None, + source_schema: Optional[str] = None, + referent_schema: Optional[str] = None, + **dialect_kw: Any, +) -> None: + """Issue a "create foreign key" instruction using the + current migration context. + + e.g.:: + + from alembic import op + + op.create_foreign_key( + "fk_user_address", + "address", + "user", + ["user_id"], + ["id"], + ) + + This internally generates a :class:`~sqlalchemy.schema.Table` object + containing the necessary columns, then generates a new + :class:`~sqlalchemy.schema.ForeignKeyConstraint` + object which it then associates with the + :class:`~sqlalchemy.schema.Table`. + Any event listeners associated with this action will be fired + off normally. The :class:`~sqlalchemy.schema.AddConstraint` + construct is ultimately used to generate the ALTER statement. + + :param constraint_name: Name of the foreign key constraint. The name + is necessary so that an ALTER statement can be emitted. For setups + that use an automated naming scheme such as that described at + :ref:`sqla:constraint_naming_conventions`, + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param source_table: String name of the source table. + :param referent_table: String name of the destination table. + :param local_cols: a list of string column names in the + source table. + :param remote_cols: a list of string column names in the + remote table. + :param onupdate: Optional string. If set, emit ON UPDATE when + issuing DDL for this constraint. Typical values include CASCADE, + DELETE and RESTRICT. + :param ondelete: Optional string. If set, emit ON DELETE when + issuing DDL for this constraint. Typical values include CASCADE, + DELETE and RESTRICT. + :param deferrable: optional bool. If set, emit DEFERRABLE or NOT + DEFERRABLE when issuing DDL for this constraint. + :param source_schema: Optional schema name of the source table. + :param referent_schema: Optional schema name of the destination table. + + """ + +def create_index( + index_name: Optional[str], + table_name: str, + columns: Sequence[Union[str, TextClause, ColumnElement[Any]]], + *, + schema: Optional[str] = None, + unique: bool = False, + if_not_exists: Optional[bool] = None, + **kw: Any, +) -> None: + r"""Issue a "create index" instruction using the current + migration context. + + e.g.:: + + from alembic import op + + op.create_index("ik_test", "t1", ["foo", "bar"]) + + Functional indexes can be produced by using the + :func:`sqlalchemy.sql.expression.text` construct:: + + from alembic import op + from sqlalchemy import text + + op.create_index("ik_test", "t1", [text("lower(foo)")]) + + :param index_name: name of the index. + :param table_name: name of the owning table. + :param columns: a list consisting of string column names and/or + :func:`~sqlalchemy.sql.expression.text` constructs. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param unique: If True, create a unique index. + + :param quote: Force quoting of this column's name on or off, + corresponding to ``True`` or ``False``. When left at its default + of ``None``, the column identifier will be quoted according to + whether the name is case sensitive (identifiers with at least one + upper case character are treated as case sensitive), or if it's a + reserved word. This flag is only needed to force quoting of a + reserved word which is not known by the SQLAlchemy dialect. + + :param if_not_exists: If True, adds IF NOT EXISTS operator when + creating the new index. + + .. versionadded:: 1.12.0 + + :param \**kw: Additional keyword arguments not mentioned above are + dialect specific, and passed in the form + ``_``. + See the documentation regarding an individual dialect at + :ref:`dialect_toplevel` for detail on documented arguments. + + """ + +def create_primary_key( + constraint_name: Optional[str], + table_name: str, + columns: List[str], + *, + schema: Optional[str] = None, +) -> None: + """Issue a "create primary key" instruction using the current + migration context. + + e.g.:: + + from alembic import op + + op.create_primary_key("pk_my_table", "my_table", ["id", "version"]) + + This internally generates a :class:`~sqlalchemy.schema.Table` object + containing the necessary columns, then generates a new + :class:`~sqlalchemy.schema.PrimaryKeyConstraint` + object which it then associates with the + :class:`~sqlalchemy.schema.Table`. + Any event listeners associated with this action will be fired + off normally. The :class:`~sqlalchemy.schema.AddConstraint` + construct is ultimately used to generate the ALTER statement. + + :param constraint_name: Name of the primary key constraint. The name + is necessary so that an ALTER statement can be emitted. For setups + that use an automated naming scheme such as that described at + :ref:`sqla:constraint_naming_conventions` + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param table_name: String name of the target table. + :param columns: a list of string column names to be applied to the + primary key constraint. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + """ + +def create_table( + table_name: str, + *columns: SchemaItem, + if_not_exists: Optional[bool] = None, + **kw: Any, +) -> Table: + r"""Issue a "create table" instruction using the current migration + context. + + This directive receives an argument list similar to that of the + traditional :class:`sqlalchemy.schema.Table` construct, but without the + metadata:: + + from sqlalchemy import INTEGER, VARCHAR, NVARCHAR, Column + from alembic import op + + op.create_table( + "account", + Column("id", INTEGER, primary_key=True), + Column("name", VARCHAR(50), nullable=False), + Column("description", NVARCHAR(200)), + Column("timestamp", TIMESTAMP, server_default=func.now()), + ) + + Note that :meth:`.create_table` accepts + :class:`~sqlalchemy.schema.Column` + constructs directly from the SQLAlchemy library. In particular, + default values to be created on the database side are + specified using the ``server_default`` parameter, and not + ``default`` which only specifies Python-side defaults:: + + from alembic import op + from sqlalchemy import Column, TIMESTAMP, func + + # specify "DEFAULT NOW" along with the "timestamp" column + op.create_table( + "account", + Column("id", INTEGER, primary_key=True), + Column("timestamp", TIMESTAMP, server_default=func.now()), + ) + + The function also returns a newly created + :class:`~sqlalchemy.schema.Table` object, corresponding to the table + specification given, which is suitable for + immediate SQL operations, in particular + :meth:`.Operations.bulk_insert`:: + + from sqlalchemy import INTEGER, VARCHAR, NVARCHAR, Column + from alembic import op + + account_table = op.create_table( + "account", + Column("id", INTEGER, primary_key=True), + Column("name", VARCHAR(50), nullable=False), + Column("description", NVARCHAR(200)), + Column("timestamp", TIMESTAMP, server_default=func.now()), + ) + + op.bulk_insert( + account_table, + [ + {"name": "A1", "description": "account 1"}, + {"name": "A2", "description": "account 2"}, + ], + ) + + :param table_name: Name of the table + :param \*columns: collection of :class:`~sqlalchemy.schema.Column` + objects within + the table, as well as optional :class:`~sqlalchemy.schema.Constraint` + objects + and :class:`~.sqlalchemy.schema.Index` objects. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_not_exists: If True, adds IF NOT EXISTS operator when + creating the new table. + + .. versionadded:: 1.13.3 + :param \**kw: Other keyword arguments are passed to the underlying + :class:`sqlalchemy.schema.Table` object created for the command. + + :return: the :class:`~sqlalchemy.schema.Table` object corresponding + to the parameters given. + + """ + +def create_table_comment( + table_name: str, + comment: Optional[str], + *, + existing_comment: Optional[str] = None, + schema: Optional[str] = None, +) -> None: + """Emit a COMMENT ON operation to set the comment for a table. + + :param table_name: string name of the target table. + :param comment: string value of the comment being registered against + the specified table. + :param existing_comment: String value of a comment + already registered on the specified table, used within autogenerate + so that the operation is reversible, but not required for direct + use. + + .. seealso:: + + :meth:`.Operations.drop_table_comment` + + :paramref:`.Operations.alter_column.comment` + + """ + +def create_unique_constraint( + constraint_name: Optional[str], + table_name: str, + columns: Sequence[str], + *, + schema: Optional[str] = None, + **kw: Any, +) -> Any: + """Issue a "create unique constraint" instruction using the + current migration context. + + e.g.:: + + from alembic import op + op.create_unique_constraint("uq_user_name", "user", ["name"]) + + This internally generates a :class:`~sqlalchemy.schema.Table` object + containing the necessary columns, then generates a new + :class:`~sqlalchemy.schema.UniqueConstraint` + object which it then associates with the + :class:`~sqlalchemy.schema.Table`. + Any event listeners associated with this action will be fired + off normally. The :class:`~sqlalchemy.schema.AddConstraint` + construct is ultimately used to generate the ALTER statement. + + :param name: Name of the unique constraint. The name is necessary + so that an ALTER statement can be emitted. For setups that + use an automated naming scheme such as that described at + :ref:`sqla:constraint_naming_conventions`, + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param table_name: String name of the source table. + :param columns: a list of string column names in the + source table. + :param deferrable: optional bool. If set, emit DEFERRABLE or + NOT DEFERRABLE when issuing DDL for this constraint. + :param initially: optional string. If set, emit INITIALLY + when issuing DDL for this constraint. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + """ + +def drop_column( + table_name: str, + column_name: str, + *, + schema: Optional[str] = None, + **kw: Any, +) -> None: + """Issue a "drop column" instruction using the current + migration context. + + e.g.:: + + drop_column("organization", "account_id") + + :param table_name: name of table + :param column_name: name of column + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_exists: If True, adds IF EXISTS operator when + dropping the new column for compatible dialects + + .. versionadded:: 1.16.0 + + :param mssql_drop_check: Optional boolean. When ``True``, on + Microsoft SQL Server only, first + drop the CHECK constraint on the column using a + SQL-script-compatible + block that selects into a @variable from sys.check_constraints, + then exec's a separate DROP CONSTRAINT for that constraint. + :param mssql_drop_default: Optional boolean. When ``True``, on + Microsoft SQL Server only, first + drop the DEFAULT constraint on the column using a + SQL-script-compatible + block that selects into a @variable from sys.default_constraints, + then exec's a separate DROP CONSTRAINT for that default. + :param mssql_drop_foreign_key: Optional boolean. When ``True``, on + Microsoft SQL Server only, first + drop a single FOREIGN KEY constraint on the column using a + SQL-script-compatible + block that selects into a @variable from + sys.foreign_keys/sys.foreign_key_columns, + then exec's a separate DROP CONSTRAINT for that default. Only + works if the column has exactly one FK constraint which refers to + it, at the moment. + """ + +def drop_constraint( + constraint_name: str, + table_name: str, + type_: Optional[str] = None, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, +) -> None: + r"""Drop a constraint of the given name, typically via DROP CONSTRAINT. + + :param constraint_name: name of the constraint. + :param table_name: table name. + :param type\_: optional, required on MySQL. can be + 'foreignkey', 'primary', 'unique', or 'check'. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_exists: If True, adds IF EXISTS operator when + dropping the constraint + + .. versionadded:: 1.16.0 + + """ + +def drop_index( + index_name: str, + table_name: Optional[str] = None, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + **kw: Any, +) -> None: + r"""Issue a "drop index" instruction using the current + migration context. + + e.g.:: + + drop_index("accounts") + + :param index_name: name of the index. + :param table_name: name of the owning table. Some + backends such as Microsoft SQL Server require this. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + :param if_exists: If True, adds IF EXISTS operator when + dropping the index. + + .. versionadded:: 1.12.0 + + :param \**kw: Additional keyword arguments not mentioned above are + dialect specific, and passed in the form + ``_``. + See the documentation regarding an individual dialect at + :ref:`dialect_toplevel` for detail on documented arguments. + + """ + +def drop_table( + table_name: str, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + **kw: Any, +) -> None: + r"""Issue a "drop table" instruction using the current + migration context. + + + e.g.:: + + drop_table("accounts") + + :param table_name: Name of the table + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_exists: If True, adds IF EXISTS operator when + dropping the table. + + .. versionadded:: 1.13.3 + :param \**kw: Other keyword arguments are passed to the underlying + :class:`sqlalchemy.schema.Table` object created for the command. + + """ + +def drop_table_comment( + table_name: str, + *, + existing_comment: Optional[str] = None, + schema: Optional[str] = None, +) -> None: + """Issue a "drop table comment" operation to + remove an existing comment set on a table. + + :param table_name: string name of the target table. + :param existing_comment: An optional string value of a comment already + registered on the specified table. + + .. seealso:: + + :meth:`.Operations.create_table_comment` + + :paramref:`.Operations.alter_column.comment` + + """ + +def execute( + sqltext: Union[Executable, str], + *, + execution_options: Optional[dict[str, Any]] = None, +) -> None: + r"""Execute the given SQL using the current migration context. + + The given SQL can be a plain string, e.g.:: + + op.execute("INSERT INTO table (foo) VALUES ('some value')") + + Or it can be any kind of Core SQL Expression construct, such as + below where we use an update construct:: + + from sqlalchemy.sql import table, column + from sqlalchemy import String + from alembic import op + + account = table("account", column("name", String)) + op.execute( + account.update() + .where(account.c.name == op.inline_literal("account 1")) + .values({"name": op.inline_literal("account 2")}) + ) + + Above, we made use of the SQLAlchemy + :func:`sqlalchemy.sql.expression.table` and + :func:`sqlalchemy.sql.expression.column` constructs to make a brief, + ad-hoc table construct just for our UPDATE statement. A full + :class:`~sqlalchemy.schema.Table` construct of course works perfectly + fine as well, though note it's a recommended practice to at least + ensure the definition of a table is self-contained within the migration + script, rather than imported from a module that may break compatibility + with older migrations. + + In a SQL script context, the statement is emitted directly to the + output stream. There is *no* return result, however, as this + function is oriented towards generating a change script + that can run in "offline" mode. Additionally, parameterized + statements are discouraged here, as they *will not work* in offline + mode. Above, we use :meth:`.inline_literal` where parameters are + to be used. + + For full interaction with a connected database where parameters can + also be used normally, use the "bind" available from the context:: + + from alembic import op + + connection = op.get_bind() + + connection.execute( + account.update() + .where(account.c.name == "account 1") + .values({"name": "account 2"}) + ) + + Additionally, when passing the statement as a plain string, it is first + coerced into a :func:`sqlalchemy.sql.expression.text` construct + before being passed along. In the less likely case that the + literal SQL string contains a colon, it must be escaped with a + backslash, as:: + + op.execute(r"INSERT INTO table (foo) VALUES ('\:colon_value')") + + + :param sqltext: Any legal SQLAlchemy expression, including: + + * a string + * a :func:`sqlalchemy.sql.expression.text` construct. + * a :func:`sqlalchemy.sql.expression.insert` construct. + * a :func:`sqlalchemy.sql.expression.update` construct. + * a :func:`sqlalchemy.sql.expression.delete` construct. + * Any "executable" described in SQLAlchemy Core documentation, + noting that no result set is returned. + + .. note:: when passing a plain string, the statement is coerced into + a :func:`sqlalchemy.sql.expression.text` construct. This construct + considers symbols with colons, e.g. ``:foo`` to be bound parameters. + To avoid this, ensure that colon symbols are escaped, e.g. + ``\:foo``. + + :param execution_options: Optional dictionary of + execution options, will be passed to + :meth:`sqlalchemy.engine.Connection.execution_options`. + """ + +def f(name: str) -> conv: + """Indicate a string name that has already had a naming convention + applied to it. + + This feature combines with the SQLAlchemy ``naming_convention`` feature + to disambiguate constraint names that have already had naming + conventions applied to them, versus those that have not. This is + necessary in the case that the ``"%(constraint_name)s"`` token + is used within a naming convention, so that it can be identified + that this particular name should remain fixed. + + If the :meth:`.Operations.f` is used on a constraint, the naming + convention will not take effect:: + + op.add_column("t", "x", Boolean(name=op.f("ck_bool_t_x"))) + + Above, the CHECK constraint generated will have the name + ``ck_bool_t_x`` regardless of whether or not a naming convention is + in use. + + Alternatively, if a naming convention is in use, and 'f' is not used, + names will be converted along conventions. If the ``target_metadata`` + contains the naming convention + ``{"ck": "ck_bool_%(table_name)s_%(constraint_name)s"}``, then the + output of the following:: + + op.add_column("t", "x", Boolean(name="x")) + + will be:: + + CONSTRAINT ck_bool_t_x CHECK (x in (1, 0))) + + The function is rendered in the output of autogenerate when + a particular constraint name is already converted. + + """ + +def get_bind() -> Connection: + """Return the current 'bind'. + + Under normal circumstances, this is the + :class:`~sqlalchemy.engine.Connection` currently being used + to emit SQL to the database. + + In a SQL script context, this value is ``None``. [TODO: verify this] + + """ + +def get_context() -> MigrationContext: + """Return the :class:`.MigrationContext` object that's + currently in use. + + """ + +def implementation_for( + op_cls: Any, replace: bool = False +) -> Callable[[_C], _C]: + """Register an implementation for a given :class:`.MigrateOperation`. + + :param replace: when True, allows replacement of an already + registered implementation for the given operation class. This + enables customization of built-in operations such as + :class:`.CreateTableOp` by providing an alternate implementation + that can augment, modify, or conditionally invoke the default + behavior. + + .. versionadded:: 1.17.2 + + This is part of the operation extensibility API. + + .. seealso:: + + :ref:`operation_plugins` + + :ref:`operations_extending_builtin` + + """ + +def inline_literal( + value: Union[str, int], type_: Optional[TypeEngine[Any]] = None +) -> _literal_bindparam: + r"""Produce an 'inline literal' expression, suitable for + using in an INSERT, UPDATE, or DELETE statement. + + When using Alembic in "offline" mode, CRUD operations + aren't compatible with SQLAlchemy's default behavior surrounding + literal values, + which is that they are converted into bound values and passed + separately into the ``execute()`` method of the DBAPI cursor. + An offline SQL + script needs to have these rendered inline. While it should + always be noted that inline literal values are an **enormous** + security hole in an application that handles untrusted input, + a schema migration is not run in this context, so + literals are safe to render inline, with the caveat that + advanced types like dates may not be supported directly + by SQLAlchemy. + + See :meth:`.Operations.execute` for an example usage of + :meth:`.Operations.inline_literal`. + + The environment can also be configured to attempt to render + "literal" values inline automatically, for those simple types + that are supported by the dialect; see + :paramref:`.EnvironmentContext.configure.literal_binds` for this + more recently added feature. + + :param value: The value to render. Strings, integers, and simple + numerics should be supported. Other types like boolean, + dates, etc. may or may not be supported yet by various + backends. + :param type\_: optional - a :class:`sqlalchemy.types.TypeEngine` + subclass stating the type of this value. In SQLAlchemy + expressions, this is usually derived automatically + from the Python type of the value itself, as well as + based on the context in which the value is used. + + .. seealso:: + + :paramref:`.EnvironmentContext.configure.literal_binds` + + """ + +@overload +def invoke(operation: CreateTableOp) -> Table: ... +@overload +def invoke( + operation: Union[ + AddConstraintOp, + DropConstraintOp, + CreateIndexOp, + DropIndexOp, + AddColumnOp, + AlterColumnOp, + AlterTableOp, + CreateTableCommentOp, + DropTableCommentOp, + DropColumnOp, + BulkInsertOp, + DropTableOp, + ExecuteSQLOp, + ], +) -> None: ... +@overload +def invoke(operation: MigrateOperation) -> Any: + """Given a :class:`.MigrateOperation`, invoke it in terms of + this :class:`.Operations` instance. + + """ + +def register_operation( + name: str, sourcename: Optional[str] = None +) -> Callable[[Type[_T]], Type[_T]]: + """Register a new operation for this class. + + This method is normally used to add new operations + to the :class:`.Operations` class, and possibly the + :class:`.BatchOperations` class as well. All Alembic migration + operations are implemented via this system, however the system + is also available as a public API to facilitate adding custom + operations. + + .. seealso:: + + :ref:`operation_plugins` + + + """ + +def rename_table( + old_table_name: str, new_table_name: str, *, schema: Optional[str] = None +) -> None: + """Emit an ALTER TABLE to rename a table. + + :param old_table_name: old name. + :param new_table_name: new name. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + """ + +def run_async( + async_function: Callable[..., Awaitable[_T]], *args: Any, **kw_args: Any +) -> _T: + """Invoke the given asynchronous callable, passing an asynchronous + :class:`~sqlalchemy.ext.asyncio.AsyncConnection` as the first + argument. + + This method allows calling async functions from within the + synchronous ``upgrade()`` or ``downgrade()`` alembic migration + method. + + The async connection passed to the callable shares the same + transaction as the connection running in the migration context. + + Any additional arg or kw_arg passed to this function are passed + to the provided async function. + + .. versionadded: 1.11 + + .. note:: + + This method can be called only when alembic is called using + an async dialect. + """ diff --git a/venv/lib/python3.12/site-packages/alembic/operations/__init__.py b/venv/lib/python3.12/site-packages/alembic/operations/__init__.py new file mode 100644 index 00000000..26197cbe --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/operations/__init__.py @@ -0,0 +1,15 @@ +from . import toimpl +from .base import AbstractOperations +from .base import BatchOperations +from .base import Operations +from .ops import MigrateOperation +from .ops import MigrationScript + + +__all__ = [ + "AbstractOperations", + "Operations", + "BatchOperations", + "MigrateOperation", + "MigrationScript", +] diff --git a/venv/lib/python3.12/site-packages/alembic/operations/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/operations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cad3e87086c530e49fc054bccfb8d53adbc19575 GIT binary patch literal 484 zcmY+Ay-ve06ou{Pw?%~tF(L#KLdwuaz=|rt4h%45v7E$etvYe!m{uM86zps~3vUo9 zVqjuJbYtQ=Ek(V<(RYu}^_BhB?RF5<;_RIdF+v}v`K#6%mX83>C`Bn2D8muPhFhYQ zS&`MS1#Cxl!#1!JISo6)&AiBK*ah|@AEOaUy%!v{=YG)sgq( zDl^@h<;?f}_ulvJdsP4zNsb+>K)$;5-hFrZ?|=XMZ}-3dw7tD0hR+vw|7mveU_AEc z{9!%~DiOc+cx^29TFi~P@my>+J`s;IU6ZSst(~ZqbZxF~q7L_JbM+JT_^!(}%r;Im z%CmZ;n5j)@LQw;|m* z(JAS6q`M}%B;A4Znu#@%?nJtKqFd5kNUxn(E9o^z_e}Iix*O?r6YC_s7U}g9>m}Wj z+c4WZ(TnstcYSW-?52rLlHP#y=84Tn_qrRA-ZHU8(wlNyXSYpkll11?mf8Cz?vwPE zoHM(9V!NcbBHcI9C+TfS@0i#j>HBg!XLn8PipMfN-q!#-2kqEBu^Z1~?)KcC+5U-s zN%!RvvjYtd~oj5A#gGfI%@tC9^K>G2C$0dCT>EVfCNk5466BAEJ z`XQu8CPpOvFw&zFqmq6E>9L71Nq-9IV-v@a?sbQ9PtHCy@sy+=%^jb8dg5uM4^Mo$ zCYD*}9{EOGVe`ZZ`F%9g>^_!hoT=G}+rR(#H){0LlQpp?WA5vr{`E&V3g^ZUgq+O<;&3s`Ylb>c4_2Ub3Qdiyh z;v8?ZkN?awW2up+#zsDU{M3`U(Z~m%O|!oG=kh4;>!=v*f8hs&F7HmI=cg|q(OjIq zkeN*vrY`<~pv9fKh50ON?@m3N@z8+L%uISAH?ON|Eq)=FPE8kP=hE!OhM9slo1PCK zm72NidYKtKT9?fi=Q7jtDV@bT&!2d(-aI&;nV(qzu>2?8sa%$j%B7~Vd3P@DrDu`9 z@r1gGWc|2?Y|BU?pGV>78g93Yy_lI^FeA`DQphdL=EriG*-V~|Y?Zf0a_NO4q17^u zQS^T`sNB5&tbhVz#adg~;j#SrY@Y9|eS+{hJ;z}rJ{+H&BaB*yU01z-dJaE3<6TI-W0PJpICmmr2iOj_2LXi;|&7-B5if%(73a zWb3{vY1AvsSv7U3-=XHT@?KDOH-A=?ESanpD(GgO{mcoer|V?)Jiw6gp+P`sEYWb$ zSpX@F6DLigrqtr+FP1tZ;4Iao#_@k7iK6PK3JZC6nv>9cWa*2=;yx4YedZI9dMuEkRe>Z%5Jhueeu>lf=1J4@ZeQ=Bu?Kx{L?md-qpE@qtR zT)J3vKtY_~k(185P6UCHEiDc{$)p2{RRrFpT{q(n4LJvr2M#3ntEWqQgchE)aCIxf$%pQCC$Tew&)RDUW%31$NC<)R`+((w&m8&YloM66E}O4 zH+qxTMt|k_yS-bkjsAznzrSh6%}obxY&vlL;K*B>Mz7WVO6M)S8w=qMM^A8vlm3@t zzZCza8nEd#*J9V=*J@^J-T2FGS8K1-J{wEarf&dU&ScJmNyvD5bp5W#M3}#jEvk88 zjmJ1|&Sn4;*Fh@JRXLYibg<&N&V0eabPz=Giz@oVi>LD!G65o3D5RFC* zC=!|dcYW-MOvgz*annWTaweBcVsM9ZIsBKg2{6$wC}@|H_A(BqpRC?!{R`PN>O`lC zi-5`OpmU*cIfGd)tycH^VvsAHE26tEq_e_=IcP!JnOm63Wv89tXO0sVGwJCp_zbLI z1PfR&XL_MHUzqhv2^wQT7^J0`$*}aHp-$JMjVvH3=1l@H!)m~S$_vhiPyU#-Wz zMwaDu%(t2+=H54S|MGD+ezoCBgV#OZ?&m?xR~nYE%Db^mF_ZIZW4^|1y4rZ9(aKrt zHnRqkX9;CoZM0fdhx#PP5^^Ek!rb^4@!?7%*1=fN>fdkshNin$o8~)BYc6(~RPgP# zV>OIjsW;-o)%tmip!!_wF{$9&?R=#jw7&jI6UJ|SaNnfdt~ctJP`ByF)#iCNR6g^Y zP0H{Cey)})Ef=?%r@^gj{yy{kO7oSLE3KyG@!0%!^W>E_w6gV; zc7Z5xkspX+-LJG&TgiSS?yg<&NzW_oc)srMHKM?yfgj>-FHh z-R4Q~ZCaP|p?xI=D!Jj5$EDWWgiEP*I*0YepMFK}Y2NLM3MM~b2frTs0co+f0hiF6 zWN|Vl$v%QMV|6D6p{|-!rT7t#ivoDl_=`p2vEzLj*HY8rTzYoOO&?u4US&P;G!akv zO-mlm6{bN!jwa2s;EpNkO%z<@ym~42=JeNwf9t$=5)U^G&sX=nx&4g+>HpG8B(&z^W@emS z{rU84CQ;ngPw_EP9BMI7&G)W;w=kXXD(-tCYCDCw6c8u5tG`%S@TM~a`AwF_&_%bESB4sd-W_?8#D3DwjE*o?c9WSTnyxW#vm`!0Usa+_RI&>=Irhwx`FU>5?QaqbkYSL{6cW%7ix&6EAw!Hq?H^<*vH}GoB z@;2u;FMajWS6aU>Hz(d&x8J|{YU}scCcf79*4n+-M!_|{{+X{fUOT?L7Kzut@aoYw zGhciD`mUkxtR1@EHgtXM(7W4qzW(`Z$6rmq9=~?#`)m7`*Q_ho*7S6IxBs#C_4|X z#_hk^`PI&HP2HNVa$9Wc_T?SPu(Nyb?Cj;hPB=AwYIM5zE#`4vE$nD z_qWA*x4iM>Zyf)%U%2+va$C=rPrq{d_0PZg!u2i5x7+qD_wBqk^8L21S2w=?)SJ_9 zw+(!^ZSAWgulKxOeDly(7yo%%qTCeg+3?!oUpxHf+W&ORZ*Td%`Eq^Dng`3ZvG&d{ zfBKbAzuk5pI@h(~Rqy)d18;X8Tt4u?%>z%}IPlb~wXcqTrTOiyHCDE`u}JHoc}ENl%f zAG{R%zxqENZ{Ad%sEKv;Uh9+w-m@(6GM2S7zATXs*lM91B2A>7^H~U00s9|S?o3)d zuofh}%v=s4+K_Yk0^S_=7BUbKa=F6gqN5*CN#zt~AUNOw^jYp=K!y#JKnZ;|KWGm{ z5!E7p+0??0Lvk7vlgU#R2uVnI_w14?0(DL;WOMU-vw2W>qfBs$3)2@M3IuQaf=ft= zHt9?)Iv~?8WJN5H)VP5#I;mVPMW$-8)SmJ(v#^$^pU5Tb0ToQ8 zn!=pXFDX`_QER`JHdUd``Wa0?#r?SaYmOaOl8)Z%y@|Kl2d>pDH?`bs+IXXBF>{5JE1?3Jn#Ow@| zG*nhOQnXJTace3A^-q2tD&j08D9}<7NJO^<-uFVrQ%Wjb2WBIx)0EpUhOLmsM;boJ zhLeJ3rx#$168hZdWv0ar)8`cD7tzhaOi+%|qhhuD!a~+VE3j<9Is!eU)%A)ZsGOoM zTi{^nkPalBVNuShb|IUp+I6V?l;%*!Yak>G-5?-M)t-_i{HB3I`$}2IMbiMU7tm=I zgidmKkP#Y$Vvve2Ksqx!2P)?+I%J@&<{wO|g$l+!EMs#NTxPt&tlZ4J2=yf2S1r~W zL^e`xSfxCmN{PIHF1j#0&1GCG>milZFNJzc53BsFjB>SlBP||B@BBV@!=;y}pFa;aMDAXd*|(M(NFk zQo9bIRDi+8uTmIw0^7U<*aNu*HqPhB#d{0jV9&#BU7XjX3}kVJ1LPOtQx;PC>D&Tn zx{sJNIn0C2o&zxf$OD47r)Xp0pkEjpgQ)`&rN>k77-U}48d(NN3kvy6hz|NCMCc)w zf1zlY4Nz62o%FN^iv>qdED&LcqLRg+@iwgh?vw1y&+SoU5}jQjKgbR7Bl zpcP_vhe0H(G+R-OY^7W zIH%pDp1g(P`prB$YH()RbmJ={{g-PRC*qZ zwNlctd6ScUWNiB;CkF*D^=j#6VRr>h=K%VR_e+wTsy@&Ztb@Va!)m~3!ttqG4FhsP zgcg1bj6WP>xKxUOJvk}F)4xe77}T%V=cIF5>QK-Ns2BT^yPRGuqBpXV;6|RNMf|RS zsz0BsQ807k=jjxcqxaBaQjJV<+@38#i)20vmV=cg-f?a#Q+||&U`$BRP%n5dtYODz z%z|ctMFG{CbiiX{^9|9h>UCJtliG$=4V-$pnwcTZNs&xh5PCY6m)EG3~y3CSlf_B+6N!3WuYT4oJLWu|PGc z<|iMl`CUDM`~^Ubm+l*~?bBoTs6G4`A5DYJFbVBc9H1a!Ktde)|9M$TEvibZAmM=m zY(ne&ibbA5Ll@XnmM)w+@a*!1f<;<6?QVH^v(y^pDdO0$c()jHVzC;4fNQ7 z3=$riOPNKGPjEA4T!N;AdVWXaqUMq2K-CZg#f8SgLJl>=^f%JYMmseTHe;gMm+Bbv z6EY^!O__}!%S0g?cY9Jv9yJ&dy#xs^n*mX?pg^~is>fmUFG_r zNkA~;podVJV#Q`y%?bDoAr4Y1`3-yG1vP6Dq~QATVfq~bHt2*%0UtYHMfnoYS2cyq z(kt^8voC1WZGi|L4FrrnYp_#r#E7HnCOJHPGeSkUtfC=-PY-yQ;30Q0F`-N zUb|di<7X+TsdbnvqHt!IU=Ty}l+olRfJ0E6ptZF;R^@qAF_Ro&7|=<~Yz8zS9?sZ{ zY5KpRC(070^`S}(HZAN+L%Gm5#mh7RaY2*BvWbl;C3Xap+LeThM*Uk!S?@52FS92C-?1qQE39khA}23j=@ zDn~282%<7Dnxf_m1OrMclDP?Fsm3r$6gmJ*sqo}L0r9e#gR>ZD{IU-x2Dq(oeNm*EKQ^}>we4f66B zFJgA8pUEv0FC^NPH(HD01gLH(H7E(O)MVuBQgvG$+TN~QCTCUZ-zbV$U*P50)*8lB}<)*eTx4hEwD{cS2NE_FehTDca;vus#Jad$fHJt** zy$#>d4@|`IrO%yF9$@4V%mSV~Y4mF&$~WF9x`f-?6X=x#3(Tj6;N8we;pku(ghXn=WVgBo|~P!ZglRt-n2`(g4lLg_O6z8Sek%e zi!H{yUq(V~j$f})h}RrwX!)k2T~!*CE5g#oDs54Zev8eZX%VLDPaVFiy1n1VYelO% z?{{%ymAZbHbu~&|<(;vvwI6S7|CQCVp(B-b5tss1TGv-Xq-u6;ZV9=_wvWLWcr{$DsewhDc3*Qx(6;cTIOWky@ps<_p3W! zZ+@$@?|M^T-C~gPEfjbBU6~83x#LN_nRN!ca zVH(D1^%jXN*xX(HK^wKJEtRhG@P`V7ezFl_mQdE}Dv%y@u(?B80}Q9r6~u4o#~>EY zKr!dHM{)?V%gUS7v4gYVs(12`i*!KkS<)2mB%~PZXfAOXtPa`3N%2fJMHPn!?@G zeCox%1dNJ)pNGlXsK!K1Bo2gJy28=oLu;`h20KcS_ys`29+Zu-fuA01$Ph!sX97|p z-hc?a|O|u8>Ik+YCWctlk;Gpunc2t(=-rH2BtR@Fi*>;489skRj8;N{7y|y z{v5#HHw1x818P@)z}F>;scJ}mRU1l#k@kx!EX>a>_#S)8C4|DPExv&xyCb{+rr3C( zRTj~rMoyo4cKpon@l)fDX+2O#ThsbqB;5NC3_ANmI5iF&p$9g#E*uG1J|RW|jgXu8 z6LdqXQUuGw8U^gW3S4V~X(PDDG7W+9N70&(-a63kgkojH+1utSO7WSco+|vy%pY+{ zp(7hSw-9CkF3qFAymY;3qgo?s@xXk@-{5!r)@8{@D?R6W`I5DJG3y8pIA3J$z2Ew^BGzJjoS*tw$O1@McGME$7oseP zT!=uYFg4Wb6hZ^Pe^~u7%mV`%?$0bN6bA@Gk8s$B+TQ6#;CyDk?~zkWT8n2%^=F+GFjo1PD1 zKvqZ%lfQ9Cn}+sK{1PlHS=xPpdxQZ1c+nRz1F+h0&xo>ju?YZC;2@$LwQ_+XSh>(? zwW5XQw7(no32Q;tD4>k&fK^86%rGIa3)tp)tGuiFV`5F%4(=p)*kPRY3VCs17r$<9 zC{@eM4EQaQKY#|CUEnz#Vm9};&CNd`EB3G=LQ(okYz;?e_3J8G!j`VDum2?&bZY3M|aCqjTF zC`Fs@EuB(q0ymFKB}Cr3(m#?jLJ? zK4lsF_kwLP@{?urm)0f6ky`}}30MpCWlF^`APJ{r!69URDr03f3gpPBh}Vgp072t| z6$Aj37eG@NiVM(?(ZkULM+Kq8#;xF8-(m9^*)4C?n^DL~?+$Fm7i$6Cq~MVdyls1! zk{=??AZ1dNo&8XQF6{~N(W|%;kets#QH9NIcY&t?o6E2}^@Lvvd;#~udfRc)GH9t@ zTIBs_xD}E@OC8}kzqGvy;}FjBm*gU-O9B`1d2kV%Z+7mz(Yg0)?l;H2z3%PKC$Bd> zsTd06^FK@rL-7O>E9wrSIvnJ!rZ*7r6!EoUmcB}>pPW4mkh8~{k)Y1`Q+ zz#0shKe`5sZR3ANqTM7xG<0Yk>`PScZa_8V-9SFvF9T#S0~brKshs&joWNNVZ>*L9 zuhWu6FSi<#bhCUEnw&fZ3l}|L&yJrSJv~GT6x(P7<)CF04&3M*xY@byM(4ikP5Ua<3RF{pw~XIHz%sIo z_^CeCSjM8|ogeJ1{Sx&=EGP0Y-uK*!|C0S?%{MfT;mx{gnxl&?k)rSubyK}7AGbCr z0Rd?0bB(i2SL?CWyUuOMHP5zOZNMIk`itBZ9r<9Vfv)>%BeYTt_#eO0h=2e!zZL%n z4Owih{~c`oH+g`ujp^?IuzJS;F+!WL;JrV60m~`~F9dfLA#2e0P9d{nxY8tpi7EYS0C!zpKF&>1e0U*{) zun0?V=bWrt#h70eZ0{8^e-#7RKw}>nDaB8&>VT6jr zb6*@E%n+cZ`dP%i3FR0(G)z{&5iD&EL?Z+hI+Q%{$o=4t)&7sA?IRKaO$sKfol^Y+ zfJqzaE4S2NJ&W+vX>M+r2It9WV-nf`t6b+WZwAPXTP@ z9eAX)_Ecg1cn~>d48oe{{UgliQln-XOHJ9LYFnx0vy3Dlc}nXoUIvT2nO@#M=DVHN zLxzU){!8B71RK0^i&lHCxC0I@^Hi}Unw0IKLx7MngRC}vH!9pIBJdn~N25qys!^r3 zs8@Jbf*=?=pzKo3K4h1+R9V0Mr@Kkozlc_1>3&F7?p@#M-1Xh=UDtPyywyE=y=`>4 zr}wp!Upe_}r>`|HxAt6b-69WU!H#?=%g))0BL7-Oiiq9=oybv2QoOweIO_Eq~m(@vF(NZMwec*f-tpY&!O>^EWmf zdn@1+|$Q0vwaG)oq*rL88?-`OWBW6PbBy?>=Tu8DZ{X9@CJ=%!`!@dxextGK1I zE^QmpRQ2pxGNRnad~VS3lZp5-lrzoRsr8TzkEJ^D(o)k>ckp^@1TW%(U6QiYN|OoB zrJmzdYhYX3*+3FImss!p8cX~`Uj8Fq{)m^qgUeFi38e@*yN4X=o@24T4p}b)(!$6!>aukPEkAq_KHNBCND)j}(R@5*fibkG~J5#A3zGV@2 z8K9@sGO9Ne!$p^NGVcWMwu-Q))^zVny!{FJBV-Zo4-B zlctJA4c}&`nBbnhWgFR&i-;OzTa)$`onsBGh2_!TZf%B_-YgX2NDU6LKKbvw(QlI)6Y=r6C4WOr=C?($km z_QW<0l-EgeeQe7iRadz;*0SS=8+ljyP_B{W33O^Vd(lyatN{e- zI(k|-9Ba#c7=j<}KuYqcrx**obws~)?kV^?Y>e`FKnH{&Mebq~Gm5RIXaWXq1~J*%E7Md%u-Qg=j=C zt{47CZ#j5TjBrCt&?QY%64G2G7ZE&01<~QQuHxO&G1lH$Fp_fklflV{}0+n4PZ>tgvgaIEQE; zVrbz0(n?LepIxaowT2tAlHQgXvZNWwxYBV+gg|Y5fq;jNx{y=!PAG^zTBdw~wgyOL z@Qu|f8&xZ$yEN?L-0`k#lvt)S=~>+^Wd!l-4!A%ukNZ(4QHfENYlR|hfJ);TX!EP+ z|H#7oP=&W0`F|~?Yne@?afoR!0_9&oP)}HZlwJusOq$8C@fk*7B?(t=Cm1jqDj=)T zeLz(K$z4?8P;cQrw}3L72?D?5luG-&1P0J%HuZ`bd-#+hW{lot1A#A45LXV3ZIo3V zQ2{ZNexDg;20Lh-lcspEht#`DorFf{H6LSOH&^ckBN7P69CGJ~GrOqudm07$gB{7! z^DiPpw9de4K@%z=GSbYNPNvG0RQ2J;PZeW|z!x}mkOJ`%{ZFc?xrYs2RZ zIbh=(@9|?JkinY5T&QS2TOj$HoJv+0)W)3v$E0XnA|49`?|eG1V>1B()Z6`iC%qWfx9Q4IrIH{uw;P2LKN0GYuV9vyW? zPM>)04C!0gT0yb@=BZ{Ve)4e$O|LFls4P1t+UJ|!kuW*U}86Q!W2P%+Hbd9Q$w7c&lLLN0>{ z%4{IQL;g1T_wppq=f@1}J93Nq3>)i%u4a?G& zVZHs)?~^erV{ZG=I|98MOXBC$>2c@Ob0%0*89V;uDd*E;Kcn1)l)Dot0wO}A zM~K<+%$egShtK>B^MpFal1M{BVY&Rb)VA@eFZgB<5Q7K^Smvo9a4^7-kAqK?Vz-C#}(~*X8 z%@NxB?=cXDT>{HzP1ykYpaW0oDA+-q8-;jkISRF|&jLwb&@AIyM;jh!i<(T?d5|%1 zGqB2QUzTuBjYG~VxMG3A0s^Kv?8~snhfIjXI#3veWqN`4am)mnAl&D`C%_#~)^{@o zDsq4VW`rQBs2{X_C@)w#h+y;fG^~}R(WLX^1zBwooY45WfFZnd=n1q#V77fQEL~l) zl0m2vk;5m4p(1tg4+)VopOrW{vfza94XEZ545xnzP!T5{x;16o#K*@2z?X3B$+0ux zsl@>eGXbFv@i9MrVa}jx9!0qN`?B2oq$`8TFyONk9~oX)Rq>$m;(-O*gwK^p?uv>m z#+9#J8N+f z548}i53K9rmS*q8VqH<|otW<)Ngi@AA1ucKX2s_&CQPUSc$G9mb2(hjge&97S(FZ7 zQ%Dd{yvWKuFktZ#rn?{eTCobk98>(*r!$N2yzt3j(Ec#08zF?NN*Xk5RzxWY454V; z6MhIZ+6&at3qplga2Enx#h(L$G(um3Lsg8j?|ce)sx5;AKcyD(zF`hi12i;TV8ZO) z7(C1^e(a7C-XFe4sS3v{%`*etTBL#{#r z)tYap9~2>sR0)jHt}4O^Az~B!7Og#*go()&cxfc*QAtKxh4sXiCOSMoQ1g6G6(il6 zwKaP1GX?UrM_a$Xb7lsG$!>zjfk#L>|7)6weC6?=hKus1>Q?a8OOaY&n( zrFvAX9V_gyQt^s^iz&S5C`Zq|p~3w4CeHZrlVi`051)Jn?#$RGQb}TW_*=gFM#qi~ zKX(E`{OQkOJHBkx5#JZmHiKGjcmUYTJK-fYUG1yHdzPS3s=tr?GnTU1IdGF!!^1;x zg#Ae73zwmlsw^%-ppBO>X`YVarc_#LbBkt5fP!SSgffQp8v1?Jdj_&F6JsSIeo{-K zA0Wu9-*_BpV@tWB@M5S?`*$P+ug@uB+H0zG) zVqiBA^wW1W^7>q_Oz3bqM1f%5>LmknKb*?|CnTqTA(zaklcI`AHV3Ciup3s`DWZS| zbV!#w#GPSOL}RA8Fw0C%9zP~prm@c-e|G#?%tZwUYp)KA!nDJ-8^5Of1T!x!>5^nzK(I=b(`;!meAL0C~ z4*`^SVGP39EnosMO?0HZ@QYQJf2}pNAZDW$XpC0)z|;tv zP`Eh#k|?PIM83lTJQN84g{P?S-5s(lA{ytjCH8yAT|peCP=lh6|I zF-z+Li&rwZO>a$oUQ>ClF4o>9&J)KHEu|)7?kXYTyd2wF>Ig43rA|<4ZPF03<uAM)om?$1%frkZ1m%lg1w=9+t5JZ6`NgWX&yb`ltKJRF=N9vt1Y_Ws622vC8I8-m_7}1JB*lec3 zG#(la3>dZm$VSD`n^H98ezSQ_4Ut>mu41D(M2}}NN7b(XFn)|z8%yY|wQw2HVH)X9 zc>rKdC7-GTKPXeCI{yQRi2;Lh)!yERkqOoj)y&J76l&`+%Hg=MJDRzU%p%PL2>Y&? ze3-yEbA?~lT(0tbzb%u0+XB=)3*$5=J7$F7J;@-HBTBcT7*x6nOyQ8HJmx>K(0p{m z4VDM|K6Z}k$Z(UBWPr))dYBl(OGGAvX!>M5Ae2<=Xy+DB4zX)jtu#FMs#OgxQJ@%V zvh0=!HareGLlnmlPS_TR$nyKU>XTe&A~b-#sWyYu97~R>VAdmY96UpifGbw!^>AB+ z?qeksMK`mBAYjBAQ2~Tu;5=)h`>7pmeAf@TVct{tir^z=xcIGyOt0P6zEh2HvI7$s zhu9e+;t5wFLquh)v@lej9(?WYD6C?)J(H2?UHJAcaW^v*6P-$!tYE6?B zw?an?zt8NSv4=bTWJtomS-JIwj03~PpDB#us)s_hqe=*cgMOu0C51P!+%u}+6V?6T zmHkQ^!w@oEbYgc4{1SU?!Kw1}_y#K}~imcr{B7_Jc}nc^sRJ z1E(RKu{_0ZXqh(*3XRyIr8_jzcM7<>9d@?MtOzoYV)?Z#~TtL8@AiSqUkx=Z2W(LSVfOuhp8jASP zcpt9%E+>JB0Tr1H%AqqjSx81jU!n-O@&Yvuj4^l`ZlxjJTI*}i5@MB(j>|2F8h5fm zSQ3*t3tv}P^*UG{dIFzpJ>%qe}c_#hlOzAszOu%{j1hJ znfA9Y3Rp*3V#Pj^BC$`%2Z~KEuhlyhp=ZH@5$ahe*Xn(HjwS}qdm&-qZ`85!q=Yi@ z$?ciNEL@VT3TmeFWHhL9A|!S7p7<+$WPuNLL}wyi;eNP&O`ZTY$SRC!#z0mg)ZTu) zbh+UIRV8|Ptcsmgd!z_PK+=C|bjmnBf_@laP*Y;q8PZ;@fheQwG+LRa7|n=1EW-A3 z5-82CxO-^Rm)3uxqP-g5rk&@WJ$~v*dPIZJhlf#fU`l*f&aI?TO0}wyjnfS=>GE3X z3k?I7Cog?jnj@_ampO}&Lm(W=jS9H~)gX@E@go5GMgh-3#P#$X=MHHJBmIRnD|YWJ zj=rh}ufx*dwJM}n8nb06w!A;)rNqmh@Ioz`_g!Az;pJcQ@~6CrOLxts%SCRs6O!y- z)DGh|+jD(sgRSwiT-w`e1D)R)oH(p>e&g7n-8xHE5=60#H$RPB@`>wj{0?c|zKu8% z+6x?kyccqp08zf!7w{RDvG87ez{g2awM!3I{#nG$5l>ZrLx@l|6M2l|BpskG%!5KH zH2xZoah?%&M{tiMP(WhFXav%A!cIFDoj-eY!Z^67v-7Y?M9S%}sFB%?H_QrFTQ$dx^B>g7JVP&{ z7tytvtdueRT?Mzvs|^$!2E0PJRCys{?qzi_M{}m~_*u-<>OBiXp?aIoR)q;`__-CI z@F|@k;Z#?>P-fsq?XN(b%BKq#5P<^jy%$!1vdWJ)Y%MD){KiZW@2@HXUO zi1+oWIz%iiMBMxWiUOx2Dk8bb4#FvewJL)rC4o?BS=lQkYndFl3bje)P6h1?r32h4 zg*TXD%D~k;V17DgGI|+?OMf5d^24NLvA|L52*5#CjRsIVa^kYEVfP)N*xVB}m>8q#E83zki-JqdvV zGY_^9_zS{=A!v9o;QlTtfA_yE#?dnR^kaBmFZL(&8TG_D+Gwb}HcoNqpwy#}$cmN| zf&~R7?bKUk#8?t^$AFZ5hNN09SUOj47`}9jQ>Dt2$pGO?j_qLq1a}D4qYd&Wc@*>E zZBA!iu=sRNfx=Lk^HGo5=M*hS`YlHVVxBO6kd3QLyh^HD$*~x#q!wAEch<(#>M|Ka z_Jb4jL}`@DW%4UX5q-?G!ZUFV#;b^iKxY`ejXjZYjzYAEVoj8>j-JT?E*R6z#PKyQ zB1(x7D8qzcE15i0fufI?3v!eJ35_X9%?3`!anMCJSz-yuI)nuop8m@CCt*25Wg6cL z5=9X7!1!v9kJyEhBgCi?-}X7F$a|i*8Mnwt=bEBphN(pzk7cV7X&}sx2zMXQ z!0JSp(*2NgO6)DdI-Y<%`A2J#8MPRtk%a{7Nb56 za!?FiH-xb1mQU&!x?*S~Xc49cbdBVhzIU>?gsW$Wd1bv7uwhvETF?@+^;uRu<(7qo zz5t;OuGZnAg!ACrf}B;Y1oA}tz$oRE8ZEtwuW*Vha9Q&QXqR$9g_a$9!C;=q@W5`P z8?Q2n+PTtTVt|}4uycF8G{DJ7<2|Bq|6rL zii|VZ;c1lT0|teVH_@R@SsWklY_xVD6HK3H6XQ2E0h-#p&FF zo4GwVBCH5=ufS@o#)kCwffvpe zKmm3z;3)eX)<#XMg?$Q@q$HO?$Jie|#Pfuvk~T}yczwPZ1SYXuMNmMmFI)p7Jci$- zs+9G^E@(zf8v^tMbvUQg>Q&vFQ--W!^-~L%S;k}`_Ja$3?kQ~F2|-FL1FClDlT`~7 zT4Tdqu3BNknx_Bt%5MF?gSbkR zV-7NiN}@m8>@On5yhMh@#GCqS6hgz30k97UxNabWxUC>Z&S*Ij>kqUBD_L+D(`~iK zt4Nt=h4wRwzzUk%v$O$Lc@`ngV*7_IAyEv$;~N)IP|m7Or)r#&rXG84dMs^IB~QU&-DeYS##)mUC|-b6$k!g*tGdP8^wu-!%)U!^g_ zc4+XhrWtTol7@)+a4*69pcvdFA9$-cMp)$6O2ZeC=;KR<)O3ulgxj8`21 zv&7#)4|I`Q-v6=bLBN_=I*Z$BPq=+Q4E~QodlKS0|3^zIZ`{uEo4x6~81%47@m zPP3TWHIgi5sm-PWr4GM3nM|dYU}Bb9ty$!i@viqCFF)kv7BBDf@>jh4pSXZoga!!$ zS!xyU$!^WPT3i%QIni6Fibi8^Jw9UbtD`kWL4h;A#8b0DMV<7xOWU`6K&2s{D8Jc_ z6V;=Z515-WFRs98nyoA$kOsR_$?a5bVgBe;1=2LQw_QrXBSs=5&mVxWeztF>P@vF0 zmG=71MOeCHTAL4YA+JNe)rw!W(H8sww&GM*#ps=Mwl2TQMmoLXL#hKx>!ng-`BRrIRu(s=R@tyM)bMaS75a zuWwc+N2PWpQ|Vl7r32007uE0H;qJy@P^#+$R;49S+!bU7{tb-hqR4|4i^HfWaX>DE z-Ktqqod=d+F+;TQfY&fOBO$z^Pp5&8`&`A`M57hPbtbYJ2O*~gSqe~ORoPCPR6cFF z%nY{8huj82l!tf|m~W^S(~OdTyuC8tP?;rd$GHI5p6$7+?r>Sq5}^d;HX~hNpI`@v z*#UUU?bz6^$ncNZnJj}03w!A+OHKHEPEYyZ1X>g(1z-~;Vz1ByPxB>@27n!i`v$_r zC*<&I=LMO zO5tv!O9Pb*Y~`2f%`P9UZ=QQNAr zit4AS`2sd1;J^*+PX)YPSiQ3dZjOB`x(@bM2M$=lKSQ5L*AgrdN*jmBED~E#*#)^gW z5VSKT%dezxdaxtg`0EPmQuF zOmF`t`hnbGFqPYCm%u<+ra%0OYKO5mE;tY)(29naw0pA#!IJ%x7&S43Tr1eHyf~IH zSuu+d^OaEmSgS%c1%UPVzp>WJaeu9Zx1-4Xh^;0cvt&jc6~2cPuA@t)$aJ?^EQ6D~ zLjA2S^UK;4#6?6@stg!tm;hjXFyV^Cy|$R0%_0i2a?%Djt<*T78sOgLGjPTlV2k9+ zy>49DU=%`14BRQi+#-|C*jrH~HeP@$O6)-eW@DfqO#idPXGWeHJ~QZ?@>3(i5rtY! zCTIoOzan2C&&xopU`4yLh^?@5-F@JH)qeksA>FV32lqoJG0_mzj)xU?znB7)&z_vo zgJu{2VO!k(YN25XBlG4*A89u5v@rW7G{Y%Co}?(4nVS!9nlktm3ZA6>H)V`hSh>wH zJTNiIpp%=ohiR1PvzJ0_E7xT^2l`gqB^s1*aFsF+u2jbDJzO+nF4Iga@lT1qR&5Wc zoU!_a=Le>h)@O*slSVCuPz1NO3W-oldw9jvE{aNhnvLiHeVP#p`sI7&ty&fRJ?NcL zy}A%(02k^8?M?=)o;m&PxH@M|(Y8d8Q$p=EC=_9Bpdrt$0 zyefKL*|G+&B&a==Obc@dHp=D}v0R0U^AV#GEyk6Ip?#_hXGZlZ5tosnfLM_}Kq<>* zc1W_Ug20io?bS5gJl%SKgI&>)RWwp*%H zZAaw#rG-hbAvk3RMXq9*l%Sx9@~HN-M1s|+h@mPHn>h)W>T`9(bYysd2`01|P;JF3 zO#nD}5DN#c$x4SI}{a;Vix#C-mWq=Bovk(>?*3D>9NH|)1;;QEWWZ@h7qWFZqd*8rC9l( zanh);XG9$`@>RB?YP)QpYFj~36B>v+-9>Vj&KYVc2(AIYs#*!~n1#5%Z{Z8U_EB;( z%7GW|8p2#g9TI-R%o)##wS2_yZkX@*#P1dagkjx2wBIdpCA__)Vm?@YwjtNr(1f^0 zKU*wBQ9J7=ezsx2-hrR3tbVYZzH);ba3b!PRXB8i z=94)NjfKvvYKgcQs+-la|J1tj;b=AyBVg0T?s&Sw%pdAFw%H6F4C#-Fep!s(ul0IOKSKgX>}#v72Q9fii) zIhmdI3dI5(0%2D{&>T5`lZFXIJIQ**$hmhiIoPpi2oW6++H_X={YD(*P{CdmKGiyW zC)}DTc!hJbE-aQsYz&-6C~oMU7y#t)7kGpkA*UjfF2aSIeYQB`$iY37bs6M5GY#iS z#8of{!Hu3d{S0JebM~8v!BS(uV^=l0ThP`fr5_=@&54yk8~-{WuKWEF!d#blJAh;I za7P~ZLjH(=4upXU3+QH@W(e#yQrM3e`r%oEi10}ZBE@}?8yFw)B#fxq?{+?zv2mQ! zpa>?9=Wi3!R^yI_GuenCUP1gkzo6M^yn6b&{Eo|S%mk@ z4afae0M85Av=gC25%rG0W}5;d>o$pSP+J&WRhR>j6r>r| zvm)jx&$}@;0YukRaVvroB(RjI4IZJ9arS6L#LV0?==lqFvajkxbk#iG>yi60q9E?(9B#=2Nsf&C6#xQF!i3^nQHepO_*C3c@- z$5amO@n(b>L&}nMu)AJ8M+w|~;YN>8i z`(z7+EaM;b779(MVk9SGg!5@sb*(~+5F;73(9yZU?LVPeftxH~R(Q&12Br;pGT%`$ zRmOubnUV?qVaZeovsN-CNaLlt+tMkU>YNH$@K#V2e6l8gcq$=4k8pL>sl-o~>0L7m zI+{?m>4&)#lX}NJc-MC0YF5~Y?U#eCme~(sIaYCt6+I7(#^3`5mlP^>2Sm>RFXYr;`Pi{gYrvq^ z*W)D|Se}_)ptn^@LB8k{d!UtkqWJbLoYXWXZ>epCO0=We%-`m2RKtN&KZUd^oA<}T z^9Aqgo%1*_*a|C$n4HP;$!f-ZSci4Y?fyO$Z5%|x`Z{7?j0l0w}?V;%s<^$OPjIM4ZZCTRbRpZ8R)kodNp;VDZDr{xg3M?7Z%Q6po0*?2h58W+dhYWj=SHp z*pp5LxsEOxY9BV(BO);M;Oewg*{;p$Z4Nb{Z+Ip*19iMUt5G)g=$+So{E<+ZN`W6f zeL4dHn9jlV3QKJ!J&VC*ux>yKZx7@P16Wh(T!=3o z7BJ{sK)et5Y>D?QCF@X8zV|;~E&$umEc!xT#vXI{5Hi#?EIx=wi)CCL`m8z-Dnpqc zKrX!nZb`@KAu}^P8?H}I3t{_!fBtrR+Gcz|#q3ff*qaXv7(LJ>9KJ!kL%?WP!gK3R zWEPzP2^dH3Gh*Zb>($kU+J?c*5dHo6PCpIfUKT#jXHt`Yzj4xpP-z$oY=cA*QHRcB zn0Yfe$({BdfXqaSmjgsF);yvQJurujR91vVEL4%~#n4sx!SBR(Pv%446jr)EC;bAp z?*~>T&J>MUD=t(p+s#g8r_BmgmcD^?}fb+8EC3ujv(en9nQ& z_qJr1H4JX@m6V;uC!_0w{|21mD_@CpB#fI3LLrBD$d(m=)JI7M=ZG>nDv*1nt#P`L zLA(u@_#g)WaGniK+TO#f#taGpaVkSCH877YGRKPK3iI|B3;;RJJu(F`Si zh|I|--7p7skm>V1WrTLJV_1m_dF-75)zhJV<yaU(iFK44ABFiF|L1k=0qx=Kcg&{=RLJ)4X zbW|SRMDk^Q#z8G!C}gn)W2HtJh6e)lq+Gu$u8w+ss;Z)`o1A=JRUMI_^<1hDAT7(Z zf0hv}q2U9z#34cCPA&1HjV5xdrc4oKg;{JZ(wE7f2X~Mh@sF<6YlkJxMq=S8rOnrC z)e!)VnhtmyA?p@vm58k*>_#nFE_GYB9DVO$%Fft`&a1+W*2OPc@rZt8diu;^3Y>IhTrW~}c z@HZk@SqQDQ@=l)-)e(kmsUr+N6;~mwfoW4irhpvU&|*LMGBCw>>Gv6}OQJ(5cm5Fg z=KTv?K4={g&w;a`mgkf)|Bv{L@fJSlIIR_8XX(py&U=ebb^?{WxBb)?nfe1<5?idI zzsI{$i5$o+?{F)^2hMYO%#gF7MT5?XEZNN5SveN$9QM@1W8s{f+x@{ll`W;5p+?pA zal}T2xXWD8ch9MVzNO_fn11kpdY^BM$wZa@4KloH=4|AyU$Yz$zK7kDbHaBsC5MGe z^F<#fHHf7{Qh&y7Qq}4OU}*CG6>cnT)GgrBgL?TjA_udd^ZqwJ?>PZ)-?O7Qd`gyaU26{4EaY580y7xZ>n9r*)6ITDLbg; z-?Ayv!AMvCM?AoYi&Lqz?B6k`y#4R})CWj?&@3IrfS>dJZ$3#7tP){V-jgt+l9E`X z0_t1LC->g>QxbYq?lJzTdiwt`C9Mm<-{px(N^%DMh@;Y_L(jsYB9ouaoVCCfKxj}0 zY%m~{5wYil!~>JT;p|WoAp#be2pW`CnxHx#3`EfE7HjgbvEBfySh1cRc$%=^k$pZ= zV>LVt>Zy)Qtnq_(A3QQxK`SGDB)~&;e~(R-p|N#&x|+;NhmN|CD{|(c7J)DB-h7! zo{X0_NV>OXOGkO5B)8StnsU${Qrv8|&F#-YCgUu`P-6W=U@0 zP)Q5Q+gR~^a@S#3w@Y%ohS<2-0YbjFW(W;Mf%W*--DavOw*G0rjY$PJraEHlHkLb? z>LPCPRUjdtvX-eHR3}_JGTbIlfO1`h$%#+z7g7?8EM+9mn(HL-mU5ZBu`>LT!s>eufmH!{^id~9V(RnL^F zo~b9|((8-;MDF{LIrQ*DG? zJ5#EDrgHJv&i-<Zo2jUU&9;q&9RmK+|6#|%MBBa z$lZiL^_Karcbng6UFpr$@4H*v7L?l*&E;-&ThYemiu>E#Hr#LVTg{fVztIrQxB74Q zKDPt4R(T#I|Xp_&rzM%?dNtY55840w-W2-E)sM)+jG zW!PtRSf*(HXVNfenSc(-KnJ1X?N0GHC}k8;YDwAMjN`|(Uv&Wy5L984lVUp-uW2Zc z7@A~qQpxxD8CWPpI#zb8;A|ar<{vaOa#9@PI*TbKtu6MVQG=yM;<8J)3rE@|MTxmY zwXM3Rg)yB3T}#T_DYeI5TCLqHPWflrp&c;W?N2^3h|a5xnpEBCcB;(?)ZRg#tjs

baWi;--Sc*5FNY#q%8mmXp7Q^z*6O}23r6E*B8e5 z_M4SNRK{YMWbxE}cr$m)e!CIi+j|r8fODJ%ffQ+2}1c;B8!dIDi3_kHCQd zQHNMAeN($B!Lo;qWYk06zjwnyArx8l4bb zjkgcO=k4c31Vg{`;rhcy6#Ngvk`JY$Z9SJP1R>!RT#Re}l{ zv$)zguuWzp!uo&Ad^!VYN(2Xm9NcVZ4^Uo9{l!GYK%;e=A?&REv6+TL|%c*(6D08qch>w`EQm9RjGHa{r&FzWwaWqV3=<4l))i0uz zpDMu8tU#w+*ecN18X~ygAk;r>x?)xgN2rjM=57m;CS#F`2FT}f2D7(va!@Es9|cuC z#z5Xv=1ZO|Yy1lJm#%cp|_jJPo1}I0UB27By83WK4o;NKmB zgNYP#XQZ2}p~?wM^(@H*XCf@onEEzTqe41VFf^f>O(fuA5HpqKL}ue%54!dP6FgFyUA3|kuF;AF2Px_Fz_ZRsCHXN7Ev`snAwy~0|WQyt2BNr|zqabEt6&|z6%3Nln=ld5+NQ$xlzbj=QVuv@VirfK-jtJN{-N+zB6KSucKxi zqELP8l;4T6*mCSbJ||)fS`pW+vY#ieb0~xdfC_+upfCR6e$}!2b3C~B$e1B02FjDW z1OS zSi~ZnkN#m{Fv+5TN&Jy*DX!cs8k!M`eE5bAdSq9lADW{u8b8q0AN@{Zz2ep==|>h@ zs$~qxq3cd1S0_UgT510=hv*SxmLc+bk@BfD((;Ln)6$ofhtr4OhkMr?uXcs5I;p+Q zXm5BT({*WSLv$kRLjHU7+!#VO8Je4@^L__?wp7-u3a z#e%F`j+?F=qcD8gPY($(sCB;hC53S z+*$AqzZbZ9H8a%`+uUDnWlA{;GNt?knNn_oOerrxruN_7M-bj?1IhOidr>1i=<4x} z8sQn(%v1{<16!GDqhDYZ42G4yJa}*=HA1 zyV;pNO!c!ftX6vlGBrrQp1n-{Y#hEi;sZsWod$8~6PKPQx==Oqrg8#eYIjF-*Yb|t zcEVycN=dd*D1{cwl! zkm_KsJ9$re>oKLA^_Wt=dQ2%-J*JeW9#fm>lDCJ;Fows2_e+?-3O5!Vyb6pOjP8D!@5#R26mXxGt(o zin`@AqrBeO zUYdDn;RTXa%;VbSwe!-!3(Pcd@#SR=FEaVpGS!1iX@j-J3CScPz;Zu9(Ob`_F|U_7 zH}bNXH=0~G2j3Yek+<;PR^DsH)~Xy&s6d=ukDe&reY`k))+Nta2iYZN*v@BteAW_F z?d{-=HNlOf+HU3T4P14=xOU*0fpb--r}yZc9S#b><2i;LbUpU{s_ zXss6}!lkCeaELYR7#2H&Ymyi@n^J9Q7fQ+G&y?tiE5{=cXjc!y7SzEjutPTh9-hXit}pS$Gg9=YNv ze&Tv)x#j+s8vnSy?b`U?I`>k|_n3IS_stz|7T@f@vGM-5>jz(|dB5R_rub9w*L!Ym r9K5k{@co$leE;dL&#Z}eU0<`~{TLJP=lU+z;rIIfzlt#-yzl=HWUbx@ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/operations/__pycache__/batch.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/operations/__pycache__/batch.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a742818d1582f9ffb4c92242f8bf8e47a7f011b3 GIT binary patch literal 31395 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/operations/__pycache__/schemaobj.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/operations/__pycache__/schemaobj.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce7d1d8e4059187f939db3d38f2fe1576ca10c69 GIT binary patch literal 11882 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/operations/__pycache__/toimpl.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/operations/__pycache__/toimpl.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..743c3ddfb81fdfa8e58d1f59d5888637150ad1d9 GIT binary patch literal 12002 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 L{3Q-2XYGFgXdB~Z literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/operations/base.py b/venv/lib/python3.12/site-packages/alembic/operations/base.py new file mode 100644 index 00000000..b9e6107f --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/operations/base.py @@ -0,0 +1,2001 @@ +# mypy: allow-untyped-calls + +from __future__ import annotations + +from contextlib import contextmanager +import re +import textwrap +from typing import Any +from typing import Awaitable +from typing import Callable +from typing import Dict +from typing import Iterator +from typing import List # noqa +from typing import Mapping +from typing import NoReturn +from typing import Optional +from typing import overload +from typing import Sequence # noqa +from typing import Tuple +from typing import Type # noqa +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +from sqlalchemy.sql.elements import conv + +from . import batch +from . import schemaobj +from .. import util +from ..ddl.base import _ServerDefaultType +from ..util import sqla_compat +from ..util.compat import formatannotation_fwdref +from ..util.compat import inspect_formatargspec +from ..util.compat import inspect_getfullargspec +from ..util.sqla_compat import _literal_bindparam + +if TYPE_CHECKING: + from typing import Literal + + from sqlalchemy import Table + from sqlalchemy.engine import Connection + from sqlalchemy.sql import Executable + from sqlalchemy.sql.expression import ColumnElement + from sqlalchemy.sql.expression import TableClause + from sqlalchemy.sql.expression import TextClause + from sqlalchemy.sql.schema import Column + from sqlalchemy.sql.schema import SchemaItem + from sqlalchemy.types import TypeEngine + + from .batch import BatchOperationsImpl + from .ops import AddColumnOp + from .ops import AddConstraintOp + from .ops import AlterColumnOp + from .ops import AlterTableOp + from .ops import BulkInsertOp + from .ops import CreateIndexOp + from .ops import CreateTableCommentOp + from .ops import CreateTableOp + from .ops import DropColumnOp + from .ops import DropConstraintOp + from .ops import DropIndexOp + from .ops import DropTableCommentOp + from .ops import DropTableOp + from .ops import ExecuteSQLOp + from .ops import MigrateOperation + from ..ddl import DefaultImpl + from ..runtime.migration import MigrationContext +__all__ = ("Operations", "BatchOperations") +_T = TypeVar("_T") + +_C = TypeVar("_C", bound=Callable[..., Any]) + + +class AbstractOperations(util.ModuleClsProxy): + """Base class for Operations and BatchOperations. + + .. versionadded:: 1.11.0 + + """ + + impl: Union[DefaultImpl, BatchOperationsImpl] + _to_impl = util.Dispatcher() + + def __init__( + self, + migration_context: MigrationContext, + impl: Optional[BatchOperationsImpl] = None, + ) -> None: + """Construct a new :class:`.Operations` + + :param migration_context: a :class:`.MigrationContext` + instance. + + """ + self.migration_context = migration_context + if impl is None: + self.impl = migration_context.impl + else: + self.impl = impl + + self.schema_obj = schemaobj.SchemaObjects(migration_context) + + @classmethod + def register_operation( + cls, name: str, sourcename: Optional[str] = None + ) -> Callable[[Type[_T]], Type[_T]]: + """Register a new operation for this class. + + This method is normally used to add new operations + to the :class:`.Operations` class, and possibly the + :class:`.BatchOperations` class as well. All Alembic migration + operations are implemented via this system, however the system + is also available as a public API to facilitate adding custom + operations. + + .. seealso:: + + :ref:`operation_plugins` + + + """ + + def register(op_cls: Type[_T]) -> Type[_T]: + if sourcename is None: + fn = getattr(op_cls, name) + source_name = fn.__name__ + else: + fn = getattr(op_cls, sourcename) + source_name = fn.__name__ + + spec = inspect_getfullargspec(fn) + + name_args = spec[0] + assert name_args[0:2] == ["cls", "operations"] + + name_args[0:2] = ["self"] + + args = inspect_formatargspec( + *spec, formatannotation=formatannotation_fwdref + ) + num_defaults = len(spec[3]) if spec[3] else 0 + + defaulted_vals: Tuple[Any, ...] + + if num_defaults: + defaulted_vals = tuple(name_args[0 - num_defaults :]) + else: + defaulted_vals = () + + defaulted_vals += tuple(spec[4]) + # here, we are using formatargspec in a different way in order + # to get a string that will re-apply incoming arguments to a new + # function call + + apply_kw = inspect_formatargspec( + name_args + spec[4], + spec[1], + spec[2], + defaulted_vals, + formatvalue=lambda x: "=" + x, + formatannotation=formatannotation_fwdref, + ) + + args = re.sub( + r'[_]?ForwardRef\(([\'"].+?[\'"])\)', + lambda m: m.group(1), + args, + ) + + func_text = textwrap.dedent( + """\ + def %(name)s%(args)s: + %(doc)r + return op_cls.%(source_name)s%(apply_kw)s + """ + % { + "name": name, + "source_name": source_name, + "args": args, + "apply_kw": apply_kw, + "doc": fn.__doc__, + } + ) + + globals_ = dict(globals()) + globals_.update({"op_cls": op_cls}) + lcl: Dict[str, Any] = {} + + exec(func_text, globals_, lcl) + setattr(cls, name, lcl[name]) + fn.__func__.__doc__ = ( + "This method is proxied on " + "the :class:`.%s` class, via the :meth:`.%s.%s` method." + % (cls.__name__, cls.__name__, name) + ) + if hasattr(fn, "_legacy_translations"): + lcl[name]._legacy_translations = fn._legacy_translations + return op_cls + + return register + + @classmethod + def implementation_for( + cls, op_cls: Any, replace: bool = False + ) -> Callable[[_C], _C]: + """Register an implementation for a given :class:`.MigrateOperation`. + + :param replace: when True, allows replacement of an already + registered implementation for the given operation class. This + enables customization of built-in operations such as + :class:`.CreateTableOp` by providing an alternate implementation + that can augment, modify, or conditionally invoke the default + behavior. + + .. versionadded:: 1.17.2 + + This is part of the operation extensibility API. + + .. seealso:: + + :ref:`operation_plugins` + + :ref:`operations_extending_builtin` + + """ + + def decorate(fn: _C) -> _C: + cls._to_impl.dispatch_for(op_cls, replace=replace)(fn) + return fn + + return decorate + + @classmethod + @contextmanager + def context( + cls, migration_context: MigrationContext + ) -> Iterator[Operations]: + op = Operations(migration_context) + op._install_proxy() + yield op + op._remove_proxy() + + @contextmanager + def batch_alter_table( + self, + table_name: str, + schema: Optional[str] = None, + recreate: Literal["auto", "always", "never"] = "auto", + partial_reordering: list[tuple[str, ...]] | None = None, + copy_from: Optional[Table] = None, + table_args: Tuple[Any, ...] = (), + table_kwargs: Mapping[str, Any] = util.immutabledict(), + reflect_args: Tuple[Any, ...] = (), + reflect_kwargs: Mapping[str, Any] = util.immutabledict(), + naming_convention: Optional[Dict[str, str]] = None, + ) -> Iterator[BatchOperations]: + """Invoke a series of per-table migrations in batch. + + Batch mode allows a series of operations specific to a table + to be syntactically grouped together, and allows for alternate + modes of table migration, in particular the "recreate" style of + migration required by SQLite. + + "recreate" style is as follows: + + 1. A new table is created with the new specification, based on the + migration directives within the batch, using a temporary name. + + 2. the data copied from the existing table to the new table. + + 3. the existing table is dropped. + + 4. the new table is renamed to the existing table name. + + The directive by default will only use "recreate" style on the + SQLite backend, and only if directives are present which require + this form, e.g. anything other than ``add_column()``. The batch + operation on other backends will proceed using standard ALTER TABLE + operations. + + The method is used as a context manager, which returns an instance + of :class:`.BatchOperations`; this object is the same as + :class:`.Operations` except that table names and schema names + are omitted. E.g.:: + + with op.batch_alter_table("some_table") as batch_op: + batch_op.add_column(Column("foo", Integer)) + batch_op.drop_column("bar") + + The operations within the context manager are invoked at once + when the context is ended. When run against SQLite, if the + migrations include operations not supported by SQLite's ALTER TABLE, + the entire table will be copied to a new one with the new + specification, moving all data across as well. + + The copy operation by default uses reflection to retrieve the current + structure of the table, and therefore :meth:`.batch_alter_table` + in this mode requires that the migration is run in "online" mode. + The ``copy_from`` parameter may be passed which refers to an existing + :class:`.Table` object, which will bypass this reflection step. + + .. note:: The table copy operation will currently not copy + CHECK constraints, and may not copy UNIQUE constraints that are + unnamed, as is possible on SQLite. See the section + :ref:`sqlite_batch_constraints` for workarounds. + + :param table_name: name of table + :param schema: optional schema name. + :param recreate: under what circumstances the table should be + recreated. At its default of ``"auto"``, the SQLite dialect will + recreate the table if any operations other than ``add_column()``, + ``create_index()``, or ``drop_index()`` are + present. Other options include ``"always"`` and ``"never"``. + :param copy_from: optional :class:`~sqlalchemy.schema.Table` object + that will act as the structure of the table being copied. If omitted, + table reflection is used to retrieve the structure of the table. + + .. seealso:: + + :ref:`batch_offline_mode` + + :paramref:`~.Operations.batch_alter_table.reflect_args` + + :paramref:`~.Operations.batch_alter_table.reflect_kwargs` + + :param reflect_args: a sequence of additional positional arguments that + will be applied to the table structure being reflected / copied; + this may be used to pass column and constraint overrides to the + table that will be reflected, in lieu of passing the whole + :class:`~sqlalchemy.schema.Table` using + :paramref:`~.Operations.batch_alter_table.copy_from`. + :param reflect_kwargs: a dictionary of additional keyword arguments + that will be applied to the table structure being copied; this may be + used to pass additional table and reflection options to the table that + will be reflected, in lieu of passing the whole + :class:`~sqlalchemy.schema.Table` using + :paramref:`~.Operations.batch_alter_table.copy_from`. + :param table_args: a sequence of additional positional arguments that + will be applied to the new :class:`~sqlalchemy.schema.Table` when + created, in addition to those copied from the source table. + This may be used to provide additional constraints such as CHECK + constraints that may not be reflected. + :param table_kwargs: a dictionary of additional keyword arguments + that will be applied to the new :class:`~sqlalchemy.schema.Table` + when created, in addition to those copied from the source table. + This may be used to provide for additional table options that may + not be reflected. + :param naming_convention: a naming convention dictionary of the form + described at :ref:`autogen_naming_conventions` which will be applied + to the :class:`~sqlalchemy.schema.MetaData` during the reflection + process. This is typically required if one wants to drop SQLite + constraints, as these constraints will not have names when + reflected on this backend. Requires SQLAlchemy **0.9.4** or greater. + + .. seealso:: + + :ref:`dropping_sqlite_foreign_keys` + + :param partial_reordering: a list of tuples, each suggesting a desired + ordering of two or more columns in the newly created table. Requires + that :paramref:`.batch_alter_table.recreate` is set to ``"always"``. + Examples, given a table with columns "a", "b", "c", and "d": + + Specify the order of all columns:: + + with op.batch_alter_table( + "some_table", + recreate="always", + partial_reordering=[("c", "d", "a", "b")], + ) as batch_op: + pass + + Ensure "d" appears before "c", and "b", appears before "a":: + + with op.batch_alter_table( + "some_table", + recreate="always", + partial_reordering=[("d", "c"), ("b", "a")], + ) as batch_op: + pass + + The ordering of columns not included in the partial_reordering + set is undefined. Therefore it is best to specify the complete + ordering of all columns for best results. + + .. note:: batch mode requires SQLAlchemy 0.8 or above. + + .. seealso:: + + :ref:`batch_migrations` + + """ + impl = batch.BatchOperationsImpl( + self, + table_name, + schema, + recreate, + copy_from, + table_args, + table_kwargs, + reflect_args, + reflect_kwargs, + naming_convention, + partial_reordering, + ) + batch_op = BatchOperations(self.migration_context, impl=impl) + yield batch_op + impl.flush() + + def get_context(self) -> MigrationContext: + """Return the :class:`.MigrationContext` object that's + currently in use. + + """ + + return self.migration_context + + @overload + def invoke(self, operation: CreateTableOp) -> Table: ... + + @overload + def invoke( + self, + operation: Union[ + AddConstraintOp, + DropConstraintOp, + CreateIndexOp, + DropIndexOp, + AddColumnOp, + AlterColumnOp, + AlterTableOp, + CreateTableCommentOp, + DropTableCommentOp, + DropColumnOp, + BulkInsertOp, + DropTableOp, + ExecuteSQLOp, + ], + ) -> None: ... + + @overload + def invoke(self, operation: MigrateOperation) -> Any: ... + + def invoke(self, operation: MigrateOperation) -> Any: + """Given a :class:`.MigrateOperation`, invoke it in terms of + this :class:`.Operations` instance. + + """ + fn = self._to_impl.dispatch( + operation, self.migration_context.impl.__dialect__ + ) + return fn(self, operation) + + def f(self, name: str) -> conv: + """Indicate a string name that has already had a naming convention + applied to it. + + This feature combines with the SQLAlchemy ``naming_convention`` feature + to disambiguate constraint names that have already had naming + conventions applied to them, versus those that have not. This is + necessary in the case that the ``"%(constraint_name)s"`` token + is used within a naming convention, so that it can be identified + that this particular name should remain fixed. + + If the :meth:`.Operations.f` is used on a constraint, the naming + convention will not take effect:: + + op.add_column("t", "x", Boolean(name=op.f("ck_bool_t_x"))) + + Above, the CHECK constraint generated will have the name + ``ck_bool_t_x`` regardless of whether or not a naming convention is + in use. + + Alternatively, if a naming convention is in use, and 'f' is not used, + names will be converted along conventions. If the ``target_metadata`` + contains the naming convention + ``{"ck": "ck_bool_%(table_name)s_%(constraint_name)s"}``, then the + output of the following:: + + op.add_column("t", "x", Boolean(name="x")) + + will be:: + + CONSTRAINT ck_bool_t_x CHECK (x in (1, 0))) + + The function is rendered in the output of autogenerate when + a particular constraint name is already converted. + + """ + return conv(name) + + def inline_literal( + self, value: Union[str, int], type_: Optional[TypeEngine[Any]] = None + ) -> _literal_bindparam: + r"""Produce an 'inline literal' expression, suitable for + using in an INSERT, UPDATE, or DELETE statement. + + When using Alembic in "offline" mode, CRUD operations + aren't compatible with SQLAlchemy's default behavior surrounding + literal values, + which is that they are converted into bound values and passed + separately into the ``execute()`` method of the DBAPI cursor. + An offline SQL + script needs to have these rendered inline. While it should + always be noted that inline literal values are an **enormous** + security hole in an application that handles untrusted input, + a schema migration is not run in this context, so + literals are safe to render inline, with the caveat that + advanced types like dates may not be supported directly + by SQLAlchemy. + + See :meth:`.Operations.execute` for an example usage of + :meth:`.Operations.inline_literal`. + + The environment can also be configured to attempt to render + "literal" values inline automatically, for those simple types + that are supported by the dialect; see + :paramref:`.EnvironmentContext.configure.literal_binds` for this + more recently added feature. + + :param value: The value to render. Strings, integers, and simple + numerics should be supported. Other types like boolean, + dates, etc. may or may not be supported yet by various + backends. + :param type\_: optional - a :class:`sqlalchemy.types.TypeEngine` + subclass stating the type of this value. In SQLAlchemy + expressions, this is usually derived automatically + from the Python type of the value itself, as well as + based on the context in which the value is used. + + .. seealso:: + + :paramref:`.EnvironmentContext.configure.literal_binds` + + """ + return sqla_compat._literal_bindparam(None, value, type_=type_) + + def get_bind(self) -> Connection: + """Return the current 'bind'. + + Under normal circumstances, this is the + :class:`~sqlalchemy.engine.Connection` currently being used + to emit SQL to the database. + + In a SQL script context, this value is ``None``. [TODO: verify this] + + """ + return self.migration_context.impl.bind # type: ignore[return-value] + + def run_async( + self, + async_function: Callable[..., Awaitable[_T]], + *args: Any, + **kw_args: Any, + ) -> _T: + """Invoke the given asynchronous callable, passing an asynchronous + :class:`~sqlalchemy.ext.asyncio.AsyncConnection` as the first + argument. + + This method allows calling async functions from within the + synchronous ``upgrade()`` or ``downgrade()`` alembic migration + method. + + The async connection passed to the callable shares the same + transaction as the connection running in the migration context. + + Any additional arg or kw_arg passed to this function are passed + to the provided async function. + + .. versionadded: 1.11 + + .. note:: + + This method can be called only when alembic is called using + an async dialect. + """ + if not sqla_compat.sqla_14_18: + raise NotImplementedError("SQLAlchemy 1.4.18+ required") + sync_conn = self.get_bind() + if sync_conn is None: + raise NotImplementedError("Cannot call run_async in SQL mode") + if not sync_conn.dialect.is_async: + raise ValueError("Cannot call run_async with a sync engine") + from sqlalchemy.ext.asyncio import AsyncConnection + from sqlalchemy.util import await_only + + async_conn = AsyncConnection._retrieve_proxy_for_target(sync_conn) + return await_only(async_function(async_conn, *args, **kw_args)) + + +class Operations(AbstractOperations): + """Define high level migration operations. + + Each operation corresponds to some schema migration operation, + executed against a particular :class:`.MigrationContext` + which in turn represents connectivity to a database, + or a file output stream. + + While :class:`.Operations` is normally configured as + part of the :meth:`.EnvironmentContext.run_migrations` + method called from an ``env.py`` script, a standalone + :class:`.Operations` instance can be + made for use cases external to regular Alembic + migrations by passing in a :class:`.MigrationContext`:: + + from alembic.migration import MigrationContext + from alembic.operations import Operations + + conn = myengine.connect() + ctx = MigrationContext.configure(conn) + op = Operations(ctx) + + op.alter_column("t", "c", nullable=True) + + Note that as of 0.8, most of the methods on this class are produced + dynamically using the :meth:`.Operations.register_operation` + method. + + """ + + if TYPE_CHECKING: + # START STUB FUNCTIONS: op_cls + # ### the following stubs are generated by tools/write_pyi.py ### + # ### do not edit ### + + def add_column( + self, + table_name: str, + column: Column[Any], + *, + schema: Optional[str] = None, + if_not_exists: Optional[bool] = None, + inline_references: Optional[bool] = None, + inline_primary_key: Optional[bool] = None, + ) -> None: + """Issue an "add column" instruction using the current + migration context. + + e.g.:: + + from alembic import op + from sqlalchemy import Column, String + + op.add_column("organization", Column("name", String())) + + The :meth:`.Operations.add_column` method typically corresponds + to the SQL command "ALTER TABLE... ADD COLUMN". Within the scope + of this command, the column's name, datatype, nullability, + and optional server-generated defaults may be indicated. Options + also exist for control of single-column primary key and foreign key + constraints to be generated. + + .. note:: + + Not all contraint types may be indicated with this directive. + NOT NULL, FOREIGN KEY, and CHECK are honored, PRIMARY KEY + is conditionally honored, UNIQUE + is currently not. + + As of 1.18.2, the following :class:`~sqlalchemy.schema.Column` + parameters are **ignored**: + + * :paramref:`~sqlalchemy.schema.Column.unique` - use the + :meth:`.Operations.create_unique_constraint` method + * :paramref:`~sqlalchemy.schema.Column.index` - use the + :meth:`.Operations.create_index` method + + **PRIMARY KEY support** + + The provided :class:`~sqlalchemy.schema.Column` object may include a + ``primary_key=True`` directive, indicating the column intends to be + part of a primary key constraint. However by default, the inline + "PRIMARY KEY" directive is not emitted, and it's assumed that a + separate :meth:`.Operations.create_primary_key` directive will be used + to create this constraint, which may potentially include other columns + as well as have an explicit name. To instead render an inline + "PRIMARY KEY" directive, the + :paramref:`.AddColumnOp.inline_primary_key` parameter may be indicated + at the same time as the ``primary_key`` parameter (both are needed):: + + from alembic import op + from sqlalchemy import Column, INTEGER + + op.add_column( + "organization", + Column("id", INTEGER, primary_key=True), + inline_primary_key=True + ) + + The ``primary_key=True`` parameter on + :class:`~sqlalchemy.schema.Column` also indicates behaviors such as + using the ``SERIAL`` datatype with the PostgreSQL database, which is + why two separate, independent parameters are provided to support all + combinations. + + .. versionadded:: 1.18.4 Added + :paramref:`.AddColumnOp.inline_primary_key` + to control use of the ``PRIMARY KEY`` inline directive. + + **FOREIGN KEY support** + + The provided :class:`~sqlalchemy.schema.Column` object may include a + :class:`~sqlalchemy.schema.ForeignKey` constraint directive, + referencing a remote table name. By default, Alembic will automatically + emit a second ALTER statement in order to add the single-column FOREIGN + KEY constraint separately:: + + from alembic import op + from sqlalchemy import Column, INTEGER, ForeignKey + + op.add_column( + "organization", + Column("account_id", INTEGER, ForeignKey("accounts.id")), + ) + + To render the FOREIGN KEY constraint inline within the ADD COLUMN + directive, use the ``inline_references`` parameter. This can improve + performance on large tables since the constraint is marked as valid + immediately for nullable columns:: + + from alembic import op + from sqlalchemy import Column, INTEGER, ForeignKey + + op.add_column( + "organization", + Column("account_id", INTEGER, ForeignKey("accounts.id")), + inline_references=True, + ) + + **Indicating server side defaults** + + The column argument passed to :meth:`.Operations.add_column` is a + :class:`~sqlalchemy.schema.Column` construct, used in the same way it's + used in SQLAlchemy. In particular, values or functions to be indicated + as producing the column's default value on the database side are + specified using the ``server_default`` parameter, and not ``default`` + which only specifies Python-side defaults:: + + from alembic import op + from sqlalchemy import Column, TIMESTAMP, func + + # specify "DEFAULT NOW" along with the column add + op.add_column( + "account", + Column("timestamp", TIMESTAMP, server_default=func.now()), + ) + + :param table_name: String name of the parent table. + :param column: a :class:`sqlalchemy.schema.Column` object + representing the new column. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_not_exists: If True, adds ``IF NOT EXISTS`` operator + when creating the new column for compatible dialects + + .. versionadded:: 1.16.0 + + :param inline_references: If True, renders ``FOREIGN KEY`` constraints + inline within the ``ADD COLUMN`` directive using ``REFERENCES`` + syntax, rather than as a separate ``ALTER TABLE ADD CONSTRAINT`` + statement. This is supported by PostgreSQL, Oracle, MySQL 5.7+, and + MariaDB 10.5+. + + .. versionadded:: 1.18.2 + + :param inline_primary_key: If True, renders the ``PRIMARY KEY`` phrase + inline within the ``ADD COLUMN`` directive. When not present or + False, ``PRIMARY KEY`` is not emitted; it is assumed that the + migration script will include an additional + :meth:`.Operations.create_primary_key` directive to create a full + primary key constraint. + + .. versionadded:: 1.18.4 + + """ # noqa: E501 + ... + + def alter_column( + self, + table_name: str, + column_name: str, + *, + nullable: Optional[bool] = None, + comment: Union[str, Literal[False], None] = False, + server_default: Union[ + _ServerDefaultType, None, Literal[False] + ] = False, + new_column_name: Optional[str] = None, + type_: Union[TypeEngine[Any], Type[TypeEngine[Any]], None] = None, + existing_type: Union[ + TypeEngine[Any], Type[TypeEngine[Any]], None + ] = None, + existing_server_default: Union[ + _ServerDefaultType, None, Literal[False] + ] = False, + existing_nullable: Optional[bool] = None, + existing_comment: Optional[str] = None, + schema: Optional[str] = None, + **kw: Any, + ) -> None: + r"""Issue an "alter column" instruction using the + current migration context. + + Generally, only that aspect of the column which + is being changed, i.e. name, type, nullability, + default, needs to be specified. Multiple changes + can also be specified at once and the backend should + "do the right thing", emitting each change either + separately or together as the backend allows. + + MySQL has special requirements here, since MySQL + cannot ALTER a column without a full specification. + When producing MySQL-compatible migration files, + it is recommended that the ``existing_type``, + ``existing_server_default``, and ``existing_nullable`` + parameters be present, if not being altered. + + Type changes which are against the SQLAlchemy + "schema" types :class:`~sqlalchemy.types.Boolean` + and :class:`~sqlalchemy.types.Enum` may also + add or drop constraints which accompany those + types on backends that don't support them natively. + The ``existing_type`` argument is + used in this case to identify and remove a previous + constraint that was bound to the type object. + + :param table_name: string name of the target table. + :param column_name: string name of the target column, + as it exists before the operation begins. + :param nullable: Optional; specify ``True`` or ``False`` + to alter the column's nullability. + :param server_default: Optional; specify a string + SQL expression, :func:`~sqlalchemy.sql.expression.text`, + or :class:`~sqlalchemy.schema.DefaultClause` to indicate + an alteration to the column's default value. + Set to ``None`` to have the default removed. + :param comment: optional string text of a new comment to add to the + column. + :param new_column_name: Optional; specify a string name here to + indicate the new name within a column rename operation. + :param type\_: Optional; a :class:`~sqlalchemy.types.TypeEngine` + type object to specify a change to the column's type. + For SQLAlchemy types that also indicate a constraint (i.e. + :class:`~sqlalchemy.types.Boolean`, :class:`~sqlalchemy.types.Enum`), + the constraint is also generated. + :param autoincrement: set the ``AUTO_INCREMENT`` flag of the column; + currently understood by the MySQL dialect. + :param existing_type: Optional; a + :class:`~sqlalchemy.types.TypeEngine` + type object to specify the previous type. This + is required for all MySQL column alter operations that + don't otherwise specify a new type, as well as for + when nullability is being changed on a SQL Server + column. It is also used if the type is a so-called + SQLAlchemy "schema" type which may define a constraint (i.e. + :class:`~sqlalchemy.types.Boolean`, + :class:`~sqlalchemy.types.Enum`), + so that the constraint can be dropped. + :param existing_server_default: Optional; The existing + default value of the column. Required on MySQL if + an existing default is not being changed; else MySQL + removes the default. + :param existing_nullable: Optional; the existing nullability + of the column. Required on MySQL if the existing nullability + is not being changed; else MySQL sets this to NULL. + :param existing_autoincrement: Optional; the existing autoincrement + of the column. Used for MySQL's system of altering a column + that specifies ``AUTO_INCREMENT``. + :param existing_comment: string text of the existing comment on the + column to be maintained. Required on MySQL if the existing comment + on the column is not being changed. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param postgresql_using: String argument which will indicate a + SQL expression to render within the Postgresql-specific USING clause + within ALTER COLUMN. This string is taken directly as raw SQL which + must explicitly include any necessary quoting or escaping of tokens + within the expression. + + """ # noqa: E501 + ... + + def bulk_insert( + self, + table: Union[Table, TableClause], + rows: List[Dict[str, Any]], + *, + multiinsert: bool = True, + ) -> None: + """Issue a "bulk insert" operation using the current + migration context. + + This provides a means of representing an INSERT of multiple rows + which works equally well in the context of executing on a live + connection as well as that of generating a SQL script. In the + case of a SQL script, the values are rendered inline into the + statement. + + e.g.:: + + from alembic import op + from datetime import date + from sqlalchemy.sql import table, column + from sqlalchemy import String, Integer, Date + + # Create an ad-hoc table to use for the insert statement. + accounts_table = table( + "account", + column("id", Integer), + column("name", String), + column("create_date", Date), + ) + + op.bulk_insert( + accounts_table, + [ + { + "id": 1, + "name": "John Smith", + "create_date": date(2010, 10, 5), + }, + { + "id": 2, + "name": "Ed Williams", + "create_date": date(2007, 5, 27), + }, + { + "id": 3, + "name": "Wendy Jones", + "create_date": date(2008, 8, 15), + }, + ], + ) + + When using --sql mode, some datatypes may not render inline + automatically, such as dates and other special types. When this + issue is present, :meth:`.Operations.inline_literal` may be used:: + + op.bulk_insert( + accounts_table, + [ + { + "id": 1, + "name": "John Smith", + "create_date": op.inline_literal("2010-10-05"), + }, + { + "id": 2, + "name": "Ed Williams", + "create_date": op.inline_literal("2007-05-27"), + }, + { + "id": 3, + "name": "Wendy Jones", + "create_date": op.inline_literal("2008-08-15"), + }, + ], + multiinsert=False, + ) + + When using :meth:`.Operations.inline_literal` in conjunction with + :meth:`.Operations.bulk_insert`, in order for the statement to work + in "online" (e.g. non --sql) mode, the + :paramref:`~.Operations.bulk_insert.multiinsert` + flag should be set to ``False``, which will have the effect of + individual INSERT statements being emitted to the database, each + with a distinct VALUES clause, so that the "inline" values can + still be rendered, rather than attempting to pass the values + as bound parameters. + + :param table: a table object which represents the target of the INSERT. + + :param rows: a list of dictionaries indicating rows. + + :param multiinsert: when at its default of True and --sql mode is not + enabled, the INSERT statement will be executed using + "executemany()" style, where all elements in the list of + dictionaries are passed as bound parameters in a single + list. Setting this to False results in individual INSERT + statements being emitted per parameter set, and is needed + in those cases where non-literal values are present in the + parameter sets. + + """ # noqa: E501 + ... + + def create_check_constraint( + self, + constraint_name: Optional[str], + table_name: str, + condition: Union[str, ColumnElement[bool], TextClause], + *, + schema: Optional[str] = None, + **kw: Any, + ) -> None: + """Issue a "create check constraint" instruction using the + current migration context. + + e.g.:: + + from alembic import op + from sqlalchemy.sql import column, func + + op.create_check_constraint( + "ck_user_name_len", + "user", + func.len(column("name")) > 5, + ) + + CHECK constraints are usually against a SQL expression, so ad-hoc + table metadata is usually needed. The function will convert the given + arguments into a :class:`sqlalchemy.schema.CheckConstraint` bound + to an anonymous table in order to emit the CREATE statement. + + :param name: Name of the check constraint. The name is necessary + so that an ALTER statement can be emitted. For setups that + use an automated naming scheme such as that described at + :ref:`sqla:constraint_naming_conventions`, + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param table_name: String name of the source table. + :param condition: SQL expression that's the condition of the + constraint. Can be a string or SQLAlchemy expression language + structure. + :param deferrable: optional bool. If set, emit DEFERRABLE or + NOT DEFERRABLE when issuing DDL for this constraint. + :param initially: optional string. If set, emit INITIALLY + when issuing DDL for this constraint. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + """ # noqa: E501 + ... + + def create_exclude_constraint( + self, + constraint_name: str, + table_name: str, + *elements: Any, + **kw: Any, + ) -> Optional[Table]: + """Issue an alter to create an EXCLUDE constraint using the + current migration context. + + .. note:: This method is Postgresql specific, and additionally + requires at least SQLAlchemy 1.0. + + e.g.:: + + from alembic import op + + op.create_exclude_constraint( + "user_excl", + "user", + ("period", "&&"), + ("group", "="), + where=("group != 'some group'"), + ) + + Note that the expressions work the same way as that of + the ``ExcludeConstraint`` object itself; if plain strings are + passed, quoting rules must be applied manually. + + :param name: Name of the constraint. + :param table_name: String name of the source table. + :param elements: exclude conditions. + :param where: SQL expression or SQL string with optional WHERE + clause. + :param deferrable: optional bool. If set, emit DEFERRABLE or + NOT DEFERRABLE when issuing DDL for this constraint. + :param initially: optional string. If set, emit INITIALLY + when issuing DDL for this constraint. + :param schema: Optional schema name to operate within. + + """ # noqa: E501 + ... + + def create_foreign_key( + self, + constraint_name: Optional[str], + source_table: str, + referent_table: str, + local_cols: List[str], + remote_cols: List[str], + *, + onupdate: Optional[str] = None, + ondelete: Optional[str] = None, + deferrable: Optional[bool] = None, + initially: Optional[str] = None, + match: Optional[str] = None, + source_schema: Optional[str] = None, + referent_schema: Optional[str] = None, + **dialect_kw: Any, + ) -> None: + """Issue a "create foreign key" instruction using the + current migration context. + + e.g.:: + + from alembic import op + + op.create_foreign_key( + "fk_user_address", + "address", + "user", + ["user_id"], + ["id"], + ) + + This internally generates a :class:`~sqlalchemy.schema.Table` object + containing the necessary columns, then generates a new + :class:`~sqlalchemy.schema.ForeignKeyConstraint` + object which it then associates with the + :class:`~sqlalchemy.schema.Table`. + Any event listeners associated with this action will be fired + off normally. The :class:`~sqlalchemy.schema.AddConstraint` + construct is ultimately used to generate the ALTER statement. + + :param constraint_name: Name of the foreign key constraint. The name + is necessary so that an ALTER statement can be emitted. For setups + that use an automated naming scheme such as that described at + :ref:`sqla:constraint_naming_conventions`, + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param source_table: String name of the source table. + :param referent_table: String name of the destination table. + :param local_cols: a list of string column names in the + source table. + :param remote_cols: a list of string column names in the + remote table. + :param onupdate: Optional string. If set, emit ON UPDATE when + issuing DDL for this constraint. Typical values include CASCADE, + DELETE and RESTRICT. + :param ondelete: Optional string. If set, emit ON DELETE when + issuing DDL for this constraint. Typical values include CASCADE, + DELETE and RESTRICT. + :param deferrable: optional bool. If set, emit DEFERRABLE or NOT + DEFERRABLE when issuing DDL for this constraint. + :param source_schema: Optional schema name of the source table. + :param referent_schema: Optional schema name of the destination table. + + """ # noqa: E501 + ... + + def create_index( + self, + index_name: Optional[str], + table_name: str, + columns: Sequence[Union[str, TextClause, ColumnElement[Any]]], + *, + schema: Optional[str] = None, + unique: bool = False, + if_not_exists: Optional[bool] = None, + **kw: Any, + ) -> None: + r"""Issue a "create index" instruction using the current + migration context. + + e.g.:: + + from alembic import op + + op.create_index("ik_test", "t1", ["foo", "bar"]) + + Functional indexes can be produced by using the + :func:`sqlalchemy.sql.expression.text` construct:: + + from alembic import op + from sqlalchemy import text + + op.create_index("ik_test", "t1", [text("lower(foo)")]) + + :param index_name: name of the index. + :param table_name: name of the owning table. + :param columns: a list consisting of string column names and/or + :func:`~sqlalchemy.sql.expression.text` constructs. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param unique: If True, create a unique index. + + :param quote: Force quoting of this column's name on or off, + corresponding to ``True`` or ``False``. When left at its default + of ``None``, the column identifier will be quoted according to + whether the name is case sensitive (identifiers with at least one + upper case character are treated as case sensitive), or if it's a + reserved word. This flag is only needed to force quoting of a + reserved word which is not known by the SQLAlchemy dialect. + + :param if_not_exists: If True, adds IF NOT EXISTS operator when + creating the new index. + + .. versionadded:: 1.12.0 + + :param \**kw: Additional keyword arguments not mentioned above are + dialect specific, and passed in the form + ``_``. + See the documentation regarding an individual dialect at + :ref:`dialect_toplevel` for detail on documented arguments. + + """ # noqa: E501 + ... + + def create_primary_key( + self, + constraint_name: Optional[str], + table_name: str, + columns: List[str], + *, + schema: Optional[str] = None, + ) -> None: + """Issue a "create primary key" instruction using the current + migration context. + + e.g.:: + + from alembic import op + + op.create_primary_key("pk_my_table", "my_table", ["id", "version"]) + + This internally generates a :class:`~sqlalchemy.schema.Table` object + containing the necessary columns, then generates a new + :class:`~sqlalchemy.schema.PrimaryKeyConstraint` + object which it then associates with the + :class:`~sqlalchemy.schema.Table`. + Any event listeners associated with this action will be fired + off normally. The :class:`~sqlalchemy.schema.AddConstraint` + construct is ultimately used to generate the ALTER statement. + + :param constraint_name: Name of the primary key constraint. The name + is necessary so that an ALTER statement can be emitted. For setups + that use an automated naming scheme such as that described at + :ref:`sqla:constraint_naming_conventions` + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param table_name: String name of the target table. + :param columns: a list of string column names to be applied to the + primary key constraint. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + """ # noqa: E501 + ... + + def create_table( + self, + table_name: str, + *columns: SchemaItem, + if_not_exists: Optional[bool] = None, + **kw: Any, + ) -> Table: + r"""Issue a "create table" instruction using the current migration + context. + + This directive receives an argument list similar to that of the + traditional :class:`sqlalchemy.schema.Table` construct, but without the + metadata:: + + from sqlalchemy import INTEGER, VARCHAR, NVARCHAR, Column + from alembic import op + + op.create_table( + "account", + Column("id", INTEGER, primary_key=True), + Column("name", VARCHAR(50), nullable=False), + Column("description", NVARCHAR(200)), + Column("timestamp", TIMESTAMP, server_default=func.now()), + ) + + Note that :meth:`.create_table` accepts + :class:`~sqlalchemy.schema.Column` + constructs directly from the SQLAlchemy library. In particular, + default values to be created on the database side are + specified using the ``server_default`` parameter, and not + ``default`` which only specifies Python-side defaults:: + + from alembic import op + from sqlalchemy import Column, TIMESTAMP, func + + # specify "DEFAULT NOW" along with the "timestamp" column + op.create_table( + "account", + Column("id", INTEGER, primary_key=True), + Column("timestamp", TIMESTAMP, server_default=func.now()), + ) + + The function also returns a newly created + :class:`~sqlalchemy.schema.Table` object, corresponding to the table + specification given, which is suitable for + immediate SQL operations, in particular + :meth:`.Operations.bulk_insert`:: + + from sqlalchemy import INTEGER, VARCHAR, NVARCHAR, Column + from alembic import op + + account_table = op.create_table( + "account", + Column("id", INTEGER, primary_key=True), + Column("name", VARCHAR(50), nullable=False), + Column("description", NVARCHAR(200)), + Column("timestamp", TIMESTAMP, server_default=func.now()), + ) + + op.bulk_insert( + account_table, + [ + {"name": "A1", "description": "account 1"}, + {"name": "A2", "description": "account 2"}, + ], + ) + + :param table_name: Name of the table + :param \*columns: collection of :class:`~sqlalchemy.schema.Column` + objects within + the table, as well as optional :class:`~sqlalchemy.schema.Constraint` + objects + and :class:`~.sqlalchemy.schema.Index` objects. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_not_exists: If True, adds IF NOT EXISTS operator when + creating the new table. + + .. versionadded:: 1.13.3 + :param \**kw: Other keyword arguments are passed to the underlying + :class:`sqlalchemy.schema.Table` object created for the command. + + :return: the :class:`~sqlalchemy.schema.Table` object corresponding + to the parameters given. + + """ # noqa: E501 + ... + + def create_table_comment( + self, + table_name: str, + comment: Optional[str], + *, + existing_comment: Optional[str] = None, + schema: Optional[str] = None, + ) -> None: + """Emit a COMMENT ON operation to set the comment for a table. + + :param table_name: string name of the target table. + :param comment: string value of the comment being registered against + the specified table. + :param existing_comment: String value of a comment + already registered on the specified table, used within autogenerate + so that the operation is reversible, but not required for direct + use. + + .. seealso:: + + :meth:`.Operations.drop_table_comment` + + :paramref:`.Operations.alter_column.comment` + + """ # noqa: E501 + ... + + def create_unique_constraint( + self, + constraint_name: Optional[str], + table_name: str, + columns: Sequence[str], + *, + schema: Optional[str] = None, + **kw: Any, + ) -> Any: + """Issue a "create unique constraint" instruction using the + current migration context. + + e.g.:: + + from alembic import op + op.create_unique_constraint("uq_user_name", "user", ["name"]) + + This internally generates a :class:`~sqlalchemy.schema.Table` object + containing the necessary columns, then generates a new + :class:`~sqlalchemy.schema.UniqueConstraint` + object which it then associates with the + :class:`~sqlalchemy.schema.Table`. + Any event listeners associated with this action will be fired + off normally. The :class:`~sqlalchemy.schema.AddConstraint` + construct is ultimately used to generate the ALTER statement. + + :param name: Name of the unique constraint. The name is necessary + so that an ALTER statement can be emitted. For setups that + use an automated naming scheme such as that described at + :ref:`sqla:constraint_naming_conventions`, + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param table_name: String name of the source table. + :param columns: a list of string column names in the + source table. + :param deferrable: optional bool. If set, emit DEFERRABLE or + NOT DEFERRABLE when issuing DDL for this constraint. + :param initially: optional string. If set, emit INITIALLY + when issuing DDL for this constraint. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + """ # noqa: E501 + ... + + def drop_column( + self, + table_name: str, + column_name: str, + *, + schema: Optional[str] = None, + **kw: Any, + ) -> None: + """Issue a "drop column" instruction using the current + migration context. + + e.g.:: + + drop_column("organization", "account_id") + + :param table_name: name of table + :param column_name: name of column + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_exists: If True, adds IF EXISTS operator when + dropping the new column for compatible dialects + + .. versionadded:: 1.16.0 + + :param mssql_drop_check: Optional boolean. When ``True``, on + Microsoft SQL Server only, first + drop the CHECK constraint on the column using a + SQL-script-compatible + block that selects into a @variable from sys.check_constraints, + then exec's a separate DROP CONSTRAINT for that constraint. + :param mssql_drop_default: Optional boolean. When ``True``, on + Microsoft SQL Server only, first + drop the DEFAULT constraint on the column using a + SQL-script-compatible + block that selects into a @variable from sys.default_constraints, + then exec's a separate DROP CONSTRAINT for that default. + :param mssql_drop_foreign_key: Optional boolean. When ``True``, on + Microsoft SQL Server only, first + drop a single FOREIGN KEY constraint on the column using a + SQL-script-compatible + block that selects into a @variable from + sys.foreign_keys/sys.foreign_key_columns, + then exec's a separate DROP CONSTRAINT for that default. Only + works if the column has exactly one FK constraint which refers to + it, at the moment. + """ # noqa: E501 + ... + + def drop_constraint( + self, + constraint_name: str, + table_name: str, + type_: Optional[str] = None, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + ) -> None: + r"""Drop a constraint of the given name, typically via DROP CONSTRAINT. + + :param constraint_name: name of the constraint. + :param table_name: table name. + :param type\_: optional, required on MySQL. can be + 'foreignkey', 'primary', 'unique', or 'check'. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_exists: If True, adds IF EXISTS operator when + dropping the constraint + + .. versionadded:: 1.16.0 + + """ # noqa: E501 + ... + + def drop_index( + self, + index_name: str, + table_name: Optional[str] = None, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + **kw: Any, + ) -> None: + r"""Issue a "drop index" instruction using the current + migration context. + + e.g.:: + + drop_index("accounts") + + :param index_name: name of the index. + :param table_name: name of the owning table. Some + backends such as Microsoft SQL Server require this. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + :param if_exists: If True, adds IF EXISTS operator when + dropping the index. + + .. versionadded:: 1.12.0 + + :param \**kw: Additional keyword arguments not mentioned above are + dialect specific, and passed in the form + ``_``. + See the documentation regarding an individual dialect at + :ref:`dialect_toplevel` for detail on documented arguments. + + """ # noqa: E501 + ... + + def drop_table( + self, + table_name: str, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + **kw: Any, + ) -> None: + r"""Issue a "drop table" instruction using the current + migration context. + + + e.g.:: + + drop_table("accounts") + + :param table_name: Name of the table + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_exists: If True, adds IF EXISTS operator when + dropping the table. + + .. versionadded:: 1.13.3 + :param \**kw: Other keyword arguments are passed to the underlying + :class:`sqlalchemy.schema.Table` object created for the command. + + """ # noqa: E501 + ... + + def drop_table_comment( + self, + table_name: str, + *, + existing_comment: Optional[str] = None, + schema: Optional[str] = None, + ) -> None: + """Issue a "drop table comment" operation to + remove an existing comment set on a table. + + :param table_name: string name of the target table. + :param existing_comment: An optional string value of a comment already + registered on the specified table. + + .. seealso:: + + :meth:`.Operations.create_table_comment` + + :paramref:`.Operations.alter_column.comment` + + """ # noqa: E501 + ... + + def execute( + self, + sqltext: Union[Executable, str], + *, + execution_options: Optional[dict[str, Any]] = None, + ) -> None: + r"""Execute the given SQL using the current migration context. + + The given SQL can be a plain string, e.g.:: + + op.execute("INSERT INTO table (foo) VALUES ('some value')") + + Or it can be any kind of Core SQL Expression construct, such as + below where we use an update construct:: + + from sqlalchemy.sql import table, column + from sqlalchemy import String + from alembic import op + + account = table("account", column("name", String)) + op.execute( + account.update() + .where(account.c.name == op.inline_literal("account 1")) + .values({"name": op.inline_literal("account 2")}) + ) + + Above, we made use of the SQLAlchemy + :func:`sqlalchemy.sql.expression.table` and + :func:`sqlalchemy.sql.expression.column` constructs to make a brief, + ad-hoc table construct just for our UPDATE statement. A full + :class:`~sqlalchemy.schema.Table` construct of course works perfectly + fine as well, though note it's a recommended practice to at least + ensure the definition of a table is self-contained within the migration + script, rather than imported from a module that may break compatibility + with older migrations. + + In a SQL script context, the statement is emitted directly to the + output stream. There is *no* return result, however, as this + function is oriented towards generating a change script + that can run in "offline" mode. Additionally, parameterized + statements are discouraged here, as they *will not work* in offline + mode. Above, we use :meth:`.inline_literal` where parameters are + to be used. + + For full interaction with a connected database where parameters can + also be used normally, use the "bind" available from the context:: + + from alembic import op + + connection = op.get_bind() + + connection.execute( + account.update() + .where(account.c.name == "account 1") + .values({"name": "account 2"}) + ) + + Additionally, when passing the statement as a plain string, it is first + coerced into a :func:`sqlalchemy.sql.expression.text` construct + before being passed along. In the less likely case that the + literal SQL string contains a colon, it must be escaped with a + backslash, as:: + + op.execute(r"INSERT INTO table (foo) VALUES ('\:colon_value')") + + + :param sqltext: Any legal SQLAlchemy expression, including: + + * a string + * a :func:`sqlalchemy.sql.expression.text` construct. + * a :func:`sqlalchemy.sql.expression.insert` construct. + * a :func:`sqlalchemy.sql.expression.update` construct. + * a :func:`sqlalchemy.sql.expression.delete` construct. + * Any "executable" described in SQLAlchemy Core documentation, + noting that no result set is returned. + + .. note:: when passing a plain string, the statement is coerced into + a :func:`sqlalchemy.sql.expression.text` construct. This construct + considers symbols with colons, e.g. ``:foo`` to be bound parameters. + To avoid this, ensure that colon symbols are escaped, e.g. + ``\:foo``. + + :param execution_options: Optional dictionary of + execution options, will be passed to + :meth:`sqlalchemy.engine.Connection.execution_options`. + """ # noqa: E501 + ... + + def rename_table( + self, + old_table_name: str, + new_table_name: str, + *, + schema: Optional[str] = None, + ) -> None: + """Emit an ALTER TABLE to rename a table. + + :param old_table_name: old name. + :param new_table_name: new name. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + """ # noqa: E501 + ... + + # END STUB FUNCTIONS: op_cls + + +class BatchOperations(AbstractOperations): + """Modifies the interface :class:`.Operations` for batch mode. + + This basically omits the ``table_name`` and ``schema`` parameters + from associated methods, as these are a given when running under batch + mode. + + .. seealso:: + + :meth:`.Operations.batch_alter_table` + + Note that as of 0.8, most of the methods on this class are produced + dynamically using the :meth:`.Operations.register_operation` + method. + + """ + + impl: BatchOperationsImpl + + def _noop(self, operation: Any) -> NoReturn: + raise NotImplementedError( + "The %s method does not apply to a batch table alter operation." + % operation + ) + + if TYPE_CHECKING: + # START STUB FUNCTIONS: batch_op + # ### the following stubs are generated by tools/write_pyi.py ### + # ### do not edit ### + + def add_column( + self, + column: Column[Any], + *, + insert_before: Optional[str] = None, + insert_after: Optional[str] = None, + if_not_exists: Optional[bool] = None, + inline_references: Optional[bool] = None, + inline_primary_key: Optional[bool] = None, + ) -> None: + """Issue an "add column" instruction using the current + batch migration context. + + .. seealso:: + + :meth:`.Operations.add_column` + + """ # noqa: E501 + ... + + def alter_column( + self, + column_name: str, + *, + nullable: Optional[bool] = None, + comment: Union[str, Literal[False], None] = False, + server_default: Union[ + _ServerDefaultType, None, Literal[False] + ] = False, + new_column_name: Optional[str] = None, + type_: Union[TypeEngine[Any], Type[TypeEngine[Any]], None] = None, + existing_type: Union[ + TypeEngine[Any], Type[TypeEngine[Any]], None + ] = None, + existing_server_default: Union[ + _ServerDefaultType, None, Literal[False] + ] = False, + existing_nullable: Optional[bool] = None, + existing_comment: Optional[str] = None, + insert_before: Optional[str] = None, + insert_after: Optional[str] = None, + **kw: Any, + ) -> None: + """Issue an "alter column" instruction using the current + batch migration context. + + Parameters are the same as that of :meth:`.Operations.alter_column`, + as well as the following option(s): + + :param insert_before: String name of an existing column which this + column should be placed before, when creating the new table. + + :param insert_after: String name of an existing column which this + column should be placed after, when creating the new table. If + both :paramref:`.BatchOperations.alter_column.insert_before` + and :paramref:`.BatchOperations.alter_column.insert_after` are + omitted, the column is inserted after the last existing column + in the table. + + .. seealso:: + + :meth:`.Operations.alter_column` + + + """ # noqa: E501 + ... + + def create_check_constraint( + self, + constraint_name: str, + condition: Union[str, ColumnElement[bool], TextClause], + **kw: Any, + ) -> None: + """Issue a "create check constraint" instruction using the + current batch migration context. + + The batch form of this call omits the ``source`` and ``schema`` + arguments from the call. + + .. seealso:: + + :meth:`.Operations.create_check_constraint` + + """ # noqa: E501 + ... + + def create_exclude_constraint( + self, constraint_name: str, *elements: Any, **kw: Any + ) -> Optional[Table]: + """Issue a "create exclude constraint" instruction using the + current batch migration context. + + .. note:: This method is Postgresql specific, and additionally + requires at least SQLAlchemy 1.0. + + .. seealso:: + + :meth:`.Operations.create_exclude_constraint` + + """ # noqa: E501 + ... + + def create_foreign_key( + self, + constraint_name: Optional[str], + referent_table: str, + local_cols: List[str], + remote_cols: List[str], + *, + referent_schema: Optional[str] = None, + onupdate: Optional[str] = None, + ondelete: Optional[str] = None, + deferrable: Optional[bool] = None, + initially: Optional[str] = None, + match: Optional[str] = None, + **dialect_kw: Any, + ) -> None: + """Issue a "create foreign key" instruction using the + current batch migration context. + + The batch form of this call omits the ``source`` and ``source_schema`` + arguments from the call. + + e.g.:: + + with batch_alter_table("address") as batch_op: + batch_op.create_foreign_key( + "fk_user_address", + "user", + ["user_id"], + ["id"], + ) + + .. seealso:: + + :meth:`.Operations.create_foreign_key` + + """ # noqa: E501 + ... + + def create_index( + self, index_name: str, columns: List[str], **kw: Any + ) -> None: + """Issue a "create index" instruction using the + current batch migration context. + + .. seealso:: + + :meth:`.Operations.create_index` + + """ # noqa: E501 + ... + + def create_primary_key( + self, constraint_name: Optional[str], columns: List[str] + ) -> None: + """Issue a "create primary key" instruction using the + current batch migration context. + + The batch form of this call omits the ``table_name`` and ``schema`` + arguments from the call. + + .. seealso:: + + :meth:`.Operations.create_primary_key` + + """ # noqa: E501 + ... + + def create_table_comment( + self, + comment: Optional[str], + *, + existing_comment: Optional[str] = None, + ) -> None: + """Emit a COMMENT ON operation to set the comment for a table + using the current batch migration context. + + :param comment: string value of the comment being registered against + the specified table. + :param existing_comment: String value of a comment + already registered on the specified table, used within autogenerate + so that the operation is reversible, but not required for direct + use. + + """ # noqa: E501 + ... + + def create_unique_constraint( + self, constraint_name: str, columns: Sequence[str], **kw: Any + ) -> Any: + """Issue a "create unique constraint" instruction using the + current batch migration context. + + The batch form of this call omits the ``source`` and ``schema`` + arguments from the call. + + .. seealso:: + + :meth:`.Operations.create_unique_constraint` + + """ # noqa: E501 + ... + + def drop_column(self, column_name: str, **kw: Any) -> None: + """Issue a "drop column" instruction using the current + batch migration context. + + .. seealso:: + + :meth:`.Operations.drop_column` + + """ # noqa: E501 + ... + + def drop_constraint( + self, constraint_name: str, type_: Optional[str] = None + ) -> None: + """Issue a "drop constraint" instruction using the + current batch migration context. + + The batch form of this call omits the ``table_name`` and ``schema`` + arguments from the call. + + .. seealso:: + + :meth:`.Operations.drop_constraint` + + """ # noqa: E501 + ... + + def drop_index(self, index_name: str, **kw: Any) -> None: + """Issue a "drop index" instruction using the + current batch migration context. + + .. seealso:: + + :meth:`.Operations.drop_index` + + """ # noqa: E501 + ... + + def drop_table_comment( + self, *, existing_comment: Optional[str] = None + ) -> None: + """Issue a "drop table comment" operation to + remove an existing comment set on a table using the current + batch operations context. + + :param existing_comment: An optional string value of a comment already + registered on the specified table. + + """ # noqa: E501 + ... + + def execute( + self, + sqltext: Union[Executable, str], + *, + execution_options: Optional[dict[str, Any]] = None, + ) -> None: + """Execute the given SQL using the current migration context. + + .. seealso:: + + :meth:`.Operations.execute` + + """ # noqa: E501 + ... + + # END STUB FUNCTIONS: batch_op diff --git a/venv/lib/python3.12/site-packages/alembic/operations/batch.py b/venv/lib/python3.12/site-packages/alembic/operations/batch.py new file mode 100644 index 00000000..9b48be59 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/operations/batch.py @@ -0,0 +1,720 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +from typing import Any +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import CheckConstraint +from sqlalchemy import Column +from sqlalchemy import ForeignKeyConstraint +from sqlalchemy import Index +from sqlalchemy import MetaData +from sqlalchemy import PrimaryKeyConstraint +from sqlalchemy import schema as sql_schema +from sqlalchemy import select +from sqlalchemy import Table +from sqlalchemy import types as sqltypes +from sqlalchemy.sql.schema import SchemaEventTarget +from sqlalchemy.util import OrderedDict +from sqlalchemy.util import topological + +from ..util import exc +from ..util.sqla_compat import _columns_for_constraint +from ..util.sqla_compat import _copy +from ..util.sqla_compat import _copy_expression +from ..util.sqla_compat import _ensure_scope_for_ddl +from ..util.sqla_compat import _fk_is_self_referential +from ..util.sqla_compat import _idx_table_bound_expressions +from ..util.sqla_compat import _is_type_bound +from ..util.sqla_compat import _remove_column_from_collection +from ..util.sqla_compat import _resolve_for_variant +from ..util.sqla_compat import constraint_name_defined +from ..util.sqla_compat import constraint_name_string + +if TYPE_CHECKING: + from typing import Literal + + from sqlalchemy.engine import Dialect + from sqlalchemy.sql.elements import ColumnClause + from sqlalchemy.sql.elements import quoted_name + from sqlalchemy.sql.schema import Constraint + from sqlalchemy.sql.type_api import TypeEngine + + from ..ddl.base import _ServerDefaultType + from ..ddl.impl import DefaultImpl + + +class BatchOperationsImpl: + def __init__( + self, + operations, + table_name, + schema, + recreate, + copy_from, + table_args, + table_kwargs, + reflect_args, + reflect_kwargs, + naming_convention, + partial_reordering, + ): + self.operations = operations + self.table_name = table_name + self.schema = schema + if recreate not in ("auto", "always", "never"): + raise ValueError( + "recreate may be one of 'auto', 'always', or 'never'." + ) + self.recreate = recreate + self.copy_from = copy_from + self.table_args = table_args + self.table_kwargs = dict(table_kwargs) + self.reflect_args = reflect_args + self.reflect_kwargs = dict(reflect_kwargs) + self.reflect_kwargs.setdefault( + "listeners", list(self.reflect_kwargs.get("listeners", ())) + ) + self.reflect_kwargs["listeners"].append( + ("column_reflect", operations.impl.autogen_column_reflect) + ) + self.naming_convention = naming_convention + self.partial_reordering = partial_reordering + self.batch = [] + + @property + def dialect(self) -> Dialect: + return self.operations.impl.dialect + + @property + def impl(self) -> DefaultImpl: + return self.operations.impl + + def _should_recreate(self) -> bool: + if self.recreate == "auto": + return self.operations.impl.requires_recreate_in_batch(self) + elif self.recreate == "always": + return True + else: + return False + + def flush(self) -> None: + should_recreate = self._should_recreate() + + with _ensure_scope_for_ddl(self.impl.connection): + if not should_recreate: + for opname, arg, kw in self.batch: + fn = getattr(self.operations.impl, opname) + fn(*arg, **kw) + else: + if self.naming_convention: + m1 = MetaData(naming_convention=self.naming_convention) + else: + m1 = MetaData() + + if self.copy_from is not None: + existing_table = self.copy_from + reflected = False + else: + if self.operations.migration_context.as_sql: + raise exc.CommandError( + f"This operation cannot proceed in --sql mode; " + f"batch mode with dialect " + f"{self.operations.migration_context.dialect.name} " # noqa: E501 + f"requires a live database connection with which " + f'to reflect the table "{self.table_name}". ' + f"To generate a batch SQL migration script using " + "table " + '"move and copy", a complete Table object ' + f'should be passed to the "copy_from" argument ' + "of the batch_alter_table() method so that table " + "reflection can be skipped." + ) + + existing_table = Table( + self.table_name, + m1, + schema=self.schema, + autoload_with=self.operations.get_bind(), + *self.reflect_args, + **self.reflect_kwargs, + ) + reflected = True + + batch_impl = ApplyBatchImpl( + self.impl, + existing_table, + self.table_args, + self.table_kwargs, + reflected, + partial_reordering=self.partial_reordering, + ) + for opname, arg, kw in self.batch: + fn = getattr(batch_impl, opname) + fn(*arg, **kw) + + batch_impl._create(self.impl) + + def alter_column(self, *arg, **kw) -> None: + self.batch.append(("alter_column", arg, kw)) + + def add_column(self, *arg, **kw) -> None: + if ( + "insert_before" in kw or "insert_after" in kw + ) and not self._should_recreate(): + raise exc.CommandError( + "Can't specify insert_before or insert_after when using " + "ALTER; please specify recreate='always'" + ) + self.batch.append(("add_column", arg, kw)) + + def drop_column(self, *arg, **kw) -> None: + self.batch.append(("drop_column", arg, kw)) + + def add_constraint(self, const: Constraint) -> None: + self.batch.append(("add_constraint", (const,), {})) + + def drop_constraint(self, const: Constraint) -> None: + self.batch.append(("drop_constraint", (const,), {})) + + def rename_table(self, *arg, **kw): + self.batch.append(("rename_table", arg, kw)) + + def create_index(self, idx: Index, **kw: Any) -> None: + self.batch.append(("create_index", (idx,), kw)) + + def drop_index(self, idx: Index, **kw: Any) -> None: + self.batch.append(("drop_index", (idx,), kw)) + + def create_table_comment(self, table): + self.batch.append(("create_table_comment", (table,), {})) + + def drop_table_comment(self, table): + self.batch.append(("drop_table_comment", (table,), {})) + + def create_table(self, table): + raise NotImplementedError("Can't create table in batch mode") + + def drop_table(self, table): + raise NotImplementedError("Can't drop table in batch mode") + + def create_column_comment(self, column): + self.batch.append(("create_column_comment", (column,), {})) + + +class ApplyBatchImpl: + def __init__( + self, + impl: DefaultImpl, + table: Table, + table_args: tuple, + table_kwargs: Dict[str, Any], + reflected: bool, + partial_reordering: tuple = (), + ) -> None: + self.impl = impl + self.table = table # this is a Table object + self.table_args = table_args + self.table_kwargs = table_kwargs + self.temp_table_name = self._calc_temp_name(table.name) + self.new_table: Optional[Table] = None + + self.partial_reordering = partial_reordering # tuple of tuples + self.add_col_ordering: Tuple[ + Tuple[str, str], ... + ] = () # tuple of tuples + + self.column_transfers = OrderedDict( + (c.name, {"expr": c}) for c in self.table.c + ) + self.existing_ordering = list(self.column_transfers) + + self.reflected = reflected + self._grab_table_elements() + + @classmethod + def _calc_temp_name(cls, tablename: Union[quoted_name, str]) -> str: + return ("_alembic_tmp_%s" % tablename)[0:50] + + def _grab_table_elements(self) -> None: + schema = self.table.schema + self.columns: Dict[str, Column[Any]] = OrderedDict() + for c in self.table.c: + c_copy = _copy(c, schema=schema) + c_copy.unique = c_copy.index = False + # ensure that the type object was copied, + # as we may need to modify it in-place + if isinstance(c.type, SchemaEventTarget): + assert c_copy.type is not c.type + self.columns[c.name] = c_copy + self.named_constraints: Dict[str, Constraint] = {} + self.unnamed_constraints = [] + self.col_named_constraints = {} + self.indexes: Dict[str, Index] = {} + self.new_indexes: Dict[str, Index] = {} + + for const in self.table.constraints: + if _is_type_bound(const): + continue + elif ( + self.reflected + and isinstance(const, CheckConstraint) + and not const.name + ): + # TODO: we are skipping unnamed reflected CheckConstraint + # because + # we have no way to determine _is_type_bound() for these. + pass + elif constraint_name_string(const.name): + self.named_constraints[const.name] = const + else: + self.unnamed_constraints.append(const) + + if not self.reflected: + for col in self.table.c: + for const in col.constraints: + if const.name: + self.col_named_constraints[const.name] = (col, const) + + for idx in self.table.indexes: + self.indexes[idx.name] = idx # type: ignore[index] + + for k in self.table.kwargs: + self.table_kwargs.setdefault(k, self.table.kwargs[k]) + + def _adjust_self_columns_for_partial_reordering(self) -> None: + pairs = set() + + col_by_idx = list(self.columns) + + if self.partial_reordering: + for tuple_ in self.partial_reordering: + for index, elem in enumerate(tuple_): + if index > 0: + pairs.add((tuple_[index - 1], elem)) + else: + for index, elem in enumerate(self.existing_ordering): + if index > 0: + pairs.add((col_by_idx[index - 1], elem)) + + pairs.update(self.add_col_ordering) + + # this can happen if some columns were dropped and not removed + # from existing_ordering. this should be prevented already, but + # conservatively making sure this didn't happen + pairs_list = [p for p in pairs if p[0] != p[1]] + + sorted_ = list( + topological.sort(pairs_list, col_by_idx, deterministic_order=True) + ) + self.columns = OrderedDict((k, self.columns[k]) for k in sorted_) + self.column_transfers = OrderedDict( + (k, self.column_transfers[k]) for k in sorted_ + ) + + def _transfer_elements_to_new_table(self) -> None: + assert self.new_table is None, "Can only create new table once" + + m = MetaData() + schema = self.table.schema + + if self.partial_reordering or self.add_col_ordering: + self._adjust_self_columns_for_partial_reordering() + + self.new_table = new_table = Table( + self.temp_table_name, + m, + *(list(self.columns.values()) + list(self.table_args)), + schema=schema, + **self.table_kwargs, + ) + + for const in ( + list(self.named_constraints.values()) + self.unnamed_constraints + ): + const_columns = {c.key for c in _columns_for_constraint(const)} + + if not const_columns.issubset(self.column_transfers): + continue + + const_copy: Constraint + if isinstance(const, ForeignKeyConstraint): + if _fk_is_self_referential(const): + # for self-referential constraint, refer to the + # *original* table name, and not _alembic_batch_temp. + # This is consistent with how we're handling + # FK constraints from other tables; we assume SQLite + # no foreign keys just keeps the names unchanged, so + # when we rename back, they match again. + const_copy = _copy( + const, schema=schema, target_table=self.table + ) + else: + # "target_table" for ForeignKeyConstraint.copy() is + # only used if the FK is detected as being + # self-referential, which we are handling above. + const_copy = _copy(const, schema=schema) + else: + const_copy = _copy( + const, schema=schema, target_table=new_table + ) + if isinstance(const, ForeignKeyConstraint): + self._setup_referent(m, const) + new_table.append_constraint(const_copy) + + def _gather_indexes_from_both_tables(self) -> List[Index]: + assert self.new_table is not None + idx: List[Index] = [] + + for idx_existing in self.indexes.values(): + # this is a lift-and-move from Table.to_metadata + + if idx_existing._column_flag: + continue + + idx_copy = Index( + idx_existing.name, + unique=idx_existing.unique, + *[ + _copy_expression(expr, self.new_table) + for expr in _idx_table_bound_expressions(idx_existing) + ], + _table=self.new_table, + **idx_existing.kwargs, + ) + idx.append(idx_copy) + + for index in self.new_indexes.values(): + idx.append( + Index( + index.name, + unique=index.unique, + *[self.new_table.c[col] for col in index.columns.keys()], + **index.kwargs, + ) + ) + return idx + + def _setup_referent( + self, metadata: MetaData, constraint: ForeignKeyConstraint + ) -> None: + spec = constraint.elements[0]._get_colspec() + parts = spec.split(".") + tname = parts[-2] + if len(parts) == 3: + referent_schema = parts[0] + else: + referent_schema = None + + if tname != self.temp_table_name: + key = sql_schema._get_table_key(tname, referent_schema) + + def colspec(elem: Any): + return elem._get_colspec() + + if key in metadata.tables: + t = metadata.tables[key] + for elem in constraint.elements: + colname = colspec(elem).split(".")[-1] + if colname not in t.c: + t.append_column(Column(colname, sqltypes.NULLTYPE)) + else: + Table( + tname, + metadata, + *[ + Column(n, sqltypes.NULLTYPE) + for n in [ + colspec(elem).split(".")[-1] + for elem in constraint.elements + ] + ], + schema=referent_schema, + ) + + def _create(self, op_impl: DefaultImpl) -> None: + self._transfer_elements_to_new_table() + + op_impl.prep_table_for_batch(self, self.table) + assert self.new_table is not None + op_impl.create_table(self.new_table) + + try: + op_impl._exec( + self.new_table.insert() + .inline() + .from_select( + list( + k + for k, transfer in self.column_transfers.items() + if "expr" in transfer + ), + select( + *[ + transfer["expr"] + for transfer in self.column_transfers.values() + if "expr" in transfer + ] + ), + ) + ) + op_impl.drop_table(self.table) + except: + op_impl.drop_table(self.new_table) + raise + else: + op_impl.rename_table( + self.temp_table_name, self.table.name, schema=self.table.schema + ) + self.new_table.name = self.table.name + try: + for idx in self._gather_indexes_from_both_tables(): + op_impl.create_index(idx) + finally: + self.new_table.name = self.temp_table_name + + def alter_column( + self, + table_name: str, + column_name: str, + nullable: Optional[bool] = None, + server_default: Union[ + _ServerDefaultType, None, Literal[False] + ] = False, + name: Optional[str] = None, + type_: Optional[TypeEngine] = None, + autoincrement: Optional[Union[bool, Literal["auto"]]] = None, + comment: Union[str, Literal[False]] = False, + **kw, + ) -> None: + existing = self.columns[column_name] + existing_transfer: Dict[str, Any] = self.column_transfers[column_name] + if name is not None and name != column_name: + # note that we don't change '.key' - we keep referring + # to the renamed column by its old key in _create(). neat! + existing.name = name + existing_transfer["name"] = name + + existing_type = kw.get("existing_type", None) + if existing_type: + resolved_existing_type = _resolve_for_variant( + kw["existing_type"], self.impl.dialect + ) + + # pop named constraints for Boolean/Enum for rename + if ( + isinstance(resolved_existing_type, SchemaEventTarget) + and resolved_existing_type.name # type:ignore[attr-defined] # noqa E501 + ): + self.named_constraints.pop( + resolved_existing_type.name, # type:ignore[attr-defined] # noqa E501 + None, + ) + + if type_ is not None: + type_ = sqltypes.to_instance(type_) + # old type is being discarded so turn off eventing + # rules. Alternatively we can + # erase the events set up by this type, but this is simpler. + # we also ignore the drop_constraint that will come here from + # Operations.implementation_for(alter_column) + + if isinstance(existing.type, SchemaEventTarget): + existing.type._create_events = ( # type:ignore[attr-defined] + existing.type.create_constraint # type:ignore[attr-defined] # noqa + ) = False + + self.impl.cast_for_batch_migrate( + existing, existing_transfer, type_ + ) + + existing.type = type_ + + # we *dont* however set events for the new type, because + # alter_column is invoked from + # Operations.implementation_for(alter_column) which already + # will emit an add_constraint() + + if nullable is not None: + existing.nullable = nullable + if server_default is not False: + if server_default is None: + existing.server_default = None + else: + sql_schema.DefaultClause( + server_default # type: ignore[arg-type] + )._set_parent(existing) + if autoincrement is not None: + existing.autoincrement = bool(autoincrement) + + if comment is not False: + existing.comment = comment + + def _setup_dependencies_for_add_column( + self, + colname: str, + insert_before: Optional[str], + insert_after: Optional[str], + ) -> None: + index_cols = self.existing_ordering + col_indexes = {name: i for i, name in enumerate(index_cols)} + + if not self.partial_reordering: + if insert_after: + if not insert_before: + if insert_after in col_indexes: + # insert after an existing column + idx = col_indexes[insert_after] + 1 + if idx < len(index_cols): + insert_before = index_cols[idx] + else: + # insert after a column that is also new + insert_before = dict(self.add_col_ordering)[ + insert_after + ] + if insert_before: + if not insert_after: + if insert_before in col_indexes: + # insert before an existing column + idx = col_indexes[insert_before] - 1 + if idx >= 0: + insert_after = index_cols[idx] + else: + # insert before a column that is also new + insert_after = { + b: a for a, b in self.add_col_ordering + }[insert_before] + + if insert_before: + self.add_col_ordering += ((colname, insert_before),) + if insert_after: + self.add_col_ordering += ((insert_after, colname),) + + if ( + not self.partial_reordering + and not insert_before + and not insert_after + and col_indexes + ): + self.add_col_ordering += ((index_cols[-1], colname),) + + def add_column( + self, + table_name: str, + column: Column[Any], + insert_before: Optional[str] = None, + insert_after: Optional[str] = None, + **kw, + ) -> None: + self._setup_dependencies_for_add_column( + column.name, insert_before, insert_after + ) + # we copy the column because operations.add_column() + # gives us a Column that is part of a Table already. + self.columns[column.name] = _copy(column, schema=self.table.schema) + self.column_transfers[column.name] = {} + + def drop_column( + self, + table_name: str, + column: Union[ColumnClause[Any], Column[Any]], + **kw, + ) -> None: + if column.name in self.table.primary_key.columns: + _remove_column_from_collection( + self.table.primary_key.columns, column + ) + del self.columns[column.name] + del self.column_transfers[column.name] + self.existing_ordering.remove(column.name) + + # pop named constraints for Boolean/Enum for rename + if ( + "existing_type" in kw + and isinstance(kw["existing_type"], SchemaEventTarget) + and kw["existing_type"].name # type:ignore[attr-defined] + ): + self.named_constraints.pop( + kw["existing_type"].name, None # type:ignore[attr-defined] + ) + + def create_column_comment(self, column): + """the batch table creation function will issue create_column_comment + on the real "impl" as part of the create table process. + + That is, the Column object will have the comment on it already, + so when it is received by add_column() it will be a normal part of + the CREATE TABLE and doesn't need an extra step here. + + """ + + def create_table_comment(self, table): + """the batch table creation function will issue create_table_comment + on the real "impl" as part of the create table process. + + """ + + def drop_table_comment(self, table): + """the batch table creation function will issue drop_table_comment + on the real "impl" as part of the create table process. + + """ + + def add_constraint(self, const: Constraint) -> None: + if not constraint_name_defined(const.name): + raise ValueError("Constraint must have a name") + if isinstance(const, sql_schema.PrimaryKeyConstraint): + if self.table.primary_key in self.unnamed_constraints: + self.unnamed_constraints.remove(self.table.primary_key) + + if constraint_name_string(const.name): + self.named_constraints[const.name] = const + else: + self.unnamed_constraints.append(const) + + def drop_constraint(self, const: Constraint) -> None: + if not const.name: + raise ValueError("Constraint must have a name") + try: + if const.name in self.col_named_constraints: + col, const = self.col_named_constraints.pop(const.name) + + for col_const in list(self.columns[col.name].constraints): + if col_const.name == const.name: + self.columns[col.name].constraints.remove(col_const) + elif constraint_name_string(const.name): + const = self.named_constraints.pop(const.name) + elif const in self.unnamed_constraints: + self.unnamed_constraints.remove(const) + + except KeyError: + if _is_type_bound(const): + # type-bound constraints are only included in the new + # table via their type object in any case, so ignore the + # drop_constraint() that comes here via the + # Operations.implementation_for(alter_column) + return + raise ValueError("No such constraint: '%s'" % const.name) + else: + if isinstance(const, PrimaryKeyConstraint): + for col in const.columns: + self.columns[col.name].primary_key = False + + def create_index(self, idx: Index) -> None: + self.new_indexes[idx.name] = idx # type: ignore[index] + + def drop_index(self, idx: Index) -> None: + try: + del self.indexes[idx.name] # type: ignore[arg-type] + except KeyError: + raise ValueError("No such index: '%s'" % idx.name) + + def rename_table(self, *arg, **kw): + raise NotImplementedError("TODO") diff --git a/venv/lib/python3.12/site-packages/alembic/operations/ops.py b/venv/lib/python3.12/site-packages/alembic/operations/ops.py new file mode 100644 index 00000000..7eda50d6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/operations/ops.py @@ -0,0 +1,2918 @@ +from __future__ import annotations + +from abc import abstractmethod +import os +import pathlib +import re +from typing import Any +from typing import Callable +from typing import cast +from typing import Dict +from typing import FrozenSet +from typing import Iterator +from typing import List +from typing import MutableMapping +from typing import Optional +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +from sqlalchemy.types import NULLTYPE + +from . import schemaobj +from .base import BatchOperations +from .base import Operations +from .. import util +from ..util import sqla_compat + +if TYPE_CHECKING: + from typing import Literal + + from sqlalchemy.sql import Executable + from sqlalchemy.sql.elements import ColumnElement + from sqlalchemy.sql.elements import conv + from sqlalchemy.sql.elements import quoted_name + from sqlalchemy.sql.elements import TextClause + from sqlalchemy.sql.schema import CheckConstraint + from sqlalchemy.sql.schema import Column + from sqlalchemy.sql.schema import Constraint + from sqlalchemy.sql.schema import ForeignKeyConstraint + from sqlalchemy.sql.schema import Index + from sqlalchemy.sql.schema import MetaData + from sqlalchemy.sql.schema import PrimaryKeyConstraint + from sqlalchemy.sql.schema import SchemaItem + from sqlalchemy.sql.schema import Table + from sqlalchemy.sql.schema import UniqueConstraint + from sqlalchemy.sql.selectable import TableClause + from sqlalchemy.sql.type_api import TypeEngine + + from ..autogenerate.rewriter import Rewriter + from ..ddl.base import _ServerDefaultType + from ..runtime.migration import MigrationContext + from ..script.revision import _RevIdType + +_T = TypeVar("_T", bound=Any) +_AC = TypeVar("_AC", bound="AddConstraintOp") + + +class MigrateOperation: + """base class for migration command and organization objects. + + This system is part of the operation extensibility API. + + .. seealso:: + + :ref:`operation_objects` + + :ref:`operation_plugins` + + :ref:`customizing_revision` + + """ + + @util.memoized_property + def info(self) -> Dict[Any, Any]: + """A dictionary that may be used to store arbitrary information + along with this :class:`.MigrateOperation` object. + + """ + return {} + + _mutations: FrozenSet[Rewriter] = frozenset() + + def reverse(self) -> MigrateOperation: + raise NotImplementedError + + def to_diff_tuple(self) -> Tuple[Any, ...]: + raise NotImplementedError + + +class AddConstraintOp(MigrateOperation): + """Represent an add constraint operation.""" + + add_constraint_ops = util.Dispatcher() + + @property + def constraint_type(self) -> str: + raise NotImplementedError() + + @classmethod + def register_add_constraint( + cls, type_: str + ) -> Callable[[Type[_AC]], Type[_AC]]: + def go(klass: Type[_AC]) -> Type[_AC]: + cls.add_constraint_ops.dispatch_for(type_)(klass.from_constraint) + return klass + + return go + + @classmethod + def from_constraint(cls, constraint: Constraint) -> AddConstraintOp: + return cls.add_constraint_ops.dispatch(constraint.__visit_name__)( # type: ignore[no-any-return] # noqa: E501 + constraint + ) + + @abstractmethod + def to_constraint( + self, migration_context: Optional[MigrationContext] = None + ) -> Constraint: + pass + + def reverse(self) -> DropConstraintOp: + return DropConstraintOp.from_constraint(self.to_constraint()) + + def to_diff_tuple(self) -> Tuple[str, Constraint]: + return ("add_constraint", self.to_constraint()) + + +@Operations.register_operation("drop_constraint") +@BatchOperations.register_operation("drop_constraint", "batch_drop_constraint") +class DropConstraintOp(MigrateOperation): + """Represent a drop constraint operation.""" + + def __init__( + self, + constraint_name: Optional[sqla_compat._ConstraintNameDefined], + table_name: str, + type_: Optional[str] = None, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + _reverse: Optional[AddConstraintOp] = None, + ) -> None: + self.constraint_name = constraint_name + self.table_name = table_name + self.constraint_type = type_ + self.schema = schema + self.if_exists = if_exists + self._reverse = _reverse + + def reverse(self) -> AddConstraintOp: + return AddConstraintOp.from_constraint(self.to_constraint()) + + def to_diff_tuple( + self, + ) -> Tuple[str, SchemaItem]: + if self.constraint_type == "foreignkey": + return ("remove_fk", self.to_constraint()) + else: + return ("remove_constraint", self.to_constraint()) + + @classmethod + def from_constraint(cls, constraint: Constraint) -> DropConstraintOp: + types = { + "unique_constraint": "unique", + "foreign_key_constraint": "foreignkey", + "primary_key_constraint": "primary", + "check_constraint": "check", + "column_check_constraint": "check", + "table_or_column_check_constraint": "check", + } + + constraint_table = sqla_compat._table_for_constraint(constraint) + return cls( + sqla_compat.constraint_name_or_none(constraint.name), + constraint_table.name, + schema=constraint_table.schema, + type_=types.get(constraint.__visit_name__), + _reverse=AddConstraintOp.from_constraint(constraint), + ) + + def to_constraint(self) -> Constraint: + if self._reverse is not None: + constraint = self._reverse.to_constraint() + constraint.name = self.constraint_name + constraint_table = sqla_compat._table_for_constraint(constraint) + constraint_table.name = self.table_name + constraint_table.schema = self.schema + + return constraint + else: + raise ValueError( + "constraint cannot be produced; " + "original constraint is not present" + ) + + @classmethod + def drop_constraint( + cls, + operations: Operations, + constraint_name: str, + table_name: str, + type_: Optional[str] = None, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + ) -> None: + r"""Drop a constraint of the given name, typically via DROP CONSTRAINT. + + :param constraint_name: name of the constraint. + :param table_name: table name. + :param type\_: optional, required on MySQL. can be + 'foreignkey', 'primary', 'unique', or 'check'. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_exists: If True, adds IF EXISTS operator when + dropping the constraint + + .. versionadded:: 1.16.0 + + """ + + op = cls( + constraint_name, + table_name, + type_=type_, + schema=schema, + if_exists=if_exists, + ) + return operations.invoke(op) + + @classmethod + def batch_drop_constraint( + cls, + operations: BatchOperations, + constraint_name: str, + type_: Optional[str] = None, + ) -> None: + """Issue a "drop constraint" instruction using the + current batch migration context. + + The batch form of this call omits the ``table_name`` and ``schema`` + arguments from the call. + + .. seealso:: + + :meth:`.Operations.drop_constraint` + + """ + op = cls( + constraint_name, + operations.impl.table_name, + type_=type_, + schema=operations.impl.schema, + ) + return operations.invoke(op) + + +@Operations.register_operation("create_primary_key") +@BatchOperations.register_operation( + "create_primary_key", "batch_create_primary_key" +) +@AddConstraintOp.register_add_constraint("primary_key_constraint") +class CreatePrimaryKeyOp(AddConstraintOp): + """Represent a create primary key operation.""" + + constraint_type = "primarykey" + + def __init__( + self, + constraint_name: Optional[sqla_compat._ConstraintNameDefined], + table_name: str, + columns: Sequence[str], + *, + schema: Optional[str] = None, + **kw: Any, + ) -> None: + self.constraint_name = constraint_name + self.table_name = table_name + self.columns = columns + self.schema = schema + self.kw = kw + + @classmethod + def from_constraint(cls, constraint: Constraint) -> CreatePrimaryKeyOp: + constraint_table = sqla_compat._table_for_constraint(constraint) + pk_constraint = cast("PrimaryKeyConstraint", constraint) + return cls( + sqla_compat.constraint_name_or_none(pk_constraint.name), + constraint_table.name, + pk_constraint.columns.keys(), + schema=constraint_table.schema, + **pk_constraint.dialect_kwargs, + ) + + def to_constraint( + self, migration_context: Optional[MigrationContext] = None + ) -> PrimaryKeyConstraint: + schema_obj = schemaobj.SchemaObjects(migration_context) + + return schema_obj.primary_key_constraint( + self.constraint_name, + self.table_name, + self.columns, + schema=self.schema, + **self.kw, + ) + + @classmethod + def create_primary_key( + cls, + operations: Operations, + constraint_name: Optional[str], + table_name: str, + columns: List[str], + *, + schema: Optional[str] = None, + ) -> None: + """Issue a "create primary key" instruction using the current + migration context. + + e.g.:: + + from alembic import op + + op.create_primary_key("pk_my_table", "my_table", ["id", "version"]) + + This internally generates a :class:`~sqlalchemy.schema.Table` object + containing the necessary columns, then generates a new + :class:`~sqlalchemy.schema.PrimaryKeyConstraint` + object which it then associates with the + :class:`~sqlalchemy.schema.Table`. + Any event listeners associated with this action will be fired + off normally. The :class:`~sqlalchemy.schema.AddConstraint` + construct is ultimately used to generate the ALTER statement. + + :param constraint_name: Name of the primary key constraint. The name + is necessary so that an ALTER statement can be emitted. For setups + that use an automated naming scheme such as that described at + :ref:`sqla:constraint_naming_conventions` + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param table_name: String name of the target table. + :param columns: a list of string column names to be applied to the + primary key constraint. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + """ + op = cls(constraint_name, table_name, columns, schema=schema) + return operations.invoke(op) + + @classmethod + def batch_create_primary_key( + cls, + operations: BatchOperations, + constraint_name: Optional[str], + columns: List[str], + ) -> None: + """Issue a "create primary key" instruction using the + current batch migration context. + + The batch form of this call omits the ``table_name`` and ``schema`` + arguments from the call. + + .. seealso:: + + :meth:`.Operations.create_primary_key` + + """ + op = cls( + constraint_name, + operations.impl.table_name, + columns, + schema=operations.impl.schema, + ) + return operations.invoke(op) + + +@Operations.register_operation("create_unique_constraint") +@BatchOperations.register_operation( + "create_unique_constraint", "batch_create_unique_constraint" +) +@AddConstraintOp.register_add_constraint("unique_constraint") +class CreateUniqueConstraintOp(AddConstraintOp): + """Represent a create unique constraint operation.""" + + constraint_type = "unique" + + def __init__( + self, + constraint_name: Optional[sqla_compat._ConstraintNameDefined], + table_name: str, + columns: Sequence[str], + *, + schema: Optional[str] = None, + **kw: Any, + ) -> None: + self.constraint_name = constraint_name + self.table_name = table_name + self.columns = columns + self.schema = schema + self.kw = kw + + @classmethod + def from_constraint( + cls, constraint: Constraint + ) -> CreateUniqueConstraintOp: + constraint_table = sqla_compat._table_for_constraint(constraint) + + uq_constraint = cast("UniqueConstraint", constraint) + + kw: Dict[str, Any] = {} + if uq_constraint.deferrable: + kw["deferrable"] = uq_constraint.deferrable + if uq_constraint.initially: + kw["initially"] = uq_constraint.initially + kw.update(uq_constraint.dialect_kwargs) + return cls( + sqla_compat.constraint_name_or_none(uq_constraint.name), + constraint_table.name, + [c.name for c in uq_constraint.columns], + schema=constraint_table.schema, + **kw, + ) + + def to_constraint( + self, migration_context: Optional[MigrationContext] = None + ) -> UniqueConstraint: + schema_obj = schemaobj.SchemaObjects(migration_context) + return schema_obj.unique_constraint( + self.constraint_name, + self.table_name, + self.columns, + schema=self.schema, + **self.kw, + ) + + @classmethod + def create_unique_constraint( + cls, + operations: Operations, + constraint_name: Optional[str], + table_name: str, + columns: Sequence[str], + *, + schema: Optional[str] = None, + **kw: Any, + ) -> Any: + """Issue a "create unique constraint" instruction using the + current migration context. + + e.g.:: + + from alembic import op + op.create_unique_constraint("uq_user_name", "user", ["name"]) + + This internally generates a :class:`~sqlalchemy.schema.Table` object + containing the necessary columns, then generates a new + :class:`~sqlalchemy.schema.UniqueConstraint` + object which it then associates with the + :class:`~sqlalchemy.schema.Table`. + Any event listeners associated with this action will be fired + off normally. The :class:`~sqlalchemy.schema.AddConstraint` + construct is ultimately used to generate the ALTER statement. + + :param name: Name of the unique constraint. The name is necessary + so that an ALTER statement can be emitted. For setups that + use an automated naming scheme such as that described at + :ref:`sqla:constraint_naming_conventions`, + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param table_name: String name of the source table. + :param columns: a list of string column names in the + source table. + :param deferrable: optional bool. If set, emit DEFERRABLE or + NOT DEFERRABLE when issuing DDL for this constraint. + :param initially: optional string. If set, emit INITIALLY + when issuing DDL for this constraint. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + """ + + op = cls(constraint_name, table_name, columns, schema=schema, **kw) + return operations.invoke(op) + + @classmethod + def batch_create_unique_constraint( + cls, + operations: BatchOperations, + constraint_name: str, + columns: Sequence[str], + **kw: Any, + ) -> Any: + """Issue a "create unique constraint" instruction using the + current batch migration context. + + The batch form of this call omits the ``source`` and ``schema`` + arguments from the call. + + .. seealso:: + + :meth:`.Operations.create_unique_constraint` + + """ + kw["schema"] = operations.impl.schema + op = cls(constraint_name, operations.impl.table_name, columns, **kw) + return operations.invoke(op) + + +@Operations.register_operation("create_foreign_key") +@BatchOperations.register_operation( + "create_foreign_key", "batch_create_foreign_key" +) +@AddConstraintOp.register_add_constraint("foreign_key_constraint") +class CreateForeignKeyOp(AddConstraintOp): + """Represent a create foreign key constraint operation.""" + + constraint_type = "foreignkey" + + def __init__( + self, + constraint_name: Optional[sqla_compat._ConstraintNameDefined], + source_table: str, + referent_table: str, + local_cols: List[str], + remote_cols: List[str], + **kw: Any, + ) -> None: + self.constraint_name = constraint_name + self.source_table = source_table + self.referent_table = referent_table + self.local_cols = local_cols + self.remote_cols = remote_cols + self.kw = kw + + def to_diff_tuple(self) -> Tuple[str, ForeignKeyConstraint]: + return ("add_fk", self.to_constraint()) + + @classmethod + def from_constraint(cls, constraint: Constraint) -> CreateForeignKeyOp: + fk_constraint = cast("ForeignKeyConstraint", constraint) + kw: Dict[str, Any] = {} + if fk_constraint.onupdate: + kw["onupdate"] = fk_constraint.onupdate + if fk_constraint.ondelete: + kw["ondelete"] = fk_constraint.ondelete + if fk_constraint.initially: + kw["initially"] = fk_constraint.initially + if fk_constraint.deferrable: + kw["deferrable"] = fk_constraint.deferrable + if fk_constraint.use_alter: + kw["use_alter"] = fk_constraint.use_alter + if fk_constraint.match: + kw["match"] = fk_constraint.match + + ( + source_schema, + source_table, + source_columns, + target_schema, + target_table, + target_columns, + onupdate, + ondelete, + deferrable, + initially, + ) = sqla_compat._fk_spec(fk_constraint) + + kw["source_schema"] = source_schema + kw["referent_schema"] = target_schema + kw.update(fk_constraint.dialect_kwargs) + return cls( + sqla_compat.constraint_name_or_none(fk_constraint.name), + source_table, + target_table, + source_columns, + target_columns, + **kw, + ) + + def to_constraint( + self, migration_context: Optional[MigrationContext] = None + ) -> ForeignKeyConstraint: + schema_obj = schemaobj.SchemaObjects(migration_context) + return schema_obj.foreign_key_constraint( + self.constraint_name, + self.source_table, + self.referent_table, + self.local_cols, + self.remote_cols, + **self.kw, + ) + + @classmethod + def create_foreign_key( + cls, + operations: Operations, + constraint_name: Optional[str], + source_table: str, + referent_table: str, + local_cols: List[str], + remote_cols: List[str], + *, + onupdate: Optional[str] = None, + ondelete: Optional[str] = None, + deferrable: Optional[bool] = None, + initially: Optional[str] = None, + match: Optional[str] = None, + source_schema: Optional[str] = None, + referent_schema: Optional[str] = None, + **dialect_kw: Any, + ) -> None: + """Issue a "create foreign key" instruction using the + current migration context. + + e.g.:: + + from alembic import op + + op.create_foreign_key( + "fk_user_address", + "address", + "user", + ["user_id"], + ["id"], + ) + + This internally generates a :class:`~sqlalchemy.schema.Table` object + containing the necessary columns, then generates a new + :class:`~sqlalchemy.schema.ForeignKeyConstraint` + object which it then associates with the + :class:`~sqlalchemy.schema.Table`. + Any event listeners associated with this action will be fired + off normally. The :class:`~sqlalchemy.schema.AddConstraint` + construct is ultimately used to generate the ALTER statement. + + :param constraint_name: Name of the foreign key constraint. The name + is necessary so that an ALTER statement can be emitted. For setups + that use an automated naming scheme such as that described at + :ref:`sqla:constraint_naming_conventions`, + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param source_table: String name of the source table. + :param referent_table: String name of the destination table. + :param local_cols: a list of string column names in the + source table. + :param remote_cols: a list of string column names in the + remote table. + :param onupdate: Optional string. If set, emit ON UPDATE when + issuing DDL for this constraint. Typical values include CASCADE, + DELETE and RESTRICT. + :param ondelete: Optional string. If set, emit ON DELETE when + issuing DDL for this constraint. Typical values include CASCADE, + DELETE and RESTRICT. + :param deferrable: optional bool. If set, emit DEFERRABLE or NOT + DEFERRABLE when issuing DDL for this constraint. + :param source_schema: Optional schema name of the source table. + :param referent_schema: Optional schema name of the destination table. + + """ + + op = cls( + constraint_name, + source_table, + referent_table, + local_cols, + remote_cols, + onupdate=onupdate, + ondelete=ondelete, + deferrable=deferrable, + source_schema=source_schema, + referent_schema=referent_schema, + initially=initially, + match=match, + **dialect_kw, + ) + return operations.invoke(op) + + @classmethod + def batch_create_foreign_key( + cls, + operations: BatchOperations, + constraint_name: Optional[str], + referent_table: str, + local_cols: List[str], + remote_cols: List[str], + *, + referent_schema: Optional[str] = None, + onupdate: Optional[str] = None, + ondelete: Optional[str] = None, + deferrable: Optional[bool] = None, + initially: Optional[str] = None, + match: Optional[str] = None, + **dialect_kw: Any, + ) -> None: + """Issue a "create foreign key" instruction using the + current batch migration context. + + The batch form of this call omits the ``source`` and ``source_schema`` + arguments from the call. + + e.g.:: + + with batch_alter_table("address") as batch_op: + batch_op.create_foreign_key( + "fk_user_address", + "user", + ["user_id"], + ["id"], + ) + + .. seealso:: + + :meth:`.Operations.create_foreign_key` + + """ + op = cls( + constraint_name, + operations.impl.table_name, + referent_table, + local_cols, + remote_cols, + onupdate=onupdate, + ondelete=ondelete, + deferrable=deferrable, + source_schema=operations.impl.schema, + referent_schema=referent_schema, + initially=initially, + match=match, + **dialect_kw, + ) + return operations.invoke(op) + + +@Operations.register_operation("create_check_constraint") +@BatchOperations.register_operation( + "create_check_constraint", "batch_create_check_constraint" +) +@AddConstraintOp.register_add_constraint("check_constraint") +@AddConstraintOp.register_add_constraint("table_or_column_check_constraint") +@AddConstraintOp.register_add_constraint("column_check_constraint") +class CreateCheckConstraintOp(AddConstraintOp): + """Represent a create check constraint operation.""" + + constraint_type = "check" + + def __init__( + self, + constraint_name: Optional[sqla_compat._ConstraintNameDefined], + table_name: str, + condition: Union[str, TextClause, ColumnElement[Any]], + *, + schema: Optional[str] = None, + **kw: Any, + ) -> None: + self.constraint_name = constraint_name + self.table_name = table_name + self.condition = condition + self.schema = schema + self.kw = kw + + @classmethod + def from_constraint( + cls, constraint: Constraint + ) -> CreateCheckConstraintOp: + constraint_table = sqla_compat._table_for_constraint(constraint) + + ck_constraint = cast("CheckConstraint", constraint) + return cls( + sqla_compat.constraint_name_or_none(ck_constraint.name), + constraint_table.name, + cast("ColumnElement[Any]", ck_constraint.sqltext), + schema=constraint_table.schema, + **ck_constraint.dialect_kwargs, + ) + + def to_constraint( + self, migration_context: Optional[MigrationContext] = None + ) -> CheckConstraint: + schema_obj = schemaobj.SchemaObjects(migration_context) + return schema_obj.check_constraint( + self.constraint_name, + self.table_name, + self.condition, + schema=self.schema, + **self.kw, + ) + + @classmethod + def create_check_constraint( + cls, + operations: Operations, + constraint_name: Optional[str], + table_name: str, + condition: Union[str, ColumnElement[bool], TextClause], + *, + schema: Optional[str] = None, + **kw: Any, + ) -> None: + """Issue a "create check constraint" instruction using the + current migration context. + + e.g.:: + + from alembic import op + from sqlalchemy.sql import column, func + + op.create_check_constraint( + "ck_user_name_len", + "user", + func.len(column("name")) > 5, + ) + + CHECK constraints are usually against a SQL expression, so ad-hoc + table metadata is usually needed. The function will convert the given + arguments into a :class:`sqlalchemy.schema.CheckConstraint` bound + to an anonymous table in order to emit the CREATE statement. + + :param name: Name of the check constraint. The name is necessary + so that an ALTER statement can be emitted. For setups that + use an automated naming scheme such as that described at + :ref:`sqla:constraint_naming_conventions`, + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param table_name: String name of the source table. + :param condition: SQL expression that's the condition of the + constraint. Can be a string or SQLAlchemy expression language + structure. + :param deferrable: optional bool. If set, emit DEFERRABLE or + NOT DEFERRABLE when issuing DDL for this constraint. + :param initially: optional string. If set, emit INITIALLY + when issuing DDL for this constraint. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + """ + op = cls(constraint_name, table_name, condition, schema=schema, **kw) + return operations.invoke(op) + + @classmethod + def batch_create_check_constraint( + cls, + operations: BatchOperations, + constraint_name: str, + condition: Union[str, ColumnElement[bool], TextClause], + **kw: Any, + ) -> None: + """Issue a "create check constraint" instruction using the + current batch migration context. + + The batch form of this call omits the ``source`` and ``schema`` + arguments from the call. + + .. seealso:: + + :meth:`.Operations.create_check_constraint` + + """ + op = cls( + constraint_name, + operations.impl.table_name, + condition, + schema=operations.impl.schema, + **kw, + ) + return operations.invoke(op) + + +@Operations.register_operation("create_index") +@BatchOperations.register_operation("create_index", "batch_create_index") +class CreateIndexOp(MigrateOperation): + """Represent a create index operation.""" + + def __init__( + self, + index_name: Optional[str], + table_name: str, + columns: Sequence[Union[str, TextClause, ColumnElement[Any]]], + *, + schema: Optional[str] = None, + unique: bool = False, + if_not_exists: Optional[bool] = None, + **kw: Any, + ) -> None: + self.index_name = index_name + self.table_name = table_name + self.columns = columns + self.schema = schema + self.unique = unique + self.if_not_exists = if_not_exists + self.kw = kw + + def reverse(self) -> DropIndexOp: + return DropIndexOp.from_index(self.to_index()) + + def to_diff_tuple(self) -> Tuple[str, Index]: + return ("add_index", self.to_index()) + + @classmethod + def from_index(cls, index: Index) -> CreateIndexOp: + assert index.table is not None + return cls( + index.name, + index.table.name, + index.expressions, + schema=index.table.schema, + unique=index.unique, + **index.kwargs, + ) + + def to_index( + self, migration_context: Optional[MigrationContext] = None + ) -> Index: + schema_obj = schemaobj.SchemaObjects(migration_context) + + idx = schema_obj.index( + self.index_name, + self.table_name, + self.columns, + schema=self.schema, + unique=self.unique, + **self.kw, + ) + return idx + + @classmethod + def create_index( + cls, + operations: Operations, + index_name: Optional[str], + table_name: str, + columns: Sequence[Union[str, TextClause, ColumnElement[Any]]], + *, + schema: Optional[str] = None, + unique: bool = False, + if_not_exists: Optional[bool] = None, + **kw: Any, + ) -> None: + r"""Issue a "create index" instruction using the current + migration context. + + e.g.:: + + from alembic import op + + op.create_index("ik_test", "t1", ["foo", "bar"]) + + Functional indexes can be produced by using the + :func:`sqlalchemy.sql.expression.text` construct:: + + from alembic import op + from sqlalchemy import text + + op.create_index("ik_test", "t1", [text("lower(foo)")]) + + :param index_name: name of the index. + :param table_name: name of the owning table. + :param columns: a list consisting of string column names and/or + :func:`~sqlalchemy.sql.expression.text` constructs. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param unique: If True, create a unique index. + + :param quote: Force quoting of this column's name on or off, + corresponding to ``True`` or ``False``. When left at its default + of ``None``, the column identifier will be quoted according to + whether the name is case sensitive (identifiers with at least one + upper case character are treated as case sensitive), or if it's a + reserved word. This flag is only needed to force quoting of a + reserved word which is not known by the SQLAlchemy dialect. + + :param if_not_exists: If True, adds IF NOT EXISTS operator when + creating the new index. + + .. versionadded:: 1.12.0 + + :param \**kw: Additional keyword arguments not mentioned above are + dialect specific, and passed in the form + ``_``. + See the documentation regarding an individual dialect at + :ref:`dialect_toplevel` for detail on documented arguments. + + """ + op = cls( + index_name, + table_name, + columns, + schema=schema, + unique=unique, + if_not_exists=if_not_exists, + **kw, + ) + return operations.invoke(op) + + @classmethod + def batch_create_index( + cls, + operations: BatchOperations, + index_name: str, + columns: List[str], + **kw: Any, + ) -> None: + """Issue a "create index" instruction using the + current batch migration context. + + .. seealso:: + + :meth:`.Operations.create_index` + + """ + + op = cls( + index_name, + operations.impl.table_name, + columns, + schema=operations.impl.schema, + **kw, + ) + return operations.invoke(op) + + +@Operations.register_operation("drop_index") +@BatchOperations.register_operation("drop_index", "batch_drop_index") +class DropIndexOp(MigrateOperation): + """Represent a drop index operation.""" + + def __init__( + self, + index_name: Union[quoted_name, str, conv], + table_name: Optional[str] = None, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + _reverse: Optional[CreateIndexOp] = None, + **kw: Any, + ) -> None: + self.index_name = index_name + self.table_name = table_name + self.schema = schema + self.if_exists = if_exists + self._reverse = _reverse + self.kw = kw + + def to_diff_tuple(self) -> Tuple[str, Index]: + return ("remove_index", self.to_index()) + + def reverse(self) -> CreateIndexOp: + return CreateIndexOp.from_index(self.to_index()) + + @classmethod + def from_index(cls, index: Index) -> DropIndexOp: + assert index.table is not None + return cls( + index.name, # type: ignore[arg-type] + table_name=index.table.name, + schema=index.table.schema, + _reverse=CreateIndexOp.from_index(index), + unique=index.unique, + **index.kwargs, + ) + + def to_index( + self, migration_context: Optional[MigrationContext] = None + ) -> Index: + schema_obj = schemaobj.SchemaObjects(migration_context) + + # need a dummy column name here since SQLAlchemy + # 0.7.6 and further raises on Index with no columns + return schema_obj.index( + self.index_name, + self.table_name, + self._reverse.columns if self._reverse else ["x"], + schema=self.schema, + **self.kw, + ) + + @classmethod + def drop_index( + cls, + operations: Operations, + index_name: str, + table_name: Optional[str] = None, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + **kw: Any, + ) -> None: + r"""Issue a "drop index" instruction using the current + migration context. + + e.g.:: + + drop_index("accounts") + + :param index_name: name of the index. + :param table_name: name of the owning table. Some + backends such as Microsoft SQL Server require this. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + :param if_exists: If True, adds IF EXISTS operator when + dropping the index. + + .. versionadded:: 1.12.0 + + :param \**kw: Additional keyword arguments not mentioned above are + dialect specific, and passed in the form + ``_``. + See the documentation regarding an individual dialect at + :ref:`dialect_toplevel` for detail on documented arguments. + + """ + op = cls( + index_name, + table_name=table_name, + schema=schema, + if_exists=if_exists, + **kw, + ) + return operations.invoke(op) + + @classmethod + def batch_drop_index( + cls, operations: BatchOperations, index_name: str, **kw: Any + ) -> None: + """Issue a "drop index" instruction using the + current batch migration context. + + .. seealso:: + + :meth:`.Operations.drop_index` + + """ + + op = cls( + index_name, + table_name=operations.impl.table_name, + schema=operations.impl.schema, + **kw, + ) + return operations.invoke(op) + + +@Operations.register_operation("create_table") +class CreateTableOp(MigrateOperation): + """Represent a create table operation.""" + + def __init__( + self, + table_name: str, + columns: Sequence[SchemaItem], + *, + schema: Optional[str] = None, + if_not_exists: Optional[bool] = None, + _namespace_metadata: Optional[MetaData] = None, + _constraints_included: bool = False, + **kw: Any, + ) -> None: + self.table_name = table_name + self.columns = columns + self.schema = schema + self.if_not_exists = if_not_exists + self.info = kw.pop("info", {}) + self.comment = kw.pop("comment", None) + self.prefixes = kw.pop("prefixes", None) + self.kw = kw + self._namespace_metadata = _namespace_metadata + self._constraints_included = _constraints_included + + def reverse(self) -> DropTableOp: + return DropTableOp.from_table( + self.to_table(), _namespace_metadata=self._namespace_metadata + ) + + def to_diff_tuple(self) -> Tuple[str, Table]: + return ("add_table", self.to_table()) + + @classmethod + def from_table( + cls, table: Table, *, _namespace_metadata: Optional[MetaData] = None + ) -> CreateTableOp: + if _namespace_metadata is None: + _namespace_metadata = table.metadata + + return cls( + table.name, + list(table.c) + list(table.constraints), + schema=table.schema, + _namespace_metadata=_namespace_metadata, + # given a Table() object, this Table will contain full Index() + # and UniqueConstraint objects already constructed in response to + # each unique=True / index=True flag on a Column. Carry this + # state along so that when we re-convert back into a Table, we + # skip unique=True/index=True so that these constraints are + # not doubled up. see #844 #848 + _constraints_included=True, + comment=table.comment, + info=dict(table.info), + prefixes=list(table._prefixes), + **table.kwargs, + ) + + def to_table( + self, migration_context: Optional[MigrationContext] = None + ) -> Table: + schema_obj = schemaobj.SchemaObjects(migration_context) + + return schema_obj.table( + self.table_name, + *self.columns, + schema=self.schema, + prefixes=list(self.prefixes) if self.prefixes else [], + comment=self.comment, + info=self.info.copy() if self.info else {}, + _constraints_included=self._constraints_included, + **self.kw, + ) + + @classmethod + def create_table( + cls, + operations: Operations, + table_name: str, + *columns: SchemaItem, + if_not_exists: Optional[bool] = None, + **kw: Any, + ) -> Table: + r"""Issue a "create table" instruction using the current migration + context. + + This directive receives an argument list similar to that of the + traditional :class:`sqlalchemy.schema.Table` construct, but without the + metadata:: + + from sqlalchemy import INTEGER, VARCHAR, NVARCHAR, Column + from alembic import op + + op.create_table( + "account", + Column("id", INTEGER, primary_key=True), + Column("name", VARCHAR(50), nullable=False), + Column("description", NVARCHAR(200)), + Column("timestamp", TIMESTAMP, server_default=func.now()), + ) + + Note that :meth:`.create_table` accepts + :class:`~sqlalchemy.schema.Column` + constructs directly from the SQLAlchemy library. In particular, + default values to be created on the database side are + specified using the ``server_default`` parameter, and not + ``default`` which only specifies Python-side defaults:: + + from alembic import op + from sqlalchemy import Column, TIMESTAMP, func + + # specify "DEFAULT NOW" along with the "timestamp" column + op.create_table( + "account", + Column("id", INTEGER, primary_key=True), + Column("timestamp", TIMESTAMP, server_default=func.now()), + ) + + The function also returns a newly created + :class:`~sqlalchemy.schema.Table` object, corresponding to the table + specification given, which is suitable for + immediate SQL operations, in particular + :meth:`.Operations.bulk_insert`:: + + from sqlalchemy import INTEGER, VARCHAR, NVARCHAR, Column + from alembic import op + + account_table = op.create_table( + "account", + Column("id", INTEGER, primary_key=True), + Column("name", VARCHAR(50), nullable=False), + Column("description", NVARCHAR(200)), + Column("timestamp", TIMESTAMP, server_default=func.now()), + ) + + op.bulk_insert( + account_table, + [ + {"name": "A1", "description": "account 1"}, + {"name": "A2", "description": "account 2"}, + ], + ) + + :param table_name: Name of the table + :param \*columns: collection of :class:`~sqlalchemy.schema.Column` + objects within + the table, as well as optional :class:`~sqlalchemy.schema.Constraint` + objects + and :class:`~.sqlalchemy.schema.Index` objects. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_not_exists: If True, adds IF NOT EXISTS operator when + creating the new table. + + .. versionadded:: 1.13.3 + :param \**kw: Other keyword arguments are passed to the underlying + :class:`sqlalchemy.schema.Table` object created for the command. + + :return: the :class:`~sqlalchemy.schema.Table` object corresponding + to the parameters given. + + """ + op = cls(table_name, columns, if_not_exists=if_not_exists, **kw) + return operations.invoke(op) + + +@Operations.register_operation("drop_table") +class DropTableOp(MigrateOperation): + """Represent a drop table operation.""" + + def __init__( + self, + table_name: str, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + table_kw: Optional[MutableMapping[Any, Any]] = None, + _reverse: Optional[CreateTableOp] = None, + ) -> None: + self.table_name = table_name + self.schema = schema + self.if_exists = if_exists + self.table_kw = table_kw or {} + self.comment = self.table_kw.pop("comment", None) + self.info = self.table_kw.pop("info", None) + self.prefixes = self.table_kw.pop("prefixes", None) + self._reverse = _reverse + + def to_diff_tuple(self) -> Tuple[str, Table]: + return ("remove_table", self.to_table()) + + def reverse(self) -> CreateTableOp: + return CreateTableOp.from_table(self.to_table()) + + @classmethod + def from_table( + cls, table: Table, *, _namespace_metadata: Optional[MetaData] = None + ) -> DropTableOp: + return cls( + table.name, + schema=table.schema, + table_kw={ + "comment": table.comment, + "info": dict(table.info), + "prefixes": list(table._prefixes), + **table.kwargs, + }, + _reverse=CreateTableOp.from_table( + table, _namespace_metadata=_namespace_metadata + ), + ) + + def to_table( + self, migration_context: Optional[MigrationContext] = None + ) -> Table: + if self._reverse: + cols_and_constraints = self._reverse.columns + else: + cols_and_constraints = [] + + schema_obj = schemaobj.SchemaObjects(migration_context) + t = schema_obj.table( + self.table_name, + *cols_and_constraints, + comment=self.comment, + info=self.info.copy() if self.info else {}, + prefixes=list(self.prefixes) if self.prefixes else [], + schema=self.schema, + _constraints_included=( + self._reverse._constraints_included if self._reverse else False + ), + **self.table_kw, + ) + return t + + @classmethod + def drop_table( + cls, + operations: Operations, + table_name: str, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + **kw: Any, + ) -> None: + r"""Issue a "drop table" instruction using the current + migration context. + + + e.g.:: + + drop_table("accounts") + + :param table_name: Name of the table + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_exists: If True, adds IF EXISTS operator when + dropping the table. + + .. versionadded:: 1.13.3 + :param \**kw: Other keyword arguments are passed to the underlying + :class:`sqlalchemy.schema.Table` object created for the command. + + """ + op = cls(table_name, schema=schema, if_exists=if_exists, table_kw=kw) + operations.invoke(op) + + +class AlterTableOp(MigrateOperation): + """Represent an alter table operation.""" + + def __init__( + self, + table_name: str, + *, + schema: Optional[str] = None, + ) -> None: + self.table_name = table_name + self.schema = schema + + +@Operations.register_operation("rename_table") +class RenameTableOp(AlterTableOp): + """Represent a rename table operation.""" + + def __init__( + self, + old_table_name: str, + new_table_name: str, + *, + schema: Optional[str] = None, + ) -> None: + super().__init__(old_table_name, schema=schema) + self.new_table_name = new_table_name + + @classmethod + def rename_table( + cls, + operations: Operations, + old_table_name: str, + new_table_name: str, + *, + schema: Optional[str] = None, + ) -> None: + """Emit an ALTER TABLE to rename a table. + + :param old_table_name: old name. + :param new_table_name: new name. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + + """ + op = cls(old_table_name, new_table_name, schema=schema) + return operations.invoke(op) + + +@Operations.register_operation("create_table_comment") +@BatchOperations.register_operation( + "create_table_comment", "batch_create_table_comment" +) +class CreateTableCommentOp(AlterTableOp): + """Represent a COMMENT ON `table` operation.""" + + def __init__( + self, + table_name: str, + comment: Optional[str], + *, + schema: Optional[str] = None, + existing_comment: Optional[str] = None, + ) -> None: + self.table_name = table_name + self.comment = comment + self.existing_comment = existing_comment + self.schema = schema + + @classmethod + def create_table_comment( + cls, + operations: Operations, + table_name: str, + comment: Optional[str], + *, + existing_comment: Optional[str] = None, + schema: Optional[str] = None, + ) -> None: + """Emit a COMMENT ON operation to set the comment for a table. + + :param table_name: string name of the target table. + :param comment: string value of the comment being registered against + the specified table. + :param existing_comment: String value of a comment + already registered on the specified table, used within autogenerate + so that the operation is reversible, but not required for direct + use. + + .. seealso:: + + :meth:`.Operations.drop_table_comment` + + :paramref:`.Operations.alter_column.comment` + + """ + + op = cls( + table_name, + comment, + existing_comment=existing_comment, + schema=schema, + ) + return operations.invoke(op) + + @classmethod + def batch_create_table_comment( + cls, + operations: BatchOperations, + comment: Optional[str], + *, + existing_comment: Optional[str] = None, + ) -> None: + """Emit a COMMENT ON operation to set the comment for a table + using the current batch migration context. + + :param comment: string value of the comment being registered against + the specified table. + :param existing_comment: String value of a comment + already registered on the specified table, used within autogenerate + so that the operation is reversible, but not required for direct + use. + + """ + + op = cls( + operations.impl.table_name, + comment, + existing_comment=existing_comment, + schema=operations.impl.schema, + ) + return operations.invoke(op) + + def reverse(self) -> Union[CreateTableCommentOp, DropTableCommentOp]: + """Reverses the COMMENT ON operation against a table.""" + if self.existing_comment is None: + return DropTableCommentOp( + self.table_name, + existing_comment=self.comment, + schema=self.schema, + ) + else: + return CreateTableCommentOp( + self.table_name, + self.existing_comment, + existing_comment=self.comment, + schema=self.schema, + ) + + def to_table( + self, migration_context: Optional[MigrationContext] = None + ) -> Table: + schema_obj = schemaobj.SchemaObjects(migration_context) + + return schema_obj.table( + self.table_name, schema=self.schema, comment=self.comment + ) + + def to_diff_tuple(self) -> Tuple[Any, ...]: + return ("add_table_comment", self.to_table(), self.existing_comment) + + +@Operations.register_operation("drop_table_comment") +@BatchOperations.register_operation( + "drop_table_comment", "batch_drop_table_comment" +) +class DropTableCommentOp(AlterTableOp): + """Represent an operation to remove the comment from a table.""" + + def __init__( + self, + table_name: str, + *, + schema: Optional[str] = None, + existing_comment: Optional[str] = None, + ) -> None: + self.table_name = table_name + self.existing_comment = existing_comment + self.schema = schema + + @classmethod + def drop_table_comment( + cls, + operations: Operations, + table_name: str, + *, + existing_comment: Optional[str] = None, + schema: Optional[str] = None, + ) -> None: + """Issue a "drop table comment" operation to + remove an existing comment set on a table. + + :param table_name: string name of the target table. + :param existing_comment: An optional string value of a comment already + registered on the specified table. + + .. seealso:: + + :meth:`.Operations.create_table_comment` + + :paramref:`.Operations.alter_column.comment` + + """ + + op = cls(table_name, existing_comment=existing_comment, schema=schema) + return operations.invoke(op) + + @classmethod + def batch_drop_table_comment( + cls, + operations: BatchOperations, + *, + existing_comment: Optional[str] = None, + ) -> None: + """Issue a "drop table comment" operation to + remove an existing comment set on a table using the current + batch operations context. + + :param existing_comment: An optional string value of a comment already + registered on the specified table. + + """ + + op = cls( + operations.impl.table_name, + existing_comment=existing_comment, + schema=operations.impl.schema, + ) + return operations.invoke(op) + + def reverse(self) -> CreateTableCommentOp: + """Reverses the COMMENT ON operation against a table.""" + return CreateTableCommentOp( + self.table_name, self.existing_comment, schema=self.schema + ) + + def to_table( + self, migration_context: Optional[MigrationContext] = None + ) -> Table: + schema_obj = schemaobj.SchemaObjects(migration_context) + + return schema_obj.table(self.table_name, schema=self.schema) + + def to_diff_tuple(self) -> Tuple[Any, ...]: + return ("remove_table_comment", self.to_table()) + + +@Operations.register_operation("alter_column") +@BatchOperations.register_operation("alter_column", "batch_alter_column") +class AlterColumnOp(AlterTableOp): + """Represent an alter column operation.""" + + def __init__( + self, + table_name: str, + column_name: str, + *, + schema: Optional[str] = None, + existing_type: Optional[Any] = None, + existing_server_default: Union[ + _ServerDefaultType, None, Literal[False] + ] = False, + existing_nullable: Optional[bool] = None, + existing_comment: Optional[str] = None, + modify_nullable: Optional[bool] = None, + modify_comment: Optional[Union[str, Literal[False]]] = False, + modify_server_default: Any = False, + modify_name: Optional[str] = None, + modify_type: Optional[Any] = None, + **kw: Any, + ) -> None: + super().__init__(table_name, schema=schema) + self.column_name = column_name + self.existing_type = existing_type + self.existing_server_default = existing_server_default + self.existing_nullable = existing_nullable + self.existing_comment = existing_comment + self.modify_nullable = modify_nullable + self.modify_comment = modify_comment + self.modify_server_default = modify_server_default + self.modify_name = modify_name + self.modify_type = modify_type + self.kw = kw + + def to_diff_tuple(self) -> Any: + col_diff = [] + schema, tname, cname = self.schema, self.table_name, self.column_name + + if self.modify_type is not None: + col_diff.append( + ( + "modify_type", + schema, + tname, + cname, + { + "existing_nullable": self.existing_nullable, + "existing_server_default": ( + self.existing_server_default + ), + "existing_comment": self.existing_comment, + }, + self.existing_type, + self.modify_type, + ) + ) + + if self.modify_nullable is not None: + col_diff.append( + ( + "modify_nullable", + schema, + tname, + cname, + { + "existing_type": self.existing_type, + "existing_server_default": ( + self.existing_server_default + ), + "existing_comment": self.existing_comment, + }, + self.existing_nullable, + self.modify_nullable, + ) + ) + + if self.modify_server_default is not False: + col_diff.append( + ( + "modify_default", + schema, + tname, + cname, + { + "existing_nullable": self.existing_nullable, + "existing_type": self.existing_type, + "existing_comment": self.existing_comment, + }, + self.existing_server_default, + self.modify_server_default, + ) + ) + + if self.modify_comment is not False: + col_diff.append( + ( + "modify_comment", + schema, + tname, + cname, + { + "existing_nullable": self.existing_nullable, + "existing_type": self.existing_type, + "existing_server_default": ( + self.existing_server_default + ), + }, + self.existing_comment, + self.modify_comment, + ) + ) + + return col_diff + + def has_changes(self) -> bool: + hc1 = ( + self.modify_nullable is not None + or self.modify_server_default is not False + or self.modify_type is not None + or self.modify_comment is not False + ) + if hc1: + return True + for kw in self.kw: + if kw.startswith("modify_"): + return True + else: + return False + + def reverse(self) -> AlterColumnOp: + kw = self.kw.copy() + kw["existing_type"] = self.existing_type + kw["existing_nullable"] = self.existing_nullable + kw["existing_server_default"] = self.existing_server_default + kw["existing_comment"] = self.existing_comment + if self.modify_type is not None: + kw["modify_type"] = self.modify_type + if self.modify_nullable is not None: + kw["modify_nullable"] = self.modify_nullable + if self.modify_server_default is not False: + kw["modify_server_default"] = self.modify_server_default + if self.modify_comment is not False: + kw["modify_comment"] = self.modify_comment + + # TODO: make this a little simpler + all_keys = { + m.group(1) + for m in [re.match(r"^(?:existing_|modify_)(.+)$", k) for k in kw] + if m + } + + for k in all_keys: + if "modify_%s" % k in kw: + swap = kw["existing_%s" % k] + kw["existing_%s" % k] = kw["modify_%s" % k] + kw["modify_%s" % k] = swap + + return self.__class__( + self.table_name, self.column_name, schema=self.schema, **kw + ) + + @classmethod + def alter_column( + cls, + operations: Operations, + table_name: str, + column_name: str, + *, + nullable: Optional[bool] = None, + comment: Optional[Union[str, Literal[False]]] = False, + server_default: Union[ + _ServerDefaultType, None, Literal[False] + ] = False, + new_column_name: Optional[str] = None, + type_: Optional[Union[TypeEngine[Any], Type[TypeEngine[Any]]]] = None, + existing_type: Optional[ + Union[TypeEngine[Any], Type[TypeEngine[Any]]] + ] = None, + existing_server_default: Union[ + _ServerDefaultType, None, Literal[False] + ] = False, + existing_nullable: Optional[bool] = None, + existing_comment: Optional[str] = None, + schema: Optional[str] = None, + **kw: Any, + ) -> None: + r"""Issue an "alter column" instruction using the + current migration context. + + Generally, only that aspect of the column which + is being changed, i.e. name, type, nullability, + default, needs to be specified. Multiple changes + can also be specified at once and the backend should + "do the right thing", emitting each change either + separately or together as the backend allows. + + MySQL has special requirements here, since MySQL + cannot ALTER a column without a full specification. + When producing MySQL-compatible migration files, + it is recommended that the ``existing_type``, + ``existing_server_default``, and ``existing_nullable`` + parameters be present, if not being altered. + + Type changes which are against the SQLAlchemy + "schema" types :class:`~sqlalchemy.types.Boolean` + and :class:`~sqlalchemy.types.Enum` may also + add or drop constraints which accompany those + types on backends that don't support them natively. + The ``existing_type`` argument is + used in this case to identify and remove a previous + constraint that was bound to the type object. + + :param table_name: string name of the target table. + :param column_name: string name of the target column, + as it exists before the operation begins. + :param nullable: Optional; specify ``True`` or ``False`` + to alter the column's nullability. + :param server_default: Optional; specify a string + SQL expression, :func:`~sqlalchemy.sql.expression.text`, + or :class:`~sqlalchemy.schema.DefaultClause` to indicate + an alteration to the column's default value. + Set to ``None`` to have the default removed. + :param comment: optional string text of a new comment to add to the + column. + :param new_column_name: Optional; specify a string name here to + indicate the new name within a column rename operation. + :param type\_: Optional; a :class:`~sqlalchemy.types.TypeEngine` + type object to specify a change to the column's type. + For SQLAlchemy types that also indicate a constraint (i.e. + :class:`~sqlalchemy.types.Boolean`, :class:`~sqlalchemy.types.Enum`), + the constraint is also generated. + :param autoincrement: set the ``AUTO_INCREMENT`` flag of the column; + currently understood by the MySQL dialect. + :param existing_type: Optional; a + :class:`~sqlalchemy.types.TypeEngine` + type object to specify the previous type. This + is required for all MySQL column alter operations that + don't otherwise specify a new type, as well as for + when nullability is being changed on a SQL Server + column. It is also used if the type is a so-called + SQLAlchemy "schema" type which may define a constraint (i.e. + :class:`~sqlalchemy.types.Boolean`, + :class:`~sqlalchemy.types.Enum`), + so that the constraint can be dropped. + :param existing_server_default: Optional; The existing + default value of the column. Required on MySQL if + an existing default is not being changed; else MySQL + removes the default. + :param existing_nullable: Optional; the existing nullability + of the column. Required on MySQL if the existing nullability + is not being changed; else MySQL sets this to NULL. + :param existing_autoincrement: Optional; the existing autoincrement + of the column. Used for MySQL's system of altering a column + that specifies ``AUTO_INCREMENT``. + :param existing_comment: string text of the existing comment on the + column to be maintained. Required on MySQL if the existing comment + on the column is not being changed. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param postgresql_using: String argument which will indicate a + SQL expression to render within the Postgresql-specific USING clause + within ALTER COLUMN. This string is taken directly as raw SQL which + must explicitly include any necessary quoting or escaping of tokens + within the expression. + + """ + + alt = cls( + table_name, + column_name, + schema=schema, + existing_type=existing_type, + existing_server_default=existing_server_default, + existing_nullable=existing_nullable, + existing_comment=existing_comment, + modify_name=new_column_name, + modify_type=type_, + modify_server_default=server_default, + modify_nullable=nullable, + modify_comment=comment, + **kw, + ) + + return operations.invoke(alt) + + @classmethod + def batch_alter_column( + cls, + operations: BatchOperations, + column_name: str, + *, + nullable: Optional[bool] = None, + comment: Optional[Union[str, Literal[False]]] = False, + server_default: Union[ + _ServerDefaultType, None, Literal[False] + ] = False, + new_column_name: Optional[str] = None, + type_: Optional[Union[TypeEngine[Any], Type[TypeEngine[Any]]]] = None, + existing_type: Optional[ + Union[TypeEngine[Any], Type[TypeEngine[Any]]] + ] = None, + existing_server_default: Union[ + _ServerDefaultType, None, Literal[False] + ] = False, + existing_nullable: Optional[bool] = None, + existing_comment: Optional[str] = None, + insert_before: Optional[str] = None, + insert_after: Optional[str] = None, + **kw: Any, + ) -> None: + """Issue an "alter column" instruction using the current + batch migration context. + + Parameters are the same as that of :meth:`.Operations.alter_column`, + as well as the following option(s): + + :param insert_before: String name of an existing column which this + column should be placed before, when creating the new table. + + :param insert_after: String name of an existing column which this + column should be placed after, when creating the new table. If + both :paramref:`.BatchOperations.alter_column.insert_before` + and :paramref:`.BatchOperations.alter_column.insert_after` are + omitted, the column is inserted after the last existing column + in the table. + + .. seealso:: + + :meth:`.Operations.alter_column` + + + """ + alt = cls( + operations.impl.table_name, + column_name, + schema=operations.impl.schema, + existing_type=existing_type, + existing_server_default=existing_server_default, + existing_nullable=existing_nullable, + existing_comment=existing_comment, + modify_name=new_column_name, + modify_type=type_, + modify_server_default=server_default, + modify_nullable=nullable, + modify_comment=comment, + insert_before=insert_before, + insert_after=insert_after, + **kw, + ) + + return operations.invoke(alt) + + +@Operations.register_operation("add_column") +@BatchOperations.register_operation("add_column", "batch_add_column") +class AddColumnOp(AlterTableOp): + """Represent an add column operation.""" + + def __init__( + self, + table_name: str, + column: Column[Any], + *, + schema: Optional[str] = None, + if_not_exists: Optional[bool] = None, + inline_references: Optional[bool] = None, + inline_primary_key: Optional[bool] = None, + **kw: Any, + ) -> None: + super().__init__(table_name, schema=schema) + self.column = column + self.if_not_exists = if_not_exists + self.inline_references = inline_references + self.inline_primary_key = inline_primary_key + self.kw = kw + + def reverse(self) -> DropColumnOp: + op = DropColumnOp.from_column_and_tablename( + self.schema, self.table_name, self.column + ) + op.if_exists = self.if_not_exists + return op + + def to_diff_tuple( + self, + ) -> Tuple[str, Optional[str], str, Column[Any]]: + return ("add_column", self.schema, self.table_name, self.column) + + def to_column(self) -> Column[Any]: + return self.column + + @classmethod + def from_column(cls, col: Column[Any]) -> AddColumnOp: + return cls(col.table.name, col, schema=col.table.schema) + + @classmethod + def from_column_and_tablename( + cls, + schema: Optional[str], + tname: str, + col: Column[Any], + ) -> AddColumnOp: + return cls(tname, col, schema=schema) + + @classmethod + def add_column( + cls, + operations: Operations, + table_name: str, + column: Column[Any], + *, + schema: Optional[str] = None, + if_not_exists: Optional[bool] = None, + inline_references: Optional[bool] = None, + inline_primary_key: Optional[bool] = None, + ) -> None: + """Issue an "add column" instruction using the current + migration context. + + e.g.:: + + from alembic import op + from sqlalchemy import Column, String + + op.add_column("organization", Column("name", String())) + + The :meth:`.Operations.add_column` method typically corresponds + to the SQL command "ALTER TABLE... ADD COLUMN". Within the scope + of this command, the column's name, datatype, nullability, + and optional server-generated defaults may be indicated. Options + also exist for control of single-column primary key and foreign key + constraints to be generated. + + .. note:: + + Not all contraint types may be indicated with this directive. + NOT NULL, FOREIGN KEY, and CHECK are honored, PRIMARY KEY + is conditionally honored, UNIQUE + is currently not. + + As of 1.18.2, the following :class:`~sqlalchemy.schema.Column` + parameters are **ignored**: + + * :paramref:`~sqlalchemy.schema.Column.unique` - use the + :meth:`.Operations.create_unique_constraint` method + * :paramref:`~sqlalchemy.schema.Column.index` - use the + :meth:`.Operations.create_index` method + + **PRIMARY KEY support** + + The provided :class:`~sqlalchemy.schema.Column` object may include a + ``primary_key=True`` directive, indicating the column intends to be + part of a primary key constraint. However by default, the inline + "PRIMARY KEY" directive is not emitted, and it's assumed that a + separate :meth:`.Operations.create_primary_key` directive will be used + to create this constraint, which may potentially include other columns + as well as have an explicit name. To instead render an inline + "PRIMARY KEY" directive, the + :paramref:`.AddColumnOp.inline_primary_key` parameter may be indicated + at the same time as the ``primary_key`` parameter (both are needed):: + + from alembic import op + from sqlalchemy import Column, INTEGER + + op.add_column( + "organization", + Column("id", INTEGER, primary_key=True), + inline_primary_key=True + ) + + The ``primary_key=True`` parameter on + :class:`~sqlalchemy.schema.Column` also indicates behaviors such as + using the ``SERIAL`` datatype with the PostgreSQL database, which is + why two separate, independent parameters are provided to support all + combinations. + + .. versionadded:: 1.18.4 Added + :paramref:`.AddColumnOp.inline_primary_key` + to control use of the ``PRIMARY KEY`` inline directive. + + **FOREIGN KEY support** + + The provided :class:`~sqlalchemy.schema.Column` object may include a + :class:`~sqlalchemy.schema.ForeignKey` constraint directive, + referencing a remote table name. By default, Alembic will automatically + emit a second ALTER statement in order to add the single-column FOREIGN + KEY constraint separately:: + + from alembic import op + from sqlalchemy import Column, INTEGER, ForeignKey + + op.add_column( + "organization", + Column("account_id", INTEGER, ForeignKey("accounts.id")), + ) + + To render the FOREIGN KEY constraint inline within the ADD COLUMN + directive, use the ``inline_references`` parameter. This can improve + performance on large tables since the constraint is marked as valid + immediately for nullable columns:: + + from alembic import op + from sqlalchemy import Column, INTEGER, ForeignKey + + op.add_column( + "organization", + Column("account_id", INTEGER, ForeignKey("accounts.id")), + inline_references=True, + ) + + **Indicating server side defaults** + + The column argument passed to :meth:`.Operations.add_column` is a + :class:`~sqlalchemy.schema.Column` construct, used in the same way it's + used in SQLAlchemy. In particular, values or functions to be indicated + as producing the column's default value on the database side are + specified using the ``server_default`` parameter, and not ``default`` + which only specifies Python-side defaults:: + + from alembic import op + from sqlalchemy import Column, TIMESTAMP, func + + # specify "DEFAULT NOW" along with the column add + op.add_column( + "account", + Column("timestamp", TIMESTAMP, server_default=func.now()), + ) + + :param table_name: String name of the parent table. + :param column: a :class:`sqlalchemy.schema.Column` object + representing the new column. + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_not_exists: If True, adds ``IF NOT EXISTS`` operator + when creating the new column for compatible dialects + + .. versionadded:: 1.16.0 + + :param inline_references: If True, renders ``FOREIGN KEY`` constraints + inline within the ``ADD COLUMN`` directive using ``REFERENCES`` + syntax, rather than as a separate ``ALTER TABLE ADD CONSTRAINT`` + statement. This is supported by PostgreSQL, Oracle, MySQL 5.7+, and + MariaDB 10.5+. + + .. versionadded:: 1.18.2 + + :param inline_primary_key: If True, renders the ``PRIMARY KEY`` phrase + inline within the ``ADD COLUMN`` directive. When not present or + False, ``PRIMARY KEY`` is not emitted; it is assumed that the + migration script will include an additional + :meth:`.Operations.create_primary_key` directive to create a full + primary key constraint. + + .. versionadded:: 1.18.4 + + """ + + op = cls( + table_name, + column, + schema=schema, + if_not_exists=if_not_exists, + inline_references=inline_references, + inline_primary_key=inline_primary_key, + ) + return operations.invoke(op) + + @classmethod + def batch_add_column( + cls, + operations: BatchOperations, + column: Column[Any], + *, + insert_before: Optional[str] = None, + insert_after: Optional[str] = None, + if_not_exists: Optional[bool] = None, + inline_references: Optional[bool] = None, + inline_primary_key: Optional[bool] = None, + ) -> None: + """Issue an "add column" instruction using the current + batch migration context. + + .. seealso:: + + :meth:`.Operations.add_column` + + """ + + kw = {} + if insert_before: + kw["insert_before"] = insert_before + if insert_after: + kw["insert_after"] = insert_after + + op = cls( + operations.impl.table_name, + column, + schema=operations.impl.schema, + if_not_exists=if_not_exists, + inline_references=inline_references, + inline_primary_key=inline_primary_key, + **kw, + ) + return operations.invoke(op) + + +@Operations.register_operation("drop_column") +@BatchOperations.register_operation("drop_column", "batch_drop_column") +class DropColumnOp(AlterTableOp): + """Represent a drop column operation.""" + + def __init__( + self, + table_name: str, + column_name: str, + *, + schema: Optional[str] = None, + if_exists: Optional[bool] = None, + _reverse: Optional[AddColumnOp] = None, + **kw: Any, + ) -> None: + super().__init__(table_name, schema=schema) + self.column_name = column_name + self.kw = kw + self.if_exists = if_exists + self._reverse = _reverse + + def to_diff_tuple( + self, + ) -> Tuple[str, Optional[str], str, Column[Any]]: + return ( + "remove_column", + self.schema, + self.table_name, + self.to_column(), + ) + + def reverse(self) -> AddColumnOp: + if self._reverse is None: + raise ValueError( + "operation is not reversible; " + "original column is not present" + ) + + op = AddColumnOp.from_column_and_tablename( + self.schema, self.table_name, self._reverse.column + ) + op.if_not_exists = self.if_exists + return op + + @classmethod + def from_column_and_tablename( + cls, + schema: Optional[str], + tname: str, + col: Column[Any], + ) -> DropColumnOp: + return cls( + tname, + col.name, + schema=schema, + _reverse=AddColumnOp.from_column_and_tablename(schema, tname, col), + ) + + def to_column( + self, migration_context: Optional[MigrationContext] = None + ) -> Column[Any]: + if self._reverse is not None: + return self._reverse.column + schema_obj = schemaobj.SchemaObjects(migration_context) + return schema_obj.column(self.column_name, NULLTYPE) + + @classmethod + def drop_column( + cls, + operations: Operations, + table_name: str, + column_name: str, + *, + schema: Optional[str] = None, + **kw: Any, + ) -> None: + """Issue a "drop column" instruction using the current + migration context. + + e.g.:: + + drop_column("organization", "account_id") + + :param table_name: name of table + :param column_name: name of column + :param schema: Optional schema name to operate within. To control + quoting of the schema outside of the default behavior, use + the SQLAlchemy construct + :class:`~sqlalchemy.sql.elements.quoted_name`. + :param if_exists: If True, adds IF EXISTS operator when + dropping the new column for compatible dialects + + .. versionadded:: 1.16.0 + + :param mssql_drop_check: Optional boolean. When ``True``, on + Microsoft SQL Server only, first + drop the CHECK constraint on the column using a + SQL-script-compatible + block that selects into a @variable from sys.check_constraints, + then exec's a separate DROP CONSTRAINT for that constraint. + :param mssql_drop_default: Optional boolean. When ``True``, on + Microsoft SQL Server only, first + drop the DEFAULT constraint on the column using a + SQL-script-compatible + block that selects into a @variable from sys.default_constraints, + then exec's a separate DROP CONSTRAINT for that default. + :param mssql_drop_foreign_key: Optional boolean. When ``True``, on + Microsoft SQL Server only, first + drop a single FOREIGN KEY constraint on the column using a + SQL-script-compatible + block that selects into a @variable from + sys.foreign_keys/sys.foreign_key_columns, + then exec's a separate DROP CONSTRAINT for that default. Only + works if the column has exactly one FK constraint which refers to + it, at the moment. + """ + + op = cls(table_name, column_name, schema=schema, **kw) + return operations.invoke(op) + + @classmethod + def batch_drop_column( + cls, operations: BatchOperations, column_name: str, **kw: Any + ) -> None: + """Issue a "drop column" instruction using the current + batch migration context. + + .. seealso:: + + :meth:`.Operations.drop_column` + + """ + op = cls( + operations.impl.table_name, + column_name, + schema=operations.impl.schema, + **kw, + ) + return operations.invoke(op) + + +@Operations.register_operation("bulk_insert") +class BulkInsertOp(MigrateOperation): + """Represent a bulk insert operation.""" + + def __init__( + self, + table: Union[Table, TableClause], + rows: List[Dict[str, Any]], + *, + multiinsert: bool = True, + ) -> None: + self.table = table + self.rows = rows + self.multiinsert = multiinsert + + @classmethod + def bulk_insert( + cls, + operations: Operations, + table: Union[Table, TableClause], + rows: List[Dict[str, Any]], + *, + multiinsert: bool = True, + ) -> None: + """Issue a "bulk insert" operation using the current + migration context. + + This provides a means of representing an INSERT of multiple rows + which works equally well in the context of executing on a live + connection as well as that of generating a SQL script. In the + case of a SQL script, the values are rendered inline into the + statement. + + e.g.:: + + from alembic import op + from datetime import date + from sqlalchemy.sql import table, column + from sqlalchemy import String, Integer, Date + + # Create an ad-hoc table to use for the insert statement. + accounts_table = table( + "account", + column("id", Integer), + column("name", String), + column("create_date", Date), + ) + + op.bulk_insert( + accounts_table, + [ + { + "id": 1, + "name": "John Smith", + "create_date": date(2010, 10, 5), + }, + { + "id": 2, + "name": "Ed Williams", + "create_date": date(2007, 5, 27), + }, + { + "id": 3, + "name": "Wendy Jones", + "create_date": date(2008, 8, 15), + }, + ], + ) + + When using --sql mode, some datatypes may not render inline + automatically, such as dates and other special types. When this + issue is present, :meth:`.Operations.inline_literal` may be used:: + + op.bulk_insert( + accounts_table, + [ + { + "id": 1, + "name": "John Smith", + "create_date": op.inline_literal("2010-10-05"), + }, + { + "id": 2, + "name": "Ed Williams", + "create_date": op.inline_literal("2007-05-27"), + }, + { + "id": 3, + "name": "Wendy Jones", + "create_date": op.inline_literal("2008-08-15"), + }, + ], + multiinsert=False, + ) + + When using :meth:`.Operations.inline_literal` in conjunction with + :meth:`.Operations.bulk_insert`, in order for the statement to work + in "online" (e.g. non --sql) mode, the + :paramref:`~.Operations.bulk_insert.multiinsert` + flag should be set to ``False``, which will have the effect of + individual INSERT statements being emitted to the database, each + with a distinct VALUES clause, so that the "inline" values can + still be rendered, rather than attempting to pass the values + as bound parameters. + + :param table: a table object which represents the target of the INSERT. + + :param rows: a list of dictionaries indicating rows. + + :param multiinsert: when at its default of True and --sql mode is not + enabled, the INSERT statement will be executed using + "executemany()" style, where all elements in the list of + dictionaries are passed as bound parameters in a single + list. Setting this to False results in individual INSERT + statements being emitted per parameter set, and is needed + in those cases where non-literal values are present in the + parameter sets. + + """ + + op = cls(table, rows, multiinsert=multiinsert) + operations.invoke(op) + + +@Operations.register_operation("execute") +@BatchOperations.register_operation("execute", "batch_execute") +class ExecuteSQLOp(MigrateOperation): + """Represent an execute SQL operation.""" + + def __init__( + self, + sqltext: Union[Executable, str], + *, + execution_options: Optional[dict[str, Any]] = None, + ) -> None: + self.sqltext = sqltext + self.execution_options = execution_options + + @classmethod + def execute( + cls, + operations: Operations, + sqltext: Union[Executable, str], + *, + execution_options: Optional[dict[str, Any]] = None, + ) -> None: + r"""Execute the given SQL using the current migration context. + + The given SQL can be a plain string, e.g.:: + + op.execute("INSERT INTO table (foo) VALUES ('some value')") + + Or it can be any kind of Core SQL Expression construct, such as + below where we use an update construct:: + + from sqlalchemy.sql import table, column + from sqlalchemy import String + from alembic import op + + account = table("account", column("name", String)) + op.execute( + account.update() + .where(account.c.name == op.inline_literal("account 1")) + .values({"name": op.inline_literal("account 2")}) + ) + + Above, we made use of the SQLAlchemy + :func:`sqlalchemy.sql.expression.table` and + :func:`sqlalchemy.sql.expression.column` constructs to make a brief, + ad-hoc table construct just for our UPDATE statement. A full + :class:`~sqlalchemy.schema.Table` construct of course works perfectly + fine as well, though note it's a recommended practice to at least + ensure the definition of a table is self-contained within the migration + script, rather than imported from a module that may break compatibility + with older migrations. + + In a SQL script context, the statement is emitted directly to the + output stream. There is *no* return result, however, as this + function is oriented towards generating a change script + that can run in "offline" mode. Additionally, parameterized + statements are discouraged here, as they *will not work* in offline + mode. Above, we use :meth:`.inline_literal` where parameters are + to be used. + + For full interaction with a connected database where parameters can + also be used normally, use the "bind" available from the context:: + + from alembic import op + + connection = op.get_bind() + + connection.execute( + account.update() + .where(account.c.name == "account 1") + .values({"name": "account 2"}) + ) + + Additionally, when passing the statement as a plain string, it is first + coerced into a :func:`sqlalchemy.sql.expression.text` construct + before being passed along. In the less likely case that the + literal SQL string contains a colon, it must be escaped with a + backslash, as:: + + op.execute(r"INSERT INTO table (foo) VALUES ('\:colon_value')") + + + :param sqltext: Any legal SQLAlchemy expression, including: + + * a string + * a :func:`sqlalchemy.sql.expression.text` construct. + * a :func:`sqlalchemy.sql.expression.insert` construct. + * a :func:`sqlalchemy.sql.expression.update` construct. + * a :func:`sqlalchemy.sql.expression.delete` construct. + * Any "executable" described in SQLAlchemy Core documentation, + noting that no result set is returned. + + .. note:: when passing a plain string, the statement is coerced into + a :func:`sqlalchemy.sql.expression.text` construct. This construct + considers symbols with colons, e.g. ``:foo`` to be bound parameters. + To avoid this, ensure that colon symbols are escaped, e.g. + ``\:foo``. + + :param execution_options: Optional dictionary of + execution options, will be passed to + :meth:`sqlalchemy.engine.Connection.execution_options`. + """ + op = cls(sqltext, execution_options=execution_options) + return operations.invoke(op) + + @classmethod + def batch_execute( + cls, + operations: Operations, + sqltext: Union[Executable, str], + *, + execution_options: Optional[dict[str, Any]] = None, + ) -> None: + """Execute the given SQL using the current migration context. + + .. seealso:: + + :meth:`.Operations.execute` + + """ + return cls.execute( + operations, sqltext, execution_options=execution_options + ) + + def to_diff_tuple(self) -> Tuple[str, Union[Executable, str]]: + return ("execute", self.sqltext) + + +class OpContainer(MigrateOperation): + """Represent a sequence of operations operation.""" + + def __init__(self, ops: Sequence[MigrateOperation] = ()) -> None: + self.ops = list(ops) + + def is_empty(self) -> bool: + return not self.ops + + def as_diffs(self) -> Any: + return list(OpContainer._ops_as_diffs(self)) + + @classmethod + def _ops_as_diffs( + cls, migrations: OpContainer + ) -> Iterator[Tuple[Any, ...]]: + for op in migrations.ops: + if hasattr(op, "ops"): + yield from cls._ops_as_diffs(cast("OpContainer", op)) + else: + yield op.to_diff_tuple() + + +class ModifyTableOps(OpContainer): + """Contains a sequence of operations that all apply to a single Table.""" + + def __init__( + self, + table_name: str, + ops: Sequence[MigrateOperation], + *, + schema: Optional[str] = None, + ) -> None: + super().__init__(ops) + self.table_name = table_name + self.schema = schema + + def reverse(self) -> ModifyTableOps: + return ModifyTableOps( + self.table_name, + ops=list(reversed([op.reverse() for op in self.ops])), + schema=self.schema, + ) + + +class UpgradeOps(OpContainer): + """contains a sequence of operations that would apply to the + 'upgrade' stream of a script. + + .. seealso:: + + :ref:`customizing_revision` + + """ + + def __init__( + self, + ops: Sequence[MigrateOperation] = (), + upgrade_token: str = "upgrades", + ) -> None: + super().__init__(ops=ops) + self.upgrade_token = upgrade_token + + def reverse_into(self, downgrade_ops: DowngradeOps) -> DowngradeOps: + downgrade_ops.ops[:] = list( + reversed([op.reverse() for op in self.ops]) + ) + return downgrade_ops + + def reverse(self) -> DowngradeOps: + return self.reverse_into(DowngradeOps(ops=[])) + + +class DowngradeOps(OpContainer): + """contains a sequence of operations that would apply to the + 'downgrade' stream of a script. + + .. seealso:: + + :ref:`customizing_revision` + + """ + + def __init__( + self, + ops: Sequence[MigrateOperation] = (), + downgrade_token: str = "downgrades", + ) -> None: + super().__init__(ops=ops) + self.downgrade_token = downgrade_token + + def reverse(self) -> UpgradeOps: + return UpgradeOps( + ops=list(reversed([op.reverse() for op in self.ops])) + ) + + +class MigrationScript(MigrateOperation): + """represents a migration script. + + E.g. when autogenerate encounters this object, this corresponds to the + production of an actual script file. + + A normal :class:`.MigrationScript` object would contain a single + :class:`.UpgradeOps` and a single :class:`.DowngradeOps` directive. + These are accessible via the ``.upgrade_ops`` and ``.downgrade_ops`` + attributes. + + In the case of an autogenerate operation that runs multiple times, + such as the multiple database example in the "multidb" template, + the ``.upgrade_ops`` and ``.downgrade_ops`` attributes are disabled, + and instead these objects should be accessed via the ``.upgrade_ops_list`` + and ``.downgrade_ops_list`` list-based attributes. These latter + attributes are always available at the very least as single-element lists. + + .. seealso:: + + :ref:`customizing_revision` + + """ + + _needs_render: Optional[bool] + _upgrade_ops: List[UpgradeOps] + _downgrade_ops: List[DowngradeOps] + + def __init__( + self, + rev_id: Optional[str], + upgrade_ops: UpgradeOps, + downgrade_ops: DowngradeOps, + *, + message: Optional[str] = None, + imports: Set[str] = set(), + head: Optional[str] = None, + splice: Optional[bool] = None, + branch_label: Optional[_RevIdType] = None, + version_path: Union[str, os.PathLike[str], None] = None, + depends_on: Optional[_RevIdType] = None, + ) -> None: + self.rev_id = rev_id + self.message = message + self.imports = imports + self.head = head + self.splice = splice + self.branch_label = branch_label + self.version_path = ( + pathlib.Path(version_path).as_posix() if version_path else None + ) + self.depends_on = depends_on + self.upgrade_ops = upgrade_ops + self.downgrade_ops = downgrade_ops + + @property + def upgrade_ops(self) -> Optional[UpgradeOps]: + """An instance of :class:`.UpgradeOps`. + + .. seealso:: + + :attr:`.MigrationScript.upgrade_ops_list` + """ + if len(self._upgrade_ops) > 1: + raise ValueError( + "This MigrationScript instance has a multiple-entry " + "list for UpgradeOps; please use the " + "upgrade_ops_list attribute." + ) + elif not self._upgrade_ops: + return None + else: + return self._upgrade_ops[0] + + @upgrade_ops.setter + def upgrade_ops( + self, upgrade_ops: Union[UpgradeOps, List[UpgradeOps]] + ) -> None: + self._upgrade_ops = util.to_list(upgrade_ops) + for elem in self._upgrade_ops: + assert isinstance(elem, UpgradeOps) + + @property + def downgrade_ops(self) -> Optional[DowngradeOps]: + """An instance of :class:`.DowngradeOps`. + + .. seealso:: + + :attr:`.MigrationScript.downgrade_ops_list` + """ + if len(self._downgrade_ops) > 1: + raise ValueError( + "This MigrationScript instance has a multiple-entry " + "list for DowngradeOps; please use the " + "downgrade_ops_list attribute." + ) + elif not self._downgrade_ops: + return None + else: + return self._downgrade_ops[0] + + @downgrade_ops.setter + def downgrade_ops( + self, downgrade_ops: Union[DowngradeOps, List[DowngradeOps]] + ) -> None: + self._downgrade_ops = util.to_list(downgrade_ops) + for elem in self._downgrade_ops: + assert isinstance(elem, DowngradeOps) + + @property + def upgrade_ops_list(self) -> List[UpgradeOps]: + """A list of :class:`.UpgradeOps` instances. + + This is used in place of the :attr:`.MigrationScript.upgrade_ops` + attribute when dealing with a revision operation that does + multiple autogenerate passes. + + """ + return self._upgrade_ops + + @property + def downgrade_ops_list(self) -> List[DowngradeOps]: + """A list of :class:`.DowngradeOps` instances. + + This is used in place of the :attr:`.MigrationScript.downgrade_ops` + attribute when dealing with a revision operation that does + multiple autogenerate passes. + + """ + return self._downgrade_ops diff --git a/venv/lib/python3.12/site-packages/alembic/operations/schemaobj.py b/venv/lib/python3.12/site-packages/alembic/operations/schemaobj.py new file mode 100644 index 00000000..59c1002f --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/operations/schemaobj.py @@ -0,0 +1,290 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +from typing import Any +from typing import Dict +from typing import List +from typing import Optional +from typing import Sequence +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import schema as sa_schema +from sqlalchemy.sql.schema import Column +from sqlalchemy.sql.schema import Constraint +from sqlalchemy.sql.schema import Index +from sqlalchemy.types import Integer +from sqlalchemy.types import NULLTYPE + +from .. import util +from ..util import sqla_compat + +if TYPE_CHECKING: + from sqlalchemy.sql.elements import ColumnElement + from sqlalchemy.sql.elements import TextClause + from sqlalchemy.sql.schema import CheckConstraint + from sqlalchemy.sql.schema import ForeignKey + from sqlalchemy.sql.schema import ForeignKeyConstraint + from sqlalchemy.sql.schema import MetaData + from sqlalchemy.sql.schema import PrimaryKeyConstraint + from sqlalchemy.sql.schema import Table + from sqlalchemy.sql.schema import UniqueConstraint + from sqlalchemy.sql.type_api import TypeEngine + + from ..runtime.migration import MigrationContext + + +class SchemaObjects: + def __init__( + self, migration_context: Optional[MigrationContext] = None + ) -> None: + self.migration_context = migration_context + + def primary_key_constraint( + self, + name: Optional[sqla_compat._ConstraintNameDefined], + table_name: str, + cols: Sequence[str], + schema: Optional[str] = None, + **dialect_kw, + ) -> PrimaryKeyConstraint: + m = self.metadata() + columns = [sa_schema.Column(n, NULLTYPE) for n in cols] + t = sa_schema.Table(table_name, m, *columns, schema=schema) + # SQLAlchemy primary key constraint name arg is wrongly typed on + # the SQLAlchemy side through 2.0.5 at least + p = sa_schema.PrimaryKeyConstraint( + *[t.c[n] for n in cols], name=name, **dialect_kw # type: ignore + ) + return p + + def foreign_key_constraint( + self, + name: Optional[sqla_compat._ConstraintNameDefined], + source: str, + referent: str, + local_cols: List[str], + remote_cols: List[str], + onupdate: Optional[str] = None, + ondelete: Optional[str] = None, + deferrable: Optional[bool] = None, + source_schema: Optional[str] = None, + referent_schema: Optional[str] = None, + initially: Optional[str] = None, + match: Optional[str] = None, + **dialect_kw, + ) -> ForeignKeyConstraint: + m = self.metadata() + if source == referent and source_schema == referent_schema: + t1_cols = local_cols + remote_cols + else: + t1_cols = local_cols + sa_schema.Table( + referent, + m, + *[sa_schema.Column(n, NULLTYPE) for n in remote_cols], + schema=referent_schema, + ) + + t1 = sa_schema.Table( + source, + m, + *[ + sa_schema.Column(n, NULLTYPE) + for n in util.unique_list(t1_cols) + ], + schema=source_schema, + ) + + tname = ( + "%s.%s" % (referent_schema, referent) + if referent_schema + else referent + ) + + dialect_kw["match"] = match + + f = sa_schema.ForeignKeyConstraint( + local_cols, + ["%s.%s" % (tname, n) for n in remote_cols], + name=name, + onupdate=onupdate, + ondelete=ondelete, + deferrable=deferrable, + initially=initially, + **dialect_kw, + ) + t1.append_constraint(f) + + return f + + def unique_constraint( + self, + name: Optional[sqla_compat._ConstraintNameDefined], + source: str, + local_cols: Sequence[str], + schema: Optional[str] = None, + **kw, + ) -> UniqueConstraint: + t = sa_schema.Table( + source, + self.metadata(), + *[sa_schema.Column(n, NULLTYPE) for n in local_cols], + schema=schema, + ) + kw["name"] = name + uq = sa_schema.UniqueConstraint(*[t.c[n] for n in local_cols], **kw) + # TODO: need event tests to ensure the event + # is fired off here + t.append_constraint(uq) + return uq + + def check_constraint( + self, + name: Optional[sqla_compat._ConstraintNameDefined], + source: str, + condition: Union[str, TextClause, ColumnElement[Any]], + schema: Optional[str] = None, + **kw, + ) -> Union[CheckConstraint]: + t = sa_schema.Table( + source, + self.metadata(), + sa_schema.Column("x", Integer), + schema=schema, + ) + ck = sa_schema.CheckConstraint(condition, name=name, **kw) + t.append_constraint(ck) + return ck + + def generic_constraint( + self, + name: Optional[sqla_compat._ConstraintNameDefined], + table_name: str, + type_: Optional[str], + schema: Optional[str] = None, + **kw, + ) -> Any: + t = self.table(table_name, schema=schema) + types: Dict[Optional[str], Any] = { + "foreignkey": lambda name: sa_schema.ForeignKeyConstraint( + [], [], name=name + ), + "primary": sa_schema.PrimaryKeyConstraint, + "unique": sa_schema.UniqueConstraint, + "check": lambda name: sa_schema.CheckConstraint("", name=name), + None: sa_schema.Constraint, + } + try: + const = types[type_] + except KeyError as ke: + raise TypeError( + "'type' can be one of %s" + % ", ".join(sorted(repr(x) for x in types)) + ) from ke + else: + const = const(name=name) + t.append_constraint(const) + return const + + def metadata(self) -> MetaData: + kw = {} + if ( + self.migration_context is not None + and "target_metadata" in self.migration_context.opts + ): + mt = self.migration_context.opts["target_metadata"] + if hasattr(mt, "naming_convention"): + kw["naming_convention"] = mt.naming_convention + return sa_schema.MetaData(**kw) + + def table(self, name: str, *columns, **kw) -> Table: + m = self.metadata() + + cols = [ + sqla_compat._copy(c) if c.table is not None else c + for c in columns + if isinstance(c, Column) + ] + # these flags have already added their UniqueConstraint / + # Index objects to the table, so flip them off here. + # SQLAlchemy tometadata() avoids this instead by preserving the + # flags and skipping the constraints that have _type_bound on them, + # but for a migration we'd rather list out the constraints + # explicitly. + _constraints_included = kw.pop("_constraints_included", False) + if _constraints_included: + for c in cols: + c.unique = c.index = False + + t = sa_schema.Table(name, m, *cols, **kw) + + constraints = [ + ( + sqla_compat._copy(elem, target_table=t) + if getattr(elem, "parent", None) is not t + and getattr(elem, "parent", None) is not None + else elem + ) + for elem in columns + if isinstance(elem, (Constraint, Index)) + ] + + for const in constraints: + t.append_constraint(const) + + for f in t.foreign_keys: + self._ensure_table_for_fk(m, f) + return t + + def column(self, name: str, type_: TypeEngine, **kw) -> Column: + return sa_schema.Column(name, type_, **kw) + + def index( + self, + name: Optional[str], + tablename: Optional[str], + columns: Sequence[Union[str, TextClause, ColumnElement[Any]]], + schema: Optional[str] = None, + **kw, + ) -> Index: + t = sa_schema.Table( + tablename or "no_table", + self.metadata(), + schema=schema, + ) + kw["_table"] = t + idx = sa_schema.Index( + name, + *[util.sqla_compat._textual_index_column(t, n) for n in columns], + **kw, + ) + return idx + + def _parse_table_key(self, table_key: str) -> Tuple[Optional[str], str]: + if "." in table_key: + tokens = table_key.split(".") + sname: Optional[str] = ".".join(tokens[0:-1]) + tname = tokens[-1] + else: + tname = table_key + sname = None + return (sname, tname) + + def _ensure_table_for_fk(self, metadata: MetaData, fk: ForeignKey) -> None: + """create a placeholder Table object for the referent of a + ForeignKey. + + """ + if isinstance(fk._colspec, str): + table_key, cname = fk._colspec.rsplit(".", 1) + sname, tname = self._parse_table_key(table_key) + if table_key not in metadata.tables: + rel_t = sa_schema.Table(tname, metadata, schema=sname) + else: + rel_t = metadata.tables[table_key] + if cname not in rel_t.c: + rel_t.append_column(sa_schema.Column(cname, NULLTYPE)) diff --git a/venv/lib/python3.12/site-packages/alembic/operations/toimpl.py b/venv/lib/python3.12/site-packages/alembic/operations/toimpl.py new file mode 100644 index 00000000..85b9d8a3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/operations/toimpl.py @@ -0,0 +1,261 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from typing import TYPE_CHECKING + +from sqlalchemy import schema as sa_schema + +from . import ops +from .base import Operations +from ..util.sqla_compat import _copy +from ..util.sqla_compat import sqla_2 + +if TYPE_CHECKING: + from sqlalchemy.sql.schema import Table + + +@Operations.implementation_for(ops.AlterColumnOp) +def alter_column( + operations: "Operations", operation: "ops.AlterColumnOp" +) -> None: + compiler = operations.impl.dialect.statement_compiler( + operations.impl.dialect, None + ) + + existing_type = operation.existing_type + existing_nullable = operation.existing_nullable + existing_server_default = operation.existing_server_default + type_ = operation.modify_type + column_name = operation.column_name + table_name = operation.table_name + schema = operation.schema + server_default = operation.modify_server_default + new_column_name = operation.modify_name + nullable = operation.modify_nullable + comment = operation.modify_comment + existing_comment = operation.existing_comment + + def _count_constraint(constraint): + return not isinstance(constraint, sa_schema.PrimaryKeyConstraint) and ( + not constraint._create_rule or constraint._create_rule(compiler) + ) + + if existing_type and type_: + t = operations.schema_obj.table( + table_name, + sa_schema.Column(column_name, existing_type), + schema=schema, + ) + for constraint in t.constraints: + if _count_constraint(constraint): + operations.impl.drop_constraint(constraint) + + # some weird pyright quirk here, these have Literal[False] + # in their types, not sure why pyright thinks they could be True + assert existing_server_default is not True # type: ignore[comparison-overlap] # noqa: E501 + assert comment is not True # type: ignore[comparison-overlap] + + operations.impl.alter_column( + table_name, + column_name, + nullable=nullable, + server_default=server_default, + name=new_column_name, + type_=type_, + schema=schema, + existing_type=existing_type, + existing_server_default=existing_server_default, + existing_nullable=existing_nullable, + comment=comment, + existing_comment=existing_comment, + **operation.kw, + ) + + if type_: + t = operations.schema_obj.table( + table_name, + operations.schema_obj.column(column_name, type_), + schema=schema, + ) + for constraint in t.constraints: + if _count_constraint(constraint): + operations.impl.add_constraint(constraint) + + +@Operations.implementation_for(ops.DropTableOp) +def drop_table(operations: "Operations", operation: "ops.DropTableOp") -> None: + kw = {} + if operation.if_exists is not None: + kw["if_exists"] = operation.if_exists + operations.impl.drop_table( + operation.to_table(operations.migration_context), **kw + ) + + +@Operations.implementation_for(ops.DropColumnOp) +def drop_column( + operations: "Operations", operation: "ops.DropColumnOp" +) -> None: + column = operation.to_column(operations.migration_context) + operations.impl.drop_column( + operation.table_name, + column, + schema=operation.schema, + if_exists=operation.if_exists, + **operation.kw, + ) + + +@Operations.implementation_for(ops.CreateIndexOp) +def create_index( + operations: "Operations", operation: "ops.CreateIndexOp" +) -> None: + idx = operation.to_index(operations.migration_context) + kw = {} + if operation.if_not_exists is not None: + kw["if_not_exists"] = operation.if_not_exists + operations.impl.create_index(idx, **kw) + + +@Operations.implementation_for(ops.DropIndexOp) +def drop_index(operations: "Operations", operation: "ops.DropIndexOp") -> None: + kw = {} + if operation.if_exists is not None: + kw["if_exists"] = operation.if_exists + + operations.impl.drop_index( + operation.to_index(operations.migration_context), + **kw, + ) + + +@Operations.implementation_for(ops.CreateTableOp) +def create_table( + operations: "Operations", operation: "ops.CreateTableOp" +) -> "Table": + kw = {} + if operation.if_not_exists is not None: + kw["if_not_exists"] = operation.if_not_exists + table = operation.to_table(operations.migration_context) + operations.impl.create_table(table, **kw) + return table + + +@Operations.implementation_for(ops.RenameTableOp) +def rename_table( + operations: "Operations", operation: "ops.RenameTableOp" +) -> None: + operations.impl.rename_table( + operation.table_name, operation.new_table_name, schema=operation.schema + ) + + +@Operations.implementation_for(ops.CreateTableCommentOp) +def create_table_comment( + operations: "Operations", operation: "ops.CreateTableCommentOp" +) -> None: + table = operation.to_table(operations.migration_context) + operations.impl.create_table_comment(table) + + +@Operations.implementation_for(ops.DropTableCommentOp) +def drop_table_comment( + operations: "Operations", operation: "ops.DropTableCommentOp" +) -> None: + table = operation.to_table(operations.migration_context) + operations.impl.drop_table_comment(table) + + +@Operations.implementation_for(ops.AddColumnOp) +def add_column(operations: "Operations", operation: "ops.AddColumnOp") -> None: + table_name = operation.table_name + column = operation.column + schema = operation.schema + kw = operation.kw + inline_references = operation.inline_references + inline_primary_key = operation.inline_primary_key + + if column.table is not None: + column = _copy(column) + + t = operations.schema_obj.table(table_name, column, schema=schema) + operations.impl.add_column( + table_name, + column, + schema=schema, + if_not_exists=operation.if_not_exists, + inline_references=inline_references, + inline_primary_key=inline_primary_key, + **kw, + ) + + for constraint in t.constraints: + if not isinstance(constraint, sa_schema.PrimaryKeyConstraint): + # Skip ForeignKeyConstraint if it was rendered inline + # This only happens when inline_references=True AND there's exactly + # one FK AND the constraint is single-column + if ( + inline_references + and isinstance(constraint, sa_schema.ForeignKeyConstraint) + and len(column.foreign_keys) == 1 + and len(constraint.columns) == 1 + ): + continue + operations.impl.add_constraint(constraint) + for index in t.indexes: + operations.impl.create_index(index) + + with_comment = ( + operations.impl.dialect.supports_comments + and not operations.impl.dialect.inline_comments + ) + comment = column.comment + if comment and with_comment: + operations.impl.create_column_comment(column) + + +@Operations.implementation_for(ops.AddConstraintOp) +def create_constraint( + operations: "Operations", operation: "ops.AddConstraintOp" +) -> None: + operations.impl.add_constraint( + operation.to_constraint(operations.migration_context) + ) + + +@Operations.implementation_for(ops.DropConstraintOp) +def drop_constraint( + operations: "Operations", operation: "ops.DropConstraintOp" +) -> None: + kw = {} + if operation.if_exists is not None: + if not sqla_2: + raise NotImplementedError("SQLAlchemy 2.0 required") + kw["if_exists"] = operation.if_exists + operations.impl.drop_constraint( + operations.schema_obj.generic_constraint( + operation.constraint_name, + operation.table_name, + operation.constraint_type, + schema=operation.schema, + ), + **kw, + ) + + +@Operations.implementation_for(ops.BulkInsertOp) +def bulk_insert( + operations: "Operations", operation: "ops.BulkInsertOp" +) -> None: + operations.impl.bulk_insert( # type: ignore[union-attr] + operation.table, operation.rows, multiinsert=operation.multiinsert + ) + + +@Operations.implementation_for(ops.ExecuteSQLOp) +def execute_sql( + operations: "Operations", operation: "ops.ExecuteSQLOp" +) -> None: + operations.migration_context.impl.execute( + operation.sqltext, execution_options=operation.execution_options + ) diff --git a/venv/lib/python3.12/site-packages/alembic/py.typed b/venv/lib/python3.12/site-packages/alembic/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/alembic/runtime/__init__.py b/venv/lib/python3.12/site-packages/alembic/runtime/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/alembic/runtime/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/runtime/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd0d060e2a0067aa5d21d79d20e803d490eeeaf5 GIT binary patch literal 191 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

cL$HG4j%d8;IR*{ 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/runtime/__pycache__/migration.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/runtime/__pycache__/migration.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce7f117e2a8eb04c35b8ccc803aff7c12f0f2dd7 GIT binary patch literal 57787 zcmeIbd3apcbtm|00}7}DssI%Bt+1o8a$mq*1UHEUZHsbb$ZW8PDv*G%=~aOw%Ah5? zm2SXNBGOKWme>UFzuO;c<(;w>N5CO~j zO{*kblR{F+GAT`2hAkEfTPLkkwqYBGZ3x?k?HsmGI))wiZlBB>&cm-`GJncB?Bs8G z2p0?&a5x{~!r?*=I}vsbyEt5cuzT3e;X;IqhKo4tLb!Oin8R*_J;NRj7a{B&_Hwuw z;gaDJ4to$T9WLdt7vZwuG7gs@>>KuRxOB37s$#f;!(|9p4p(y6H(51RJzUMA2zLy3aJX@@b1E<#;IJRzuHh~YH%)d=^$hoLxOuX7s&BXt z;g(SAWdGEL;SC5^h1w=JPHh_A#NqbI%~M;3w{W-v;jP14Iovt9ZEE}Qb`A#+-Z8v` z!(9mP9NuY>tWwx95mhIF{u|!)jz#=6yxS@rkwV?CN}-;2FgNHK-otUdi0jkh_HtZ5 z;x=e;`#5eR;x=h<`#Eki;pd5UAVBX)-&_Taodu=59$Bw$Io zg45G8(O`6ZW;%jDi$`asqv1=@so-?*Tv(itQGI!lFjY>WL(}KRr>SMO zIhl$rJux%-qKJGpDhH$CbJT_6gQBk|L|=bEV?%jDAXKfN&JyNI385$v_1E^7nThX zlk!u+2!X(<#vtrc!zY5Xqcj23N0~5|^}~p{CWWP8OGp~FhMXbGtM*}A*d8hfSzook zqofDQDh%21B@e%L{N{&TAqRe)A$KSbzXkL8fudB&$YMrMPf z;YdFXQ18X@==oj>DUlb#(--KII%48J`}Hj*P@=vU=60e%nDUqAlVTX{Bk?qW!+JVI6--qY5gZ zutZ_*b{_)Qqy=e$Vl$ozBcPOT&q{JXf&nY{Jmnt2R90u#ZHv4S-y&4FU*3dw$~Q6# z=#Pg(#&ofElh&K0+eL->@OnjB^^_$&Z3$0X(z7Yy*%WtflDFVPTA#F`9K)AfeY$K} zu)Jb@TKXz}CzKh7Kx7_Mv0el$_7W_rvL}SddFWF>!nx#pz3@qp&?bBy zN~Fo5Oyt{=yq)SW%-MpG5um-WjAd4v-7f9pGn6eKFAS2WY|$m4Z=5hhjqxIjA&_fA zex*Oh;?t%XGAj?TlXbzS?yR3~ie^qywEq&J5PeJnb}>mE(fQf%NXl^`I5`(i6<}eH zhG{xSs84=8V!#gKFSE(37CeOSk)3z}E-p{f)tYd%#$6k6CqF1{M>DBx<;q!cKV*KQhe*nI@SC(as}6RHn#PNN9IDI29flNfnNaOwEMm zCMoP58F^_gI4Qo!n~^@{1QMGK%h7p82)xW?n&0vPdJ*8{{q!Q#C4fn7k?F;gkTC13 z(z?~+*u0h}-QVYaGh6<4-rgEDqntc{ay(Q5KajivP7onTD2iP|BEk+FoiI zwiQUB(gG>-zfc+WQpd18>`4C-@}>Ws`B$hMd#^*qn!|bF{7?n$!di?I^;L>oAb0an zM?t7Ml#l(nFjNzA;@1_b4Aq7T@Wmaf3l$=!C{!PE;kP){5OU+!gZLu+dehq;Y77-4 zSBcuDkU!)>s?u;-s43(%w84j%=1_?trhMKSXpxTqUcs+o4ty>=D~BUk_5SGjus;+8 zF$BWJ9|27o_D=;vVSn&KaGXg^e{{wl^lRI4AuWsuQ$u}){KvrPWH1sLIMbIoZ_oJ0 zBV7I%D66PHJRRb)rv0GJFp)U3RyF7hg@o(z&&dAZBoNXxNRSKR$$8WtiOS>C=lnBs z(b>7EXhT#E2dDh!kOC$8XM=Kdd~|LyDC^w`2|}@l)1whn{@}F#fFL;hn75HxA{sA* zm5%(}d6YCh9T`XW2`p#MfO-KLa^{TE;U138-CwQ;eb!-jVLV8A_`|?dIC>sn$|g1Y z&V{2Yp>@Vj$!0>L(HGB;kDezm>O}^lXqZAZ^(oZZnKJ_eVlZfQTKwEsqQ%Ci0e0$x z(gSV?r4i6jKrucwJ0nN^N@03I+MuhA^Y2j;8JpZ05Lpgm+~y*n>S&l4iZf#baT7R3 z=FqKR#6LD8bCZbdACJ!aW#j}CLDL5PycnbjI^!QbA0B;?VLv)2%QR~*VDLbl3V=`Z zCv_$%gQNgEb50V(K+@02;WM0B0Glhpbi(AH4qx==9%28?*$K>xVr@%I$ z))_yB(T`@++)*1UE1Uu56~%KlwWR{vZ%z<=`a_i4lIS0+McOSskAtRdEH5<@aIucjYm(1C~`22h_q;%}NW#_yfYvon$C zIiR1HCI|Zar{+~~GzYYL2pJ%sD1e5HP=h+ns3FQ!MVidE>I2+K`3z<1kj$LM`WNQn z)QV1F9D`?PE?_hT6XYMoYz|IFX8dQv7`!m!Gi-sOxlu8)DsIssO53C0^Zm3QX*p=K z>!kKg%tfG{XmENooW7QyFvK+3rf1}-;G~AxFytyPt3lMb-un!VQbt^FiavZZvw)NS zfxt;Z3LT;|vwe)FfgMO1(AhjS&rEf5kH0xGGZhxm_=|)Y#BJ|6CC`NwWHB_V$nJOx zcj9d$uJlKU1?6Zra-|D4{6+LCKg_XejM{>2f=JSgCuDg?NspWgX&^~C(pp0{Ribm* zkt2;=E$?z(juZ~P6x6BpEl29SP`+9xQWH_e53yh)T7MQC@}6jk`YlQfJ3k?3HGV-w zmn#v5p9QPhQbYMJH39NesA2tAu!kXnvRjRvZUW^kjc7Tewd$9NIyI#K>a`ln$kF!# zwS`FQQOj6x;4iQCm-(nd{9)*o2APm+!FEBCw?*5Ykl(G=e901lG&b68b0Z|=vJX;#)k9(t~gN{(`iLdAx>m1-+`NpK zqE7R3IX_4Fr6CO}oTEm|;mnbvOocqk*Je_bm?nNENI;PBOzg>sz@H2Hx<*UQ<}zaa zCiwv4i7u0P+dInIL%SN)??_8bOvW=oJWa;4kiU?t$3A_;zEiGpu&AX;Eh0+%7(Ww4 zmS;SniX5#VJ}u*!cse5jf2t#DAw1#F{w|k4q5evB#IQ~t-_oiaE$uO>H9u6Xw`^Vt z)foQ@)DA(O%&bK!FBlmCfp9ogVvx5%@C-3=s@f1Wf<1IZ+ip`{NGuiFaD<4WdBJ}Y z08-Ym>6Bx3PCge-c?2H;iUqPt&|s+|CF1Pd*jQLjxmofohet?4a4J=y{uv3&D19Up z9t+M*MpH#`43fH9MrPPLg zrHb`w5g8eqmOo1sf1Y0Tl-(AYk6^ZO3*_f1`g0UrIs&>*Mw6g3Rhm8rIL@ifBHQH5xs-i;dTb^I zNgXrWkY>@E4-~KpQqPZdIjml-K8$+4U81vPokdr5%L%ko4-1Riau59KSt3Pm+JZNZs;H|n|m+-dVvL`$DB|7)LU%1@)42ozhUL1V=BuemA zUE6ncU%ad@?(V~TrDx;f;C**swV{&|eCVxBdIu8Tf%gWMy@%IqQc=l0cVNwp-#3mf z9ldhuwIN933X1MIYf)BZJ#}Way!zVnSD(Lr?&jQbc`srLS6y{C?U3+2XzE(=`mQ;z zIyo6)xW?grG--2QgeTU9IG#&4CqUGr8A z+D+f?;hR%x-b~$XSVfx7w>od_eQ(Qh^L~nITYbURsO!_t@e4FlUU-s=(Z_D7!2JdU+3}K=o2TCX!dqWRw(Ur??MSvANVFZe=X?s1)Him0ZPx?0hsGve z-gD30`@rdN5VK8$6F|F81%3kuf?rHa5`xeyZ4X~H(@Lpwe5 z7z^MHz+>dUVjY)2nAjtUCklE_F%yhL%h0k zvdYES0ww8X`}E|3Rqlyu)b)f$ck89;H1%btRa$UPXtc7P<{c#%vX4s(dEc`9q6KB| zU9idhk1sm}sk*VHKV}U@3-*Xh=S)CtDsV%O?Sh+#mZ_}-FJ}Rr!Oc7MK*!Ll>JBW@LV-SLD)q|xJBDCL`0-+ zhPaA^K+QALlaP`$`yp2k_xb%SbX0_~LgvRsP%lZ)ph_7+VeErb$jH(qB1`-bt|{9H zm5YQzC`H+oUO*2|%~xcns+`4-Xdz?O0HyUXyMsvF2QB*Xo{^bI zN;#=fBN&&Ib1L{^m_sh2$y9=@;Ls%kDxlBGl0ZSqHaZy*#1%2=DYvmPg7PAIOOL1V zWI8OBk9tNRFgBsPQU!pjkVD66vZycZ+n=K3$e-W^@@uc;_Ac&TDJx$bTya&#U5zWX z^^3^T{Co)jCk zw(+8~w8^%RFmM>5XJER)s$^pdB%nv#{$S%Enb~fbSu@BTNNcir%e6VmAfh?rQR}rx z(;GoHkV3mKBMJUgtS(HyO%!FP*GmNnwl{JSwGtnd@o2T0e4EfjW_l*I*2@t0 znU#0h29b`ond|vN*6(QBJ_~_#v8Cx)7{r>U^_U4WwYJ%(ts#&$(1u!m*?P^wLJgBK zjGQv>g8~!#SG)`M$3xyczghoZHuT>jy=?ojVW5$hI!u2m^HmcL>gB3%v`F(HQRP1K zUOsl_Xrj;TQjK$U-ZF1#A(7L6A05+GF!9*Yxd;L0&vJAwN*AR|wl7E*E&tMX(Gsu+ zkApioM5aHamue2qMQ0%Rfc;Xl|Li2JCVQBKRp~}WDI1xaWo_|+=T4nGc=E)FW2ZDa zVmvZ4$;vMOWEgsTXi2qCOuCc49V&ScJ7hcws*pDGanwHz+fGuTs%4W>Yi1M%uGohu zy<(rBl?K5U83{my6Ae#+Fj91EppBt^@tXt1OPyQF?skIp|K=lUTuqi7F`8}Kc$s~zEHcTh+-o!g5HOq55xsSkx8sbbbgYF zRjtWAqy-0&GOrbbc8o_^cBgj?N#;;0$g1>SWVbj1sul(jT4^vPm^*i#8>Y1!8co>n zPO8vGvvre3K%1n7#(_hZ*dl=ji`fyhI7l24X#=V4WV6iLO;T0rmLi(b#96&_G$?Gx z0X3+iq1QExMw@~&G-@z=lE={%6G2LCG-zVcw!o9bOfd*s6sId z(O?oHrdXyWMf@(>K}JXDSx~8&VWyXq31h>V4Q)X^zHM1x-hO9Qx`gbgxJ zOxT+$>urj+Qi6|0B`AAjp^kKhQib{oB+m$qA{gNb9dJW=1U-$ugp4X1IRfI6)iA8R zNVy0Vkt&8Ps<+WMwk;(^N2-jlX-4U(5>3)JqDW9ujFL$#`o*W$tR)4z|HNIgiVAmqeHST%UcUNrymC|A zy9w`=vif*gV8z#v^tB~?ZTEZ~q(t#t^@s{rS~rp`f78d371D}4Y2cV8?mc?N{!5;l zt#1e33fybnMje`8nooMV?s~dzMc@6>x4%S16kIJxdRr6T*8AS}4@$bT>TT_O`{Y|E zldU@wtvi=ncU`gH_clS>Q`+;RwQABO{5R3ozm51Y2fsRKf4bZ9d-?Vw)za^|4i@8& z|GoO4=jl${A9ULAN1D5j4H@viWqk~@?w>-EvSjM{A{Ry}B|j>+_$;MWg!bw;`ZeS2 zWbc9nsZEp}FLp}OW$QGQ03pj9LJQW&Pgqyvr3FibJz7wsiQFY;UrdxNA?uulBpf;O zm`G6spRB7tCQ6Oa5n;5m^Nb#E>`ZS$R}56k3$VEU;jAs-tz; z4keLhh0Z0IwBYZf?cgi_GuZ#92=RiRf%cksu{1i7G$Y!kqi@<-3}Xi4L6kI=X<|}L z>_Q0E6*8<~!?cUZ`W8n-59|!Et2G<8*=A$=?qN^WIF- zib#G?H%1c!q1Q1ZcudmNQwDb0Y7qbk8Y^19qniG7sRqrwtRpV!D`3U5F*-WH7+6R40E110#~N z8R;E>CQX%B7^wII{^sLip){whlnjbm%L;x7nj)+s{D6bdx}b9`v?78aa}DwnW631i zIL4fmDy2~Q0RI44xx$nAj|EXyAy&u<5IvzN)-n2EepZ^u^)t#0s2&2uv~m77~NznXxgU);)8k zw--t#@Pnk_4G;%TW1y8}0D{Kpu-OpQSz<`iOQ6;P=Y?UHHj368C!9VRF3!wNhKP%d zhJzsk*DB_KqW@KWE}EY@bmZ6&M0{beds@2`wx4p)5TyCr80P6Id=uViU@n9+1jEn& z8FuhhagQbz5)wHGq0l~H$a&PNRmlqxeWUu6#V%N2P4>dZZ7g#kyay>AIXR$Ww#rYJ zC@5_qdEfMmqA@);2W38RA#xBmKL!U6aN`lbqHZ^I3rut*jA4q*WMcZ9t2&eXKcllj zr-nw2BmqhKVP|Do|1tvq%px>epb(5{l<<>0bbr}G+K(K0HRBY$V3j(6M^X&k?EO>sp52_Liv}FnHj+|SgQ&nSds;e zSo4!1My5uQ2E2$7eBJ`(Zqn6x*VXx;tl=Ab;%rrN841 zTNY1#;DObdlB1;jTJ_cHWJ%B6lAiCCHO1XcSN1CW--F8LZ(NRhTi0q}hn8&GlxW)Y zUTeH*@0H>Y%9y6*6`0Vy@U<5{EUI0%+KM{i!C73lMxjTAlF$E*y=V$C zghu1M2mbD)e<0?yhX@FpZy$R@1i;c(P_#=WBH1 z@sNeM64^3uHT`OPaS&gdpJ40-7Zo#p$7ohX24Eoz+0(Pp)4u^C_#Auk3Y4e#vZ=0J zuy;_*m>6klu73))($J=YF*PSZ)ld{$4%;Eqc0njr-OmkZjd%?5^gnDEi zGX-nvamecQ>`JF;yM|?nVv<2SjT{_LGYz5*DJPC*k-OsEbsk$|1X5bD7op3j0=C{T zBhlJKvPFOnwJ0z;H$xIpKm-z2+FwC&!TN{xNaQE+BD7sZ)f17cl_Ef3pf40U$}kR0 zc%hoyx(vv4na~%c6W6WL7+Ot4VF*zpL53-9%nCM~XIHB;XP97sE`uoy^o7il4Jr*b zb|cF&hAdA_jG;nD^!UfgeRJN>Zke{SA-NUr*k?a4d@z+s*U=W~U=iVzkkn$_NO(w4 zSNGdp(3yqF&6uS4Swqtq2slzDSN%-NcoHfs4FYh zmW7YalC2O*CbN5BWWQ8Wp7g?qw|&{$x#)P{Dqfs>ZQy#z*Y-h__MpgnrTn$w>rG$# zM-NIXlBF$)(iS+Zmu!T+a&q&b#O6bfY?hM4mUWx0taweb6&1rS)mMGJBvxTgeEz>`@VEM{(NK1G!+@)j4 zLFoZRX`pxH2ljpSt;~nQ`k?>+8LC>9Xar)zBgnc?J4r z|Bgd7z;UL`K3GN2S{X^dq zKokI>_W~QAgC8?@G`;m;yOdd^(jgkB#pcG?O+XPSJ3bPtDF7S)2zTW-Mlk z3SmQ)DLZLvh62_h`7NvzwgtV7m|T@m{g~ z!r@0&{4MyauX;)>ffB(+&7xWC)+KCa^P@8(lbG@;H|>z>5aFmNN81SPr}!8>)B=B#-wjx**CD#xCEo?znq5xUi(B z<*uj2V4-U%-wyhKEOTwpbvcnIS=N;(>$+Fgvr<`&6PDh1dFkc2rz`I4!ux^8_lQPy zRVdVwm1t1k18>EZ=(WpNFO!ZfS-B}uxoO$E`C}UfdfkD<>ypC@zaK|I;op4Jh?Ygj zD(`plHVih|)09LM%>>970kmt9?geAE*|4#s2Dlddn z7o&6(6fGV0YD~i?T50M!;9@3$n2_9cq;eG(yk-OU3#pK7-KZnNX5?))@VOmZQfvx^AiBAOMV zjj#wmW-}M_NBwO0MXbp@MJy5P8IS7StsWKV5W_}fUaCU1CLrw(<4S=eU%*G<;;YHt z6fE~0e2KMWk5m?(?WAGajnsS|TL!kMNqROWJR50rATPRpDp}v3sPDhyOKyBRvGHjL zTL>}0VNl3dGytJAS}+XQ-$&qy@)aB!VTL3kr3m1Z9M>W#KKy4UB zCy;c~NmEgXIY8Eenbo8oAS{I3hUySUQ>itdjnyrhT6<=%8 zw;|!%aHs0Mt$*x009PD$ZPMM9aChC>a^Jn-5rm^pS+Zr8g{TfZvSs~iSY5f0)#GLV zSWC1rGah*x;!O}0IAU!}HM5b>56EGT*u)#f=-+`Y&JG)n zcyeVe!~Vjw4-p@1MN~Xu(n6K<#Q_zX4F&LsMjilvzsuLG41y;0jWC|$9f zE@3Pqnep_1kWy`KIMpwS$T1_->@?(lTiQc4M}X z8#CHspXk*-XaTeqXq+ByZJ!7m-`E+tF=lzF1Bbq#o>t>m5snh3;m3Sz)H)U{-*LU8 ztU8NC5-WHh<wr{AfZhmV_Lj&3$INi$EQ#}sS}}n zXWhU6WY+_RV+{Gr8KDt}nx6J9czMI*n9Pl}DtVuSG=S6vQ`j}3PgdMnvGGzDa<@q< zgEv;P?;o3n#zb{!#Rkbcyjmr8Q$-p(1#` zCB`Q4*pUMZ&pAG1l8eL``RUj?&D~n@D$<&x8i}ArT~ef4RZt|Hz=e=W?IjZ&h8`N| zNyVl8i(OqWUL5eh7@o%j1C6ElS4SWn1TfSYrcZK`9BK_RtXOrVNFE7V5c5hjNb^fk zo1_80Ghk2zsR}Pe(5VuDDk3nnJJuw`YNW&I?d9W?gyxCOUfAdbEUq|cXFm{D%7x_- z9ht&;`_b@W94;^aGGb$eKsw~JfMbecuHIh1`e(onhwJDi`FD^=5PeQsJAg|hP{Vcr zDKDTZqeS(TcFVgDmpvM zB2xaGJNP5QQge1?{-q$5$%aVHut>9}74|8}+;zxi^qWB1*T z-RJ;p4~#|$DC63Rt0$7a&V&zy`MZs`8}IpcKJb?DQ9N}wqiBUv2qiss^;pu^eb?8$ zg5!nuetU1cY8T{V1|5TzmA(v-Su|hh$Aw6?@M?g@3`mP0wemQf8Ra- zzAHz+=c|Ji1f6N~g&SXZ(9m?VKiRxJ(Y$@RVaG~S%U>5b$}2$pH|@9lutO@{lPK9u zb`o3Hb|~V<>n|r8x7=;ql2dv@N9tUUzWMTvm&Kvr{;i9fs{-_npk(%RL<=S@=s`5 z6Ul>VZFLmXD1b|rZA_HmRHsKIFa5^0#S=gJIA8KM;Pj<}>hG0Rt=aG^1V>-3Xq4V8 z=(c^^(rp_xm2Z&fq!iK z8-jj5Les;6LaXp6fQ8 zdv)L0FL-U^%%=H^@wTiFAwV7vY$-Ej*f5 zI5pC;PE+ZhS>Rk@Gi8*F(@!l{GxjIxFq`Gc_wfz;9~})M=1|HdG?a8C5e}VV)hApy6%Xw+$88ikVZymBr1fX5sM3t-FLS$WHELGe7Tuk zqtrg3tr0ljAtHD%&!{SzbBHk$US^&2_p4NM6H=q*kMk@A9Z#p z3p$h;bf~t5R4#)KlM$|`*(cn<^oD3=loOd7XPK{-oj9prsQ3KLsA1ic@29~PU7Vv1 z5*^23o;67PK*$=F-$Amq3C)c1nLL6N2YYid46S-lXVj=ZQ69@Vi7-?c6+;Ka;go%+ zh}`cJx(40#nR=2l%L#Z13_@|LhXKgWJo2Kp=e4BZoX*Ou2b`Zxy;pl^A!sLny7-mh=p-Hel zTm{6pU@x@66pCrEixDuTf=|Mjvn3roNkI$B`%{22{H8ksvw94{cG zaxsRP33{2g+wGX@#yeZ=)aY1w){3X-e?e=j7#V;w+a<4$RRhk3$1J3yk6B1EokWZ8 znU5I?eU?S-vEz!x3C}e3c8G)Q2S6_NWF`pJKBl3%qZrs$eG*`oBkMCL`1wT36z0%{^z)}1KpPL^#-lx>Q;H-VmI z_y_~(LUFJYiQpLgpZHEH*QLFHZYa~@G@tSmE2bOQUI4=G@gJC;Kh16Zq)tnpeem3A zzQ0A>wUR$1o`6>j^Z!jXR#1nkwG#O9HBkW$Vmp0W{tCW-Qlx!H)u*|=u@WtAP~3bW zS|k5^D)p;WYLy{{+G@(L^hGa}yF$}K9D}9YIa7R$gatTx?*+Pv=5)Yv7?;)%KL=Zk znVCuMSZuFoaMn#iJ$}HB+<;DqBO|zs9-J^QJj!375>B5!4dQ|8{Sd_};ZHoL$Z|=* z03K7PQS0ZaZQrLCr#_~q=2)l`ew#ja((BuJfjf)JhO~D1kLVLgZ1q^=N+KdwA-W() zJ3<*SMItB@^2;M}%+u~z zTOFUVtol0F927vR;*Pa^4mzdE*0ll-7D}+XbaBv4RTOcsSgPz_^Kj5BdFn(_Q1lem zd{joU>$qL0WX;M6rY%yQ?<4CA7RPRi_N{SnZNVzFcdyybS{&Wf7!Ir!99efz%=%GV zwWDr*TeqX|<6%plW4~qHYj+f`yNewg*Zqerjt%RdvF15y)+(g!yVmlH92-`PTGkx+ zMK|iUaLk&M;7~wu4S9}3mX#VDVM>A3g35IV1>l5Dw;`1}8mVEp4GE#OPL9iWRB_xR zD?(zhXq<>R(;lk`S=P58V7Lt_U)(H+8wPQ&;IJLPbf2ID_YBf~f^@GS-6xnopBHek zuq2If0?8%FE}9!4yXcTIQ4ih*LwZfnkMBxD=w?C1^(2)K#7tN~{zGJA>4-{GuBj0b zstQsOVfI7QK318oO4Kg@`?sheLbgR9V8waF56Y{qmw#h;(f$jHa$9QyQaSNI3b(BL zF_}dl%=Wemii2^EosXKA@#vEHi=MtcUOlF35_P9vDOF_G|e#+{vUPv?r$ zO_v0|QM^=qNjymboU)Cg>wSXw2j3+XcqqS8T3Gt_y9#%fSVExLm z1*@4ZHe`Jfbho@S+NhQ_;a5ZYFD_0Yt8%mUHtVU>kaZ7rRV18t=qhwEuqq~96wFJ7K8nr6NWR408Q>l1cbC77w0V#|1xm$&#{Gg* zrsdy2?I{O)M$7L~O9&J2$`*zmqx8K(brpsa-gI~b^^cmB!Srb=PRDabvQ$ zEm7Q-3k_^YxHmwWea(H){?X{J&;Vo0v-f{+EO`H-PRVVyg6d z>ziFSx)Pq&xU)6xX-&tYZFK90fxL>~j*L%@SO2W2Z0YmgD!z5$J=^VtW&iHQ&o3A4 z`Lm+ZrROv(X^v~RzSXx}wvkZnCZO8l;`RJ|Mzy}50;;XP-U4+SZ2gmsI}?pN-`o7# zyMJ?cy!0p`+hbr7pNwn)J?){X%+%oT;!SMDghEbhDGD6I%z*!L-(uWE6luvsy>1B;$EP z>&~K2nhl_y_3^pBh_SkCyKKMgcmjlRJ`^so&@QmR24++2kLg-5Gk9zMhOiog{V`Hi z>a9{kT&6N3h}}XTs?~4$@8kO*Q_l%oWIT`0#XU#AFXum@=XPA&k>2xsrRPr6U^bST zMdXj!bGnl$<9R|K0J+NrmkXcJ1II$aLZND)&!mUhNKhKXNCfhRVqV3{Sx}l}rsAWk zVU<|HmQrjJJHGee@G0`hKn&dY*`gak(!C-{i{e%YvpD9DO%r)Cas@mopYT;ziD6N- zpx8^(31IT0V$+qBBNRS6cTWDVfTfmSIdMwlPnD*#)SUH4l)PN2jyp6WQimg{GUMMP zuo|1j{RVd6w^XH)LFt~Jr93@G$&@Nh-+$Qo%f-?GVHIPijSKcd3`t=mu0Tci3M*2| zpgk~rfgNG!*QIodEz9%p2LdwOZ`g!EiqIN`JV||oT!{>KV)YpU2fcVQEzbjrhxuc9{;oC%RoV+;NO`cy=S4lOzVr+ zBrhCcGPa-MNIolO<8M)jUjG{qQG`(XtJ1pF>hP~Soe(ek${oe)ebtWQk0E*P75OOj zDE`+T%vO3V>xJlwF7|y6*Cq_(aDcEqWZRAJFk+^#T?so7&Qrqq2s;ho0wr9Cc$WyP zm(OSZhAQ~#dPg`9*W2Ur`ggc3k~Ex;m?~Ucug5q!23OZ>X$lZiopyzNA!2IMuCRA; zOsOH4n`6ohF-08XGsG0nmj`N7MVfl>7|d20#49+bDh3-D!8RJ5l^m#%6!)9O$!Od_?jetA2NGMR+4F<)v=YI!I@3kfToMVVu8 zwt*5DQBOJmE%S~=p{Uo|cz$N)MWubL3#03Cz#Fi+ZOVx-^y5>r@`o58^1{))e1WIs zKc`4wU1MY$9*XTtZ3piyu=}E4pR}H;reakfaJ-2zg2(h%kRtgnx0;?-+!#l zK^+>AOK|R{1CuO40u@~u-hUWJi-iZUTEziyFmX|isb>2P{Tw`6Yr0Xi!HnVnPQHeT z2H#wz*!I9m5BE=yje&A99WA^mO|puj=zyaj9YHrWtDiLoH#%Vluh&YKpb2w5;V1_= zE0r}iN2kr<@N%R_1hq}ImgF^0>z)m3D0<+`m-Pz`c*N9osyId9z@agMg5DCChVWe4 z4nP`BL)^%z^}}$xlv=9x?aMS5h=}L8Abi&29)H~F7ve-fUwbHw^LkDGGty)v~0vG$^=AN zWM)ntg*aM8QsW`e>nGG9W_Xw+StPKuqp1LkQ|jn`d*;_>76+5==7bw!SNP1X?An*y zbuzJw&c#-BLw`T8+Li^Y;253W^eI8bxQ5}C392esn&EB)HX%!gm+mvp@XD3$jWnRO z1>QdSYbOb4L{|!+?c9^xc`~sx7toMjOfa%}$Th)C6>6rc7gemnQ|!4Tu&)|0UuFi( ziv14`LAuSFe(__UO8FVB&M*NPtKUY0!|U}<|cW|{&r?M$XD z*r}k4_;+j5Z|@mGdpzs9X~LPUWx0uk05Xl;}US?7@WuhyQ*jT*wH& z^bVNdX)))1G61#_6f>8tH>0vhtl_bZiG2GB@H~>}KeFsO8h0N3`-SItZg}Rx<3A0c zc9;RR+^j~(#?m2cn&{3Gzt_ftU&$&755+UQf>E2HQPOPt)5mtwTTk71S4vSlk!t=i#*-qXER1m zrnKPyLGe)JjIfO{@5T|u@K2cPwNR6I(lU?(^e6R3G`NpKq|uT0Q(JZ6LKVf)YYTz$ zDFl8VhJ1N29MT;9^UCPm@s58 zBtr>V5MoJ8$c~VM1~7la)lZ^&toV&oyFBvfFC$qze?5|~hFsL=4WAnU8k z@U;-7m65bYlfSsQe52IH4T+63Tr}?q_zcn$5_bt1K?N%#Wrehk;Iw4Osb?c8pODP5 zR6vUrhPu2IiNTit0IxJp8vHG&KjIRuK^Y=EV)o;&Ec9%HHEgUz!yDA3K1BUCO>GA< zK%LpyosDQQ=|vTN2;?L%H8}(lDUW)rkCmG(R&3^$VW(TDW2NbhqBv3rMudGL`$G!K zE|Bm9;?96*q%vc;HflfqwyX>8Sa(<=ojDHeP||{`wGN%aVRvp7uMiY&+vHb}Um(BF zAQYkG;4+1iP?;hE62f%hOKo6>7>*`M{w)e&A0asEfY_?Le*9MZoz`W~_PBHVPtXB5 zK~3FFh-oig=}j#sdWGdxX^qu43uVwv68$@~=C&<+cEp`K9y2KfyurSrRhbV-BZT#; zMQkUTqfZ>S)?x$U%B?hhyGxBD%gk3K^*n687&*IPBu_$O%R0%jbIsOo;^bG|@YYOw zj1FlMExlVRbAmSck21nke>fn9hi!?p_Ik{iQV73*N=nM2WUu1zLoL_+3E-m z2xQL|c`JR|Mz0JA%Krz_8+Ax5)SQH+jAvlaz%-Z9Julf%9TN^j|2>q##6^%{ifRR%I zJR(L$3@%7KXiD^-(vUrej6lY?(`?7rcC6Cj|KBKhpy{7ZCENBV+VND?W3;;=yi}@42)wG+D~6~5^D>^(d##;>%WVSKQFD+ zbuZhyS84*w_5eBrI%}mA)K>Wi`M%YHl9aO>L`GrhinDvwQS=e0E?m{lLH>x2 z(C8})6&E;~Mfa#vqv((Jpo8>S)_d?pCvlAA2!){#vcQ`FL=OlFJCs7~YXAjJ6?q$I zqz9>os>$y_i-CHG^H+!R46#B*ly8WEibzRkB$b4k$Ve)=L$yR44Hu~`4Aq4|4hRDyw(c&9OaW(8kdZ??0OYjRN)o>|(z4$E)HIhPV*oPMTLq7grAia_1PP(MsX3sxCYwX z9H~y=Yv%J!Yl!R^O-M$3vGLYuABq(jyVMygf)$9SjoXv51i&(CAjv|rPH=yqxCzf1 ziveUr^j{NX{Ak!$B&hMlL7dIY?q;%ZKKa_oHHVFMKbtD0Ha3+eAy2$tM8J6H>w*Qo zS<0hY#asfr1M3-63pIeiIH8rCWT=WT^3 zuo5QCnIvyyB>WN}Rh9uMiu_xGlOQ2JbXQ!N`<2OMclSr|@7S{9Ex%TLwHWtH_P*7d z@b-U%yDqoTX63o+xw$pjvN?f&-p!yRR3KsnYY&APh<|scf(M^BolK&LB4ggP-hj`s z0oW08u>cK;^9nC7T^91<$E$6k$dHr7ArD_|$hE$i4r{I>_|5W2brl!D zPSd$jljE^)h^b~x-w?@~{>AA`W~??180jDL2`)VXE+9Gf@l6eM40Ezq=58{acW2!CI@)#(oe0niKizY2AxqvN<@ES;z z4aD68KSg*|=9pn7vyNs>j?`##=A+Co`3o4gtm)%6$|7RbX<#vas?44`*;9oWHsfr{ z-by~*)cKi%;_c04WN@co>1S-Q`oG8kN%#x0^f* zQeWIW0}aTppir?V44ZT|Rm}%2M_>>W#4*}N)0J3z8VGWx>mWFgOyCviLr>|IE&9kW zdv?EXNq7##od-m(jZ;|b^)2o-rZIjqAKWoQM20b+lEf@-!Ge2Na_2xhGatTV7k68s z4&rb#9^L52*I5P_Xk!h8M-fOu;@FUHEY^f+~+;i-ohg5rUFVT$BV; zDKdO$rh6Hh=>Vk6uebw4y)?*pKRPNM$5rjn>Wf2$AxNZ)*{4Tw#U)+$Dhw`+S;-Ip zPHQ*`7=9WCJCDPsKx;Ci#@i~*j#T-GYL04L^D0eB@|Bd6`X}R5V)+bmWDur#h{{+x zW`?=lG-U+K3*j_taqoKkD`i!5liQx9JxNzn!qs%EJmKo2NFz2`bbjEeL=4Og>io&t zt%=&Lcf!lHyBCkIRJO#+TH@}O?75~9uKx<{OIu+Ub>;l{>T;Sp@|Q4kOlUJR?}HpE zT^W-Of9h8LM$0MOGh`dJo~L_;ipd%u&ORng$k(l}dvRNk{jx1e!ZUuN=(Tvz29s6r zSQyyVuj=n*$8@<~_Tx*0zkt??1xLshve#%HtoYk5EX&M1V};e(m`%H*$A&w497Bg8 z$aV0Q0BkMxga3*LzSe(;?xhMtF7R3lHS_(Or~P+#9jHH4l$Unnk48M4!bq`P z06@=t45VTXIhVNH6q-pZZ+g&BhO9LbZE&}3L%f;EFQQ4A{s5SA30ehSXGy-+?c4`c&BZ-a>t?@bQFAU+>Hr$<4p^! zHdowL*Gm)bI{GcSj^n->biAHTSZPH3<0Jr&A>z@p%ts^1Ev6*7cKQlRwc=n=tW2U~ zHJORbbgUcqXn=YS^O5Kfspw!l2cSjLz*s}7%?iJiy=7#B``?d6=L zsT)YrA>=URggb_F1j0%Lbk{v3?Du<`OvlxWLnG@eNd@m2c^7Fnr5#YmB#F-!+0EyYZQB!V+uw68w;fn+dg}ep-fcPzhp|m8-IuUeuS;but5-Yl1G)7788Zo!N2-76*>ms~|&Ii~$E>D!o{^ve3c;M6_cmRwB>415KEoWmQw*8+vJAQ5s*MSPR zntVBg@sW9rYW%*KSUB2ED5ui+{CuUrR1pPq!85fSTaZGncxVbXrcJ?Y&(&MV6Zt0c zG3tb|_FYf!N=0q5qBBv^N!sh8t3^rgj)WJAT5DzT;t_BhCB7?1z99~OflYkTVauxL z(7J;HD>x3O&VRG=n^E-!n({A_|O^q%DSVIF$VkP5<5aMgr10jrK+0&H5W4g!>cK{VTPNZ|=FVCs}(i zQF~Ci32>#n=3`eLDJAn1ecF)f9oLQ0#Oz|T4Q|c%Defq}j^Ty1{2bby%9}Ym0WwH@ z;3WTnlKe+{{Q$4qcJ`A0#QAKPPi9pdbSWi@V9N8KQva#oSdB2%Jr0q{3v|dQAN48D z`^-wrrjJ>%F~7ln7)A6L|0X-%(@KM6y;kFirc|r^G#aEDv(VrKsxs|VHdFULNA0Fv z$zZI)av`?YIYS@Vz={zgNiJoQ;t2AaXlt1$Lz&1InRZ&4aGgx6mzM_Le35C{!lpCy z`U_x=$bJM~#hLsCj)9fBjx`(oZr&+=k37S_s|7{t4#a;{!3Sf)nnba|wK$qItCJ?h z>csBoRpJn0tKKyjCikuuRDFb#Fx77eDMw=3#aW#&%|%E{ze`EKODQYQu}evBaXh8k z@^Ub*+|jz$SLrBRDX(9%;diy5W!-__^-UEHSO9M*R6gN%wIHzW!0&ohiKBkK9hW?; zSMbZ@*tyosMl%*iyVj+4rAuaU`HFE3o!TkRr4*(h7*p+0XbH z>A$eCvA*hnjg18sHZ~aXvQsx~ZRi}&JlM~W`?WKa|EhLR_%KX^VS!_`m?;#-!iY6m z&A7B!qhYZdxvR-~Mx{uGi#Wz;Ok51dZL*`$?P`QQjaO|u>~F}frUdpkWLHxfYGAvX zGW-pznqeP)VM{Yyj*=VGY-lRL9QjiPn&aD3ehu|vQ4p-sV3m>$0zJ21Y6L(tPlkc@Yg&3#mTwL2uzQ$F!MI~9+ zpQ!5>p2u~aJ*rwZtL$JVH@E_re(H2DhMf59HrMI4) zcr|+OPpJdoZf6;D}Sfd$Eo-o6Cjb;k(G`z|1#NjRa6h5TEo_430SBJeY%lUv{ zPQ(NV2DZQ%Uv_tXB-z;lM;u9V%bx7qmB2smu8(Z^u(UVQu z{y5-}zi!qBB0*yMu_%4%z)(#LKBCr;9G@*ua67?I11$0{nKg(gp;&`CP}Ju9h?+y9 zceOb=z(Z)Bae|PT>YD`Coe_5+89gd%FN_x=)~6-DXw@X8kev#NX)AIvYYR${Z5&J| z9Wz^XdbhGUt^Y!uYDZzJ`2wWOuf2S;2{+Kkoh@-s3rYI`_v=kdm*Y-Fj0a$8&(FNK z@^w_F_R@4lGplCkWj3E?0}4;;<&LX6Ub~F5RXt5{XH(qMM7_jtUbifLDekPv#`9V) ze;pl9!}FhdOw;B8pq}~sxbwiA4-|8R#3l?k)dEJ}%zqX2rXgXLVHlv7Ex>RR^oXm1 zu7yopGUtehHG`=#b8!Cl`5WhNzI5ZoxWS#tIK^p?@(i)CVfa%omoTYA}^e z33UXiALKbqFMx@~K#u_vtGMg+rDT-eDw>8Of)n9=5IlvXH1=Jqxmt7m!1c57k|xE} z_UWakugFV7WOaLV>FAZuEbnsX3Enq{reEPDfA4B3<@ zX}Vi-_*>74v+n-Kk!0JxMBBdi9m{Qp$xeI2hh+D0?da8`-+-)@55+5A_VycWwT;L~ z8=p<6?a9bUg$;J_K{iSsrAc*zURfP=Wh@v|T~HtOlujv4N4O<)L#9*s+N9}de=6<$ zrfGXg^b>2+7kCa0%f`$W^JZ}UC7e!TYHh9|whtM?=7vTT(Fc|ud0 zvd+!oa#*MxA~dj?vVrBR#rjZ?aCHm8eHN)yniPsz*gxSQ`{$RQzY@I`yBdR5d$qLU zTG!Psu?yq&)JOSJLCK4`gg@P3dcq^*T$RujlpX9#44laHHM_a|-I z5eA0i5)RJb%4$|av&0lPA925bo#vV@wq-o=ee{9_46$;pBvqJvB4|81QT|Ix$}(J% zh02QrRw1p8)riVfac+-alhz8+ZfuW8U=ZcfjZ6knt}2t&G8(?obyDU+VHW1{OUrj! z{wI3F3^C3wq3_@L<%Vwc2%X4)RElX@!s7u?l%rf2# zp)Sp-OeyYbl$c)s4r?j03xS`PAeQalyjE1?2&{TwYJy*I*2VC3p^%f#Z@?Wz0ZgZg znl%@N+)`x^r$jcGFh@cLjNf&i-LY-m&Hj(%BAEw?wbJY&O@NCKZ~hF; zz+kFyWJDbG2B$1q+Nnb2hNH>xv*L0tEN5Kfg{z~_!CF}+sxOsC*S+Hp+Tdlvn=*-X zWKsjmv{B2%ugfGmkcl~!iD8z>>{RyBtAt*pN=p^$cQ}gU<$V-eqQ@#%Q_8gS@YQni z0;QE}6x~VD9wk~<4y=#^^r?$p-SoWx2DQgAKw(*|)eH2cmtIHc^;`5JY|bve z!Y7Sa)PDLvYC>_wf^)ZUh1@H@hs5|NMLxm{_Yzwyf8~@cE#H^g9!kxBA{GCMRQOP; zcqr9Alqw%e_54Oy{E0U{RXvpacykscXniR4K9qJnlzP4|l|Gcp9!d=lrIrtD-iP+U zLwnmp`^Ja%O%Lr`@#pvL{SWPp5ACfF?Y$h|g}`cFG9`KMn*1QY1x7=81nKLZm=hmRlR8%_<~iDf$zQEvF4)6+GgO$;O*7?`9M0r`g~e2A4-Q<-4FF}I?U<;s7KNf zs0ZbcCgh{(D65CHXucuc!0HjG$I>y5C=u!hIsv{L(~TTaNqUcmDBfJRD{SqpG~MCs zuXMAAyh-HfRU$Xs@i=XJs)`}%6XHav zkYyV!np2V~N>mYxB~4RgQ7wp)0klQNAsG&fP%cfWhKT&x)ol7O6h1%8EFl zQ?aO16BQzAs$sGjq>`ymDg}i~rown+^?XsHs-YKZv-gWr`iwHIP!K}_@yt?@v2&*v zvr<8%B@n4F%b4guN5v~@L1vRoYWldOiBy?{X<;TX9h81X(KJW$WKsn726RZWtjMX9 z_)_wvSChx=b!8nhWyecly?u!_h6P9b%h%QQZwSxyNU)$svKuY!h4|{vp37ulGtOEV<#Pz zi3KSSdMvk~!X%9 z07MIzC{irg9XSC zVqzOB6_Tr(h}%ZU>tvQoc*=3AS=FXlqRLz{t+?OlSStxz{`CXu$m)AHLx z%28*P_GcebHNs<_JoS_4zUAhh?fc2THKB{PL*oT2AS)9RU=?U4^qrEVsS~O~tzglC zysQITY#9LJ<=d5V%T)qY1cSrgXV(<`f{5louxoDS$j>H!*R%D?ySGxljttSw)>z zjOx??tCOX?QUE|Sib@uAk;UP-G!rf|#NoP)7*}9LVo{-hr+9#+LTwSEF{PI@8Ky8x z6!ua~Wi`n#Qe(;8uc|R|8qe^vE3@ZQGA4+onD#2aT9(?1dX?m4vR@n<>tt}>IX1>V zvbt8v7u+vo2=&DCr&r*7!@i`%Zxj);0m6;a_)(`-w0FW0RTdFG)Iv=Lwtq~1wvEru zQZ@XPI0F8|xCHu^;o1Uo603bl@f~P}g*Yb$1V#g@O#_hVjxzd1xZ`Xc&cg1y%n(Q6 z#=FA!6m`R#q>Aw!4Oy9bjgxCHwUI}w_Amu%))bl)N9==ZpoMIc>e=TeQcq<6jGD-DPk#+zw(HG%?a0(tZ zu>{$1^D>~#grDK6<(Vj04cl$sin-s8*)cYZp@DZTVu$Ep@eANPY{QzmOR(JO0QCPs za}4+yzXy+bvN^)}Lg5APda!3L*s~ryz7{;bA+&Go?cUgXV56mD6TH2bw+PT|hDjv) zUlCuZfnj^L9)^R!3%K)~}40F6n=DfSK`zoFha(Rf5Ie*2!8@S6= zD5o!0_}$zc6R!?dDt_4`^X=r0Gr#-h$kOq8D!0Ajt@zQ!Svu(@Ca!{&T-D)mTL^L2 z0kVET$oIo$)a_dcPjIsLYOunORHB5 zao%?8LW39-y;r1flutUqQFQiZJHogyoN@6iIjHGbNi*I^R#|Ak*+9Mh_CLsP=gDe( z=+l`M^ZJRKsT--~iJx8h$(8k{BcIOv&cL1cvz}hAd=SD06a5SbwCbHFH+FzPqHRQ# zMF_-2oDMYRtRf|rJ^f-LZt=jv3LD@-mt7}oQ7xF}gnVtI4AT1Z4J zKC2m4#71!zj#x3Zkkv}EQVnGSs0e7V0#1+B#FVAT8PtJdU|?w2vOy>WN~^N4=?p7c z!1xqa2Rc?{Isoz*2v=}9IOPv*%^t3d)*s*9 zpS<5V@L23!Y5sY`^}w?CM^U(F_I5uNNNd-6OJc1h@ynLJg;Nh=@w;e5@VFNn+GuQh z;wPPnE#A}7@T-Hp%grlqell|V{CfY;TK~{L`Tr&I&yoB6=YHh9)w;=(=>AOtbTFbB z1ZaEI*tOnxWUcYY%Fw;WKX@#}uAN>yy}I|+&kp@#-#_%-6HaXit?NS1n$UA=V*SwQ z+M&_=!l}&{NNfMo7s(26c{`@g zcEsV8K@l_d(t5WEfdif=LH{$9oi);I$uNPZ4#AV1KJ45^ov4Y=5?M1UsLIVvfnEyA zOkF_5k_uz)rXwmbU9h{S$`(EUesN|>%}zml0_>_jzO5xpqv75-2`bErp$#~Ss5!xx zrr<)Wsrs-1zW*-f!%}b4`V~d)uQL$Itjbf12e*k|XDn>x5DbGMfsP?J<0SMLS9TWw z#Hnz>KynW1+`I+^mz0-J)#S6g{*APtixT{I-!WcsB49!rV62wmr|F${k-}ar3~&K<5lx@#7>E zyCy6OAAECp zCi|apOYEfaW!eq26w^|)2S>rpNyUoDxMAR8ci|;Y@mith@Npg<5WBpNbwn`w20Y3k zG^20B_{-ah**RUy&_O6YdSJrz5*Lnt@#GvVs@Y1FayZmqmYzD9vB<{`!28gTBpm&rz&NmGKt*^qD;oBK;*hCGpBKs@kLr=Z84PJjWBFj~yDwm!RIRN~E zGHEA}d=Ax;&u^1N_=3eHEn;y;%D#7#$-beo=iOwv{Y(KIIPe3Vqh52CM2q_qiur7d z>rVJApHb9Q(-KspdYe(Y7rQ+aye@RkARCwP+DFQ70)!R7D+IK(eD(*rrf{z9gH2Ds z7khjJ6$94~$=v9kYw5-GGXI0j{HaIb==|{Kds-KK8aU%B!6njkI?KaTHT?Rf38?$6p*4}9%ze0WtD-i(rX+tWA^8m^@l zQ_I|P*Rr%czI=Fj->rk!3wI+utKJ?v>;@64rD_U)4Ndhy_ijOh%za$-7r+>P9~6(q zQ&nS?Yc+@ks!j||YQG2Ms=pl6sPT7+9J<9jO6~WDrEV`-dJTh=1P{Ly>;Pf-fy>8| zCjc}30GIQD03@ScJJAH$!4a5YaJ zx4gLC(zn*qw|abdy=7z}ywP;v=Fp9yUp93w3_lR!>q6g}(6=)DN&DY-eA;neIQfJp z%{}mF9p!8&w(X_{X=TCrwa-SuvoKAf4PkAf1RxC%NAXlyA^Y}%pBU^F2H>X-_&IEn zUdCP@<|hq?n3f;v*nyg*c!nq*ANoi5z&*AxnL#k-AMD^J1gX<=z)_(TQ|ERxgScUv zFYW@xkh6x1R&db1%if?mbYS3V$ne5r0FP z9+Iwyr03uH#)sack79e~1;E8X+x+CZzje*ux*WXgKRD0-xBsMv>v}@)xpmqbC{yfN}_+KTI;uZh^ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/runtime/environment.py b/venv/lib/python3.12/site-packages/alembic/runtime/environment.py new file mode 100644 index 00000000..5817e2d9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/runtime/environment.py @@ -0,0 +1,1074 @@ +from __future__ import annotations + +from typing import Any +from typing import Callable +from typing import Collection +from typing import Dict +from typing import List +from typing import Mapping +from typing import MutableMapping +from typing import Optional +from typing import overload +from typing import Sequence +from typing import TextIO +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy.sql.schema import Column +from sqlalchemy.sql.schema import FetchedValue +from typing_extensions import ContextManager +from typing_extensions import Literal + +from .migration import _ProxyTransaction +from .migration import MigrationContext +from .. import util +from ..operations import Operations +from ..script.revision import _GetRevArg + +if TYPE_CHECKING: + from sqlalchemy.engine import URL + from sqlalchemy.engine.base import Connection + from sqlalchemy.sql import Executable + from sqlalchemy.sql.schema import MetaData + from sqlalchemy.sql.schema import SchemaItem + from sqlalchemy.sql.type_api import TypeEngine + + from .migration import MigrationInfo + from ..autogenerate.api import AutogenContext + from ..config import Config + from ..ddl import DefaultImpl + from ..operations.ops import MigrationScript + from ..script.base import ScriptDirectory + +_RevNumber = Optional[Union[str, Tuple[str, ...]]] + +ProcessRevisionDirectiveFn = Callable[ + [MigrationContext, _GetRevArg, List["MigrationScript"]], None +] + +RenderItemFn = Callable[ + [str, Any, "AutogenContext"], Union[str, Literal[False]] +] + +NameFilterType = Literal[ + "schema", + "table", + "column", + "index", + "unique_constraint", + "foreign_key_constraint", +] +NameFilterParentNames = MutableMapping[ + Literal["schema_name", "table_name", "schema_qualified_table_name"], + Optional[str], +] +IncludeNameFn = Callable[ + [Optional[str], NameFilterType, NameFilterParentNames], bool +] + +IncludeObjectFn = Callable[ + [ + "SchemaItem", + Optional[str], + NameFilterType, + bool, + Optional["SchemaItem"], + ], + bool, +] + +OnVersionApplyFn = Callable[ + [MigrationContext, "MigrationInfo", Collection[Any], Mapping[str, Any]], + None, +] + +CompareServerDefault = Callable[ + [ + MigrationContext, + "Column[Any]", + "Column[Any]", + Optional[str], + Optional[FetchedValue], + Optional[str], + ], + Optional[bool], +] + +CompareType = Callable[ + [ + MigrationContext, + "Column[Any]", + "Column[Any]", + "TypeEngine[Any]", + "TypeEngine[Any]", + ], + Optional[bool], +] + + +class EnvironmentContext(util.ModuleClsProxy): + """A configurational facade made available in an ``env.py`` script. + + The :class:`.EnvironmentContext` acts as a *facade* to the more + nuts-and-bolts objects of :class:`.MigrationContext` as well as certain + aspects of :class:`.Config`, + within the context of the ``env.py`` script that is invoked by + most Alembic commands. + + :class:`.EnvironmentContext` is normally instantiated + when a command in :mod:`alembic.command` is run. It then makes + itself available in the ``alembic.context`` module for the scope + of the command. From within an ``env.py`` script, the current + :class:`.EnvironmentContext` is available by importing this module. + + :class:`.EnvironmentContext` also supports programmatic usage. + At this level, it acts as a Python context manager, that is, is + intended to be used using the + ``with:`` statement. A typical use of :class:`.EnvironmentContext`:: + + from alembic.config import Config + from alembic.script import ScriptDirectory + + config = Config() + config.set_main_option("script_location", "myapp:migrations") + script = ScriptDirectory.from_config(config) + + + def my_function(rev, context): + '''do something with revision "rev", which + will be the current database revision, + and "context", which is the MigrationContext + that the env.py will create''' + + + with EnvironmentContext( + config, + script, + fn=my_function, + as_sql=False, + starting_rev="base", + destination_rev="head", + tag="sometag", + ): + script.run_env() + + The above script will invoke the ``env.py`` script + within the migration environment. If and when ``env.py`` + calls :meth:`.MigrationContext.run_migrations`, the + ``my_function()`` function above will be called + by the :class:`.MigrationContext`, given the context + itself as well as the current revision in the database. + + .. note:: + + For most API usages other than full blown + invocation of migration scripts, the :class:`.MigrationContext` + and :class:`.ScriptDirectory` objects can be created and + used directly. The :class:`.EnvironmentContext` object + is *only* needed when you need to actually invoke the + ``env.py`` module present in the migration environment. + + """ + + _migration_context: Optional[MigrationContext] = None + + config: Config = None # type:ignore[assignment] + """An instance of :class:`.Config` representing the + configuration file contents as well as other variables + set programmatically within it.""" + + script: ScriptDirectory = None # type:ignore[assignment] + """An instance of :class:`.ScriptDirectory` which provides + programmatic access to version files within the ``versions/`` + directory. + + """ + + def __init__( + self, config: Config, script: ScriptDirectory, **kw: Any + ) -> None: + r"""Construct a new :class:`.EnvironmentContext`. + + :param config: a :class:`.Config` instance. + :param script: a :class:`.ScriptDirectory` instance. + :param \**kw: keyword options that will be ultimately + passed along to the :class:`.MigrationContext` when + :meth:`.EnvironmentContext.configure` is called. + + """ + self.config = config + self.script = script + self.context_opts = kw + + def __enter__(self) -> EnvironmentContext: + """Establish a context which provides a + :class:`.EnvironmentContext` object to + env.py scripts. + + The :class:`.EnvironmentContext` will + be made available as ``from alembic import context``. + + """ + self._install_proxy() + return self + + def __exit__(self, *arg: Any, **kw: Any) -> None: + self._remove_proxy() + + def is_offline_mode(self) -> bool: + """Return True if the current migrations environment + is running in "offline mode". + + This is ``True`` or ``False`` depending + on the ``--sql`` flag passed. + + This function does not require that the :class:`.MigrationContext` + has been configured. + + """ + return self.context_opts.get("as_sql", False) # type: ignore[no-any-return] # noqa: E501 + + def is_transactional_ddl(self) -> bool: + """Return True if the context is configured to expect a + transactional DDL capable backend. + + This defaults to the type of database in use, and + can be overridden by the ``transactional_ddl`` argument + to :meth:`.configure` + + This function requires that a :class:`.MigrationContext` + has first been made available via :meth:`.configure`. + + """ + return self.get_context().impl.transactional_ddl + + def requires_connection(self) -> bool: + return not self.is_offline_mode() + + def get_head_revision(self) -> _RevNumber: + """Return the hex identifier of the 'head' script revision. + + If the script directory has multiple heads, this + method raises a :class:`.CommandError`; + :meth:`.EnvironmentContext.get_head_revisions` should be preferred. + + This function does not require that the :class:`.MigrationContext` + has been configured. + + .. seealso:: :meth:`.EnvironmentContext.get_head_revisions` + + """ + return self.script.as_revision_number("head") + + def get_head_revisions(self) -> _RevNumber: + """Return the hex identifier of the 'heads' script revision(s). + + This returns a tuple containing the version number of all + heads in the script directory. + + This function does not require that the :class:`.MigrationContext` + has been configured. + + """ + return self.script.as_revision_number("heads") + + def get_starting_revision_argument(self) -> _RevNumber: + """Return the 'starting revision' argument, + if the revision was passed using ``start:end``. + + This is only meaningful in "offline" mode. + Returns ``None`` if no value is available + or was configured. + + This function does not require that the :class:`.MigrationContext` + has been configured. + + """ + if self._migration_context is not None: + return self.script.as_revision_number( + self.get_context()._start_from_rev + ) + elif "starting_rev" in self.context_opts: + return self.script.as_revision_number( + self.context_opts["starting_rev"] + ) + else: + # this should raise only in the case that a command + # is being run where the "starting rev" is never applicable; + # this is to catch scripts which rely upon this in + # non-sql mode or similar + raise util.CommandError( + "No starting revision argument is available." + ) + + def get_revision_argument(self) -> _RevNumber: + """Get the 'destination' revision argument. + + This is typically the argument passed to the + ``upgrade`` or ``downgrade`` command. + + If it was specified as ``head``, the actual + version number is returned; if specified + as ``base``, ``None`` is returned. + + This function does not require that the :class:`.MigrationContext` + has been configured. + + """ + return self.script.as_revision_number( + self.context_opts["destination_rev"] + ) + + def get_tag_argument(self) -> Optional[str]: + """Return the value passed for the ``--tag`` argument, if any. + + The ``--tag`` argument is not used directly by Alembic, + but is available for custom ``env.py`` configurations that + wish to use it; particularly for offline generation scripts + that wish to generate tagged filenames. + + This function does not require that the :class:`.MigrationContext` + has been configured. + + .. seealso:: + + :meth:`.EnvironmentContext.get_x_argument` - a newer and more + open ended system of extending ``env.py`` scripts via the command + line. + + """ + return self.context_opts.get("tag", None) + + @overload + def get_x_argument(self, as_dictionary: Literal[False]) -> List[str]: ... + + @overload + def get_x_argument( + self, as_dictionary: Literal[True] + ) -> Dict[str, str]: ... + + @overload + def get_x_argument( + self, as_dictionary: bool = ... + ) -> Union[List[str], Dict[str, str]]: ... + + def get_x_argument( + self, as_dictionary: bool = False + ) -> Union[List[str], Dict[str, str]]: + """Return the value(s) passed for the ``-x`` argument, if any. + + The ``-x`` argument is an open ended flag that allows any user-defined + value or values to be passed on the command line, then available + here for consumption by a custom ``env.py`` script. + + The return value is a list, returned directly from the ``argparse`` + structure. If ``as_dictionary=True`` is passed, the ``x`` arguments + are parsed using ``key=value`` format into a dictionary that is + then returned. If there is no ``=`` in the argument, value is an empty + string. + + .. versionchanged:: 1.13.1 Support ``as_dictionary=True`` when + arguments are passed without the ``=`` symbol. + + For example, to support passing a database URL on the command line, + the standard ``env.py`` script can be modified like this:: + + cmd_line_url = context.get_x_argument( + as_dictionary=True).get('dbname') + if cmd_line_url: + engine = create_engine(cmd_line_url) + else: + engine = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool) + + This then takes effect by running the ``alembic`` script as:: + + alembic -x dbname=postgresql://user:pass@host/dbname upgrade head + + This function does not require that the :class:`.MigrationContext` + has been configured. + + .. seealso:: + + :meth:`.EnvironmentContext.get_tag_argument` + + :attr:`.Config.cmd_opts` + + """ + if self.config.cmd_opts is not None: + value = self.config.cmd_opts.x or [] + else: + value = [] + if as_dictionary: + dict_value = {} + for arg in value: + x_key, _, x_value = arg.partition("=") + dict_value[x_key] = x_value + value = dict_value + + return value + + def configure( + self, + connection: Optional[Connection] = None, + url: Optional[Union[str, URL]] = None, + dialect_name: Optional[str] = None, + dialect_opts: Optional[Dict[str, Any]] = None, + transactional_ddl: Optional[bool] = None, + transaction_per_migration: bool = False, + output_buffer: Optional[TextIO] = None, + starting_rev: Optional[str] = None, + tag: Optional[str] = None, + template_args: Optional[Dict[str, Any]] = None, + render_as_batch: bool = False, + target_metadata: Union[MetaData, Sequence[MetaData], None] = None, + include_name: Optional[IncludeNameFn] = None, + include_object: Optional[IncludeObjectFn] = None, + include_schemas: bool = False, + process_revision_directives: Optional[ + ProcessRevisionDirectiveFn + ] = None, + compare_type: Union[bool, CompareType] = True, + compare_server_default: Union[bool, CompareServerDefault] = False, + render_item: Optional[RenderItemFn] = None, + literal_binds: bool = False, + upgrade_token: str = "upgrades", + downgrade_token: str = "downgrades", + alembic_module_prefix: str = "op.", + sqlalchemy_module_prefix: str = "sa.", + user_module_prefix: Optional[str] = None, + on_version_apply: Optional[OnVersionApplyFn] = None, + autogenerate_plugins: Sequence[str] | None = None, + **kw: Any, + ) -> None: + """Configure a :class:`.MigrationContext` within this + :class:`.EnvironmentContext` which will provide database + connectivity and other configuration to a series of + migration scripts. + + Many methods on :class:`.EnvironmentContext` require that + this method has been called in order to function, as they + ultimately need to have database access or at least access + to the dialect in use. Those which do are documented as such. + + The important thing needed by :meth:`.configure` is a + means to determine what kind of database dialect is in use. + An actual connection to that database is needed only if + the :class:`.MigrationContext` is to be used in + "online" mode. + + If the :meth:`.is_offline_mode` function returns ``True``, + then no connection is needed here. Otherwise, the + ``connection`` parameter should be present as an + instance of :class:`sqlalchemy.engine.Connection`. + + This function is typically called from the ``env.py`` + script within a migration environment. It can be called + multiple times for an invocation. The most recent + :class:`~sqlalchemy.engine.Connection` + for which it was called is the one that will be operated upon + by the next call to :meth:`.run_migrations`. + + General parameters: + + :param connection: a :class:`~sqlalchemy.engine.Connection` + to use + for SQL execution in "online" mode. When present, is also + used to determine the type of dialect in use. + :param url: a string database url, or a + :class:`sqlalchemy.engine.url.URL` object. + The type of dialect to be used will be derived from this if + ``connection`` is not passed. + :param dialect_name: string name of a dialect, such as + "postgresql", "mssql", etc. + The type of dialect to be used will be derived from this if + ``connection`` and ``url`` are not passed. + :param dialect_opts: dictionary of options to be passed to dialect + constructor. + :param transactional_ddl: Force the usage of "transactional" + DDL on or off; + this otherwise defaults to whether or not the dialect in + use supports it. + :param transaction_per_migration: if True, nest each migration script + in a transaction rather than the full series of migrations to + run. + :param output_buffer: a file-like object that will be used + for textual output + when the ``--sql`` option is used to generate SQL scripts. + Defaults to + ``sys.stdout`` if not passed here and also not present on + the :class:`.Config` + object. The value here overrides that of the :class:`.Config` + object. + :param output_encoding: when using ``--sql`` to generate SQL + scripts, apply this encoding to the string output. + :param literal_binds: when using ``--sql`` to generate SQL + scripts, pass through the ``literal_binds`` flag to the compiler + so that any literal values that would ordinarily be bound + parameters are converted to plain strings. + + .. warning:: Dialects can typically only handle simple datatypes + like strings and numbers for auto-literal generation. Datatypes + like dates, intervals, and others may still require manual + formatting, typically using :meth:`.Operations.inline_literal`. + + .. note:: the ``literal_binds`` flag is ignored on SQLAlchemy + versions prior to 0.8 where this feature is not supported. + + .. seealso:: + + :meth:`.Operations.inline_literal` + + :param starting_rev: Override the "starting revision" argument + when using ``--sql`` mode. + :param tag: a string tag for usage by custom ``env.py`` scripts. + Set via the ``--tag`` option, can be overridden here. + :param template_args: dictionary of template arguments which + will be added to the template argument environment when + running the "revision" command. Note that the script environment + is only run within the "revision" command if the --autogenerate + option is used, or if the option "revision_environment=true" + is present in the alembic.ini file. + + :param version_table: The name of the Alembic version table. + The default is ``'alembic_version'``. + :param version_table_schema: Optional schema to place version + table within. + :param version_table_pk: boolean, whether the Alembic version table + should use a primary key constraint for the "value" column; this + only takes effect when the table is first created. + Defaults to True; setting to False should not be necessary and is + here for backwards compatibility reasons. + :param on_version_apply: a callable or collection of callables to be + run for each migration step. + The callables will be run in the order they are given, once for + each migration step, after the respective operation has been + applied but before its transaction is finalized. + Each callable accepts no positional arguments and the following + keyword arguments: + + * ``ctx``: the :class:`.MigrationContext` running the migration, + * ``step``: a :class:`.MigrationInfo` representing the + step currently being applied, + * ``heads``: a collection of version strings representing the + current heads, + * ``run_args``: the ``**kwargs`` passed to :meth:`.run_migrations`. + + Parameters specific to the autogenerate feature, when + ``alembic revision`` is run with the ``--autogenerate`` feature: + + :param target_metadata: a :class:`sqlalchemy.schema.MetaData` + object, or a sequence of :class:`~sqlalchemy.schema.MetaData` + objects, that will be consulted during autogeneration. + The tables present in each :class:`~sqlalchemy.schema.MetaData` + will be compared against + what is locally available on the target + :class:`~sqlalchemy.engine.Connection` + to produce candidate upgrade/downgrade operations. + :param compare_type: Indicates type comparison behavior during + an autogenerate + operation. Defaults to ``True`` turning on type comparison, which + has good accuracy on most backends. See :ref:`compare_types` + for an example as well as information on other type + comparison options. Set to ``False`` which disables type + comparison. A callable can also be passed to provide custom type + comparison, see :ref:`compare_types` for additional details. + + .. versionchanged:: 1.12.0 The default value of + :paramref:`.EnvironmentContext.configure.compare_type` has been + changed to ``True``. + + .. seealso:: + + :ref:`compare_types` + + :paramref:`.EnvironmentContext.configure.compare_server_default` + + :param compare_server_default: Indicates server default comparison + behavior during + an autogenerate operation. Defaults to ``False`` which disables + server default + comparison. Set to ``True`` to turn on server default comparison, + which has + varied accuracy depending on backend. + + To customize server default comparison behavior, a callable may + be specified + which can filter server default comparisons during an + autogenerate operation. + defaults during an autogenerate operation. The format of this + callable is:: + + def my_compare_server_default(context, inspected_column, + metadata_column, inspected_default, metadata_default, + rendered_metadata_default): + # return True if the defaults are different, + # False if not, or None to allow the default implementation + # to compare these defaults + return None + + context.configure( + # ... + compare_server_default = my_compare_server_default + ) + + ``inspected_column`` is a dictionary structure as returned by + :meth:`sqlalchemy.engine.reflection.Inspector.get_columns`, whereas + ``metadata_column`` is a :class:`sqlalchemy.schema.Column` from + the local model environment. + + A return value of ``None`` indicates to allow default server default + comparison + to proceed. Note that some backends such as Postgresql actually + execute + the two defaults on the database side to compare for equivalence. + + .. seealso:: + + :paramref:`.EnvironmentContext.configure.compare_type` + + :param include_name: A callable function which is given + the chance to return ``True`` or ``False`` for any database reflected + object based on its name, including database schema names when + the :paramref:`.EnvironmentContext.configure.include_schemas` flag + is set to ``True``. + + The function accepts the following positional arguments: + + * ``name``: the name of the object, such as schema name or table name. + Will be ``None`` when indicating the default schema name of the + database connection. + * ``type``: a string describing the type of object; currently + ``"schema"``, ``"table"``, ``"column"``, ``"index"``, + ``"unique_constraint"``, or ``"foreign_key_constraint"`` + * ``parent_names``: a dictionary of "parent" object names, that are + relative to the name being given. Keys in this dictionary may + include: ``"schema_name"``, ``"table_name"`` or + ``"schema_qualified_table_name"``. + + E.g.:: + + def include_name(name, type_, parent_names): + if type_ == "schema": + return name in ["schema_one", "schema_two"] + else: + return True + + context.configure( + # ... + include_schemas = True, + include_name = include_name + ) + + .. seealso:: + + :ref:`autogenerate_include_hooks` + + :paramref:`.EnvironmentContext.configure.include_object` + + :paramref:`.EnvironmentContext.configure.include_schemas` + + + :param include_object: A callable function which is given + the chance to return ``True`` or ``False`` for any object, + indicating if the given object should be considered in the + autogenerate sweep. + + The function accepts the following positional arguments: + + * ``object``: a :class:`~sqlalchemy.schema.SchemaItem` object such + as a :class:`~sqlalchemy.schema.Table`, + :class:`~sqlalchemy.schema.Column`, + :class:`~sqlalchemy.schema.Index` + :class:`~sqlalchemy.schema.UniqueConstraint`, + or :class:`~sqlalchemy.schema.ForeignKeyConstraint` object + * ``name``: the name of the object. This is typically available + via ``object.name``. + * ``type``: a string describing the type of object; currently + ``"table"``, ``"column"``, ``"index"``, ``"unique_constraint"``, + or ``"foreign_key_constraint"`` + * ``reflected``: ``True`` if the given object was produced based on + table reflection, ``False`` if it's from a local :class:`.MetaData` + object. + * ``compare_to``: the object being compared against, if available, + else ``None``. + + E.g.:: + + def include_object(object, name, type_, reflected, compare_to): + if (type_ == "column" and + not reflected and + object.info.get("skip_autogenerate", False)): + return False + else: + return True + + context.configure( + # ... + include_object = include_object + ) + + For the use case of omitting specific schemas from a target database + when :paramref:`.EnvironmentContext.configure.include_schemas` is + set to ``True``, the :attr:`~sqlalchemy.schema.Table.schema` + attribute can be checked for each :class:`~sqlalchemy.schema.Table` + object passed to the hook, however it is much more efficient + to filter on schemas before reflection of objects takes place + using the :paramref:`.EnvironmentContext.configure.include_name` + hook. + + .. seealso:: + + :ref:`autogenerate_include_hooks` + + :paramref:`.EnvironmentContext.configure.include_name` + + :paramref:`.EnvironmentContext.configure.include_schemas` + + :param render_as_batch: if True, commands which alter elements + within a table will be placed under a ``with batch_alter_table():`` + directive, so that batch migrations will take place. + + .. seealso:: + + :ref:`batch_migrations` + + :param include_schemas: If True, autogenerate will scan across + all schemas located by the SQLAlchemy + :meth:`~sqlalchemy.engine.reflection.Inspector.get_schema_names` + method, and include all differences in tables found across all + those schemas. When using this option, you may want to also + use the :paramref:`.EnvironmentContext.configure.include_name` + parameter to specify a callable which + can filter the tables/schemas that get included. + + .. seealso:: + + :ref:`autogenerate_include_hooks` + + :paramref:`.EnvironmentContext.configure.include_name` + + :paramref:`.EnvironmentContext.configure.include_object` + + :param render_item: Callable that can be used to override how + any schema item, i.e. column, constraint, type, + etc., is rendered for autogenerate. The callable receives a + string describing the type of object, the object, and + the autogen context. If it returns False, the + default rendering method will be used. If it returns None, + the item will not be rendered in the context of a Table + construct, that is, can be used to skip columns or constraints + within op.create_table():: + + def my_render_column(type_, col, autogen_context): + if type_ == "column" and isinstance(col, MySpecialCol): + return repr(col) + else: + return False + + context.configure( + # ... + render_item = my_render_column + ) + + Available values for the type string include: ``"column"``, + ``"primary_key"``, ``"foreign_key"``, ``"unique"``, ``"check"``, + ``"type"``, ``"server_default"``. + + .. seealso:: + + :ref:`autogen_render_types` + + :param upgrade_token: When autogenerate completes, the text of the + candidate upgrade operations will be present in this template + variable when ``script.py.mako`` is rendered. Defaults to + ``upgrades``. + :param downgrade_token: When autogenerate completes, the text of the + candidate downgrade operations will be present in this + template variable when ``script.py.mako`` is rendered. Defaults to + ``downgrades``. + + :param alembic_module_prefix: When autogenerate refers to Alembic + :mod:`alembic.operations` constructs, this prefix will be used + (i.e. ``op.create_table``) Defaults to "``op.``". + Can be ``None`` to indicate no prefix. + + :param sqlalchemy_module_prefix: When autogenerate refers to + SQLAlchemy + :class:`~sqlalchemy.schema.Column` or type classes, this prefix + will be used + (i.e. ``sa.Column("somename", sa.Integer)``) Defaults to "``sa.``". + Can be ``None`` to indicate no prefix. + Note that when dialect-specific types are rendered, autogenerate + will render them using the dialect module name, i.e. ``mssql.BIT()``, + ``postgresql.UUID()``. + + :param user_module_prefix: When autogenerate refers to a SQLAlchemy + type (e.g. :class:`.TypeEngine`) where the module name is not + under the ``sqlalchemy`` namespace, this prefix will be used + within autogenerate. If left at its default of + ``None``, the ``__module__`` attribute of the type is used to + render the import module. It's a good practice to set this + and to have all custom types be available from a fixed module space, + in order to future-proof migration files against reorganizations + in modules. + + .. seealso:: + + :ref:`autogen_module_prefix` + + :param process_revision_directives: a callable function that will + be passed a structure representing the end result of an autogenerate + or plain "revision" operation, which can be manipulated to affect + how the ``alembic revision`` command ultimately outputs new + revision scripts. The structure of the callable is:: + + def process_revision_directives(context, revision, directives): + pass + + The ``directives`` parameter is a Python list containing + a single :class:`.MigrationScript` directive, which represents + the revision file to be generated. This list as well as its + contents may be freely modified to produce any set of commands. + The section :ref:`customizing_revision` shows an example of + doing this. The ``context`` parameter is the + :class:`.MigrationContext` in use, + and ``revision`` is a tuple of revision identifiers representing the + current revision of the database. + + The callable is invoked at all times when the ``--autogenerate`` + option is passed to ``alembic revision``. If ``--autogenerate`` + is not passed, the callable is invoked only if the + ``revision_environment`` variable is set to True in the Alembic + configuration, in which case the given ``directives`` collection + will contain empty :class:`.UpgradeOps` and :class:`.DowngradeOps` + collections for ``.upgrade_ops`` and ``.downgrade_ops``. The + ``--autogenerate`` option itself can be inferred by inspecting + ``context.config.cmd_opts.autogenerate``. + + The callable function may optionally be an instance of + a :class:`.Rewriter` object. This is a helper object that + assists in the production of autogenerate-stream rewriter functions. + + .. seealso:: + + :ref:`customizing_revision` + + :ref:`autogen_rewriter` + + :paramref:`.command.revision.process_revision_directives` + + :param autogenerate_plugins: A list of string names of "plugins" that + should participate in this autogenerate run. Defaults to the list + ``["alembic.autogenerate.*"]``, which indicates that Alembic's default + autogeneration plugins will be used. + + See the section :ref:`plugins_autogenerate` for complete background + on how to use this parameter. + + .. versionadded:: 1.18.0 Added a new plugin system for autogenerate + compare directives. + + .. seealso:: + + :ref:`plugins_autogenerate` - background on enabling/disabling + autogenerate plugins + + :ref:`alembic.plugins.toplevel` - Introduction and documentation + to the plugin system + + Parameters specific to individual backends: + + :param mssql_batch_separator: The "batch separator" which will + be placed between each statement when generating offline SQL Server + migrations. Defaults to ``GO``. Note this is in addition to the + customary semicolon ``;`` at the end of each statement; SQL Server + considers the "batch separator" to denote the end of an + individual statement execution, and cannot group certain + dependent operations in one step. + :param oracle_batch_separator: The "batch separator" which will + be placed between each statement when generating offline + Oracle migrations. Defaults to ``/``. Oracle doesn't add a + semicolon between statements like most other backends. + + """ + opts = self.context_opts + if transactional_ddl is not None: + opts["transactional_ddl"] = transactional_ddl + if output_buffer is not None: + opts["output_buffer"] = output_buffer + elif self.config.output_buffer is not None: + opts["output_buffer"] = self.config.output_buffer + if starting_rev: + opts["starting_rev"] = starting_rev + if tag: + opts["tag"] = tag + if template_args and "template_args" in opts: + opts["template_args"].update(template_args) + opts["transaction_per_migration"] = transaction_per_migration + opts["target_metadata"] = target_metadata + opts["include_name"] = include_name + opts["include_object"] = include_object + opts["include_schemas"] = include_schemas + opts["render_as_batch"] = render_as_batch + opts["upgrade_token"] = upgrade_token + opts["downgrade_token"] = downgrade_token + opts["sqlalchemy_module_prefix"] = sqlalchemy_module_prefix + opts["alembic_module_prefix"] = alembic_module_prefix + opts["user_module_prefix"] = user_module_prefix + opts["literal_binds"] = literal_binds + opts["process_revision_directives"] = process_revision_directives + opts["on_version_apply"] = util.to_tuple(on_version_apply, default=()) + + if autogenerate_plugins is not None: + opts["autogenerate_plugins"] = autogenerate_plugins + + if render_item is not None: + opts["render_item"] = render_item + opts["compare_type"] = compare_type + if compare_server_default is not None: + opts["compare_server_default"] = compare_server_default + opts["script"] = self.script + + opts.update(kw) + + self._migration_context = MigrationContext.configure( + connection=connection, + url=url, + dialect_name=dialect_name, + environment_context=self, + dialect_opts=dialect_opts, + opts=opts, + ) + + def run_migrations(self, **kw: Any) -> None: + """Run migrations as determined by the current command line + configuration + as well as versioning information present (or not) in the current + database connection (if one is present). + + The function accepts optional ``**kw`` arguments. If these are + passed, they are sent directly to the ``upgrade()`` and + ``downgrade()`` + functions within each target revision file. By modifying the + ``script.py.mako`` file so that the ``upgrade()`` and ``downgrade()`` + functions accept arguments, parameters can be passed here so that + contextual information, usually information to identify a particular + database in use, can be passed from a custom ``env.py`` script + to the migration functions. + + This function requires that a :class:`.MigrationContext` has + first been made available via :meth:`.configure`. + + """ + assert self._migration_context is not None + with Operations.context(self._migration_context): + self.get_context().run_migrations(**kw) + + def execute( + self, + sql: Union[Executable, str], + execution_options: Optional[Dict[str, Any]] = None, + ) -> None: + """Execute the given SQL using the current change context. + + The behavior of :meth:`.execute` is the same + as that of :meth:`.Operations.execute`. Please see that + function's documentation for full detail including + caveats and limitations. + + This function requires that a :class:`.MigrationContext` has + first been made available via :meth:`.configure`. + + """ + self.get_context().execute(sql, execution_options=execution_options) + + def static_output(self, text: str) -> None: + """Emit text directly to the "offline" SQL stream. + + Typically this is for emitting comments that + start with --. The statement is not treated + as a SQL execution, no ; or batch separator + is added, etc. + + """ + self.get_context().impl.static_output(text) + + def begin_transaction( + self, + ) -> Union[_ProxyTransaction, ContextManager[None, Optional[bool]]]: + """Return a context manager that will + enclose an operation within a "transaction", + as defined by the environment's offline + and transactional DDL settings. + + e.g.:: + + with context.begin_transaction(): + context.run_migrations() + + :meth:`.begin_transaction` is intended to + "do the right thing" regardless of + calling context: + + * If :meth:`.is_transactional_ddl` is ``False``, + returns a "do nothing" context manager + which otherwise produces no transactional + state or directives. + * If :meth:`.is_offline_mode` is ``True``, + returns a context manager that will + invoke the :meth:`.DefaultImpl.emit_begin` + and :meth:`.DefaultImpl.emit_commit` + methods, which will produce the string + directives ``BEGIN`` and ``COMMIT`` on + the output stream, as rendered by the + target backend (e.g. SQL Server would + emit ``BEGIN TRANSACTION``). + * Otherwise, calls :meth:`sqlalchemy.engine.Connection.begin` + on the current online connection, which + returns a :class:`sqlalchemy.engine.Transaction` + object. This object demarcates a real + transaction and is itself a context manager, + which will roll back if an exception + is raised. + + Note that a custom ``env.py`` script which + has more specific transactional needs can of course + manipulate the :class:`~sqlalchemy.engine.Connection` + directly to produce transactional state in "online" + mode. + + """ + + return self.get_context().begin_transaction() + + def get_context(self) -> MigrationContext: + """Return the current :class:`.MigrationContext` object. + + If :meth:`.EnvironmentContext.configure` has not been + called yet, raises an exception. + + """ + + if self._migration_context is None: + raise Exception("No context has been configured yet.") + return self._migration_context + + def get_bind(self) -> Connection: + """Return the current 'bind'. + + In "online" mode, this is the + :class:`sqlalchemy.engine.Connection` currently being used + to emit SQL to the database. + + This function requires that a :class:`.MigrationContext` + has first been made available via :meth:`.configure`. + + """ + return self.get_context().bind # type: ignore[return-value] + + def get_impl(self) -> DefaultImpl: + return self.get_context().impl diff --git a/venv/lib/python3.12/site-packages/alembic/runtime/migration.py b/venv/lib/python3.12/site-packages/alembic/runtime/migration.py new file mode 100644 index 00000000..3fccf22a --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/runtime/migration.py @@ -0,0 +1,1346 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +from contextlib import contextmanager +from contextlib import nullcontext +import logging +import sys +from typing import Any +from typing import Callable +from typing import cast +from typing import Collection +from typing import Dict +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import Set +from typing import Tuple +from typing import TYPE_CHECKING +from typing import Union + +from sqlalchemy import literal_column +from sqlalchemy import select +from sqlalchemy.engine import Engine +from sqlalchemy.engine import url as sqla_url +from sqlalchemy.engine.strategies import MockEngineStrategy +from typing_extensions import ContextManager + +from .. import ddl +from .. import util +from ..util import sqla_compat +from ..util.compat import EncodedIO + +if TYPE_CHECKING: + from sqlalchemy.engine import Dialect + from sqlalchemy.engine import URL + from sqlalchemy.engine.base import Connection + from sqlalchemy.engine.base import Transaction + from sqlalchemy.engine.mock import MockConnection + from sqlalchemy.sql import Executable + + from .environment import EnvironmentContext + from ..config import Config + from ..script.base import Script + from ..script.base import ScriptDirectory + from ..script.revision import _RevisionOrBase + from ..script.revision import Revision + from ..script.revision import RevisionMap + +log = logging.getLogger(__name__) + + +class _ProxyTransaction: + def __init__(self, migration_context: MigrationContext) -> None: + self.migration_context = migration_context + + @property + def _proxied_transaction(self) -> Optional[Transaction]: + return self.migration_context._transaction + + def rollback(self) -> None: + t = self._proxied_transaction + assert t is not None + t.rollback() + self.migration_context._transaction = None + + def commit(self) -> None: + t = self._proxied_transaction + assert t is not None + t.commit() + self.migration_context._transaction = None + + def __enter__(self) -> _ProxyTransaction: + return self + + def __exit__(self, type_: Any, value: Any, traceback: Any) -> None: + if self._proxied_transaction is not None: + self._proxied_transaction.__exit__(type_, value, traceback) + self.migration_context._transaction = None + + +class MigrationContext: + """Represent the database state made available to a migration + script. + + :class:`.MigrationContext` is the front end to an actual + database connection, or alternatively a string output + stream given a particular database dialect, + from an Alembic perspective. + + When inside the ``env.py`` script, the :class:`.MigrationContext` + is available via the + :meth:`.EnvironmentContext.get_context` method, + which is available at ``alembic.context``:: + + # from within env.py script + from alembic import context + + migration_context = context.get_context() + + For usage outside of an ``env.py`` script, such as for + utility routines that want to check the current version + in the database, the :meth:`.MigrationContext.configure` + method to create new :class:`.MigrationContext` objects. + For example, to get at the current revision in the + database using :meth:`.MigrationContext.get_current_revision`:: + + # in any application, outside of an env.py script + from alembic.migration import MigrationContext + from sqlalchemy import create_engine + + engine = create_engine("postgresql://mydatabase") + conn = engine.connect() + + context = MigrationContext.configure(conn) + current_rev = context.get_current_revision() + + The above context can also be used to produce + Alembic migration operations with an :class:`.Operations` + instance:: + + # in any application, outside of the normal Alembic environment + from alembic.operations import Operations + + op = Operations(context) + op.alter_column("mytable", "somecolumn", nullable=True) + + """ + + def __init__( + self, + dialect: Dialect, + connection: Optional[Connection], + opts: Dict[str, Any], + environment_context: Optional[EnvironmentContext] = None, + ) -> None: + self.environment_context = environment_context + self.opts = opts + self.dialect = dialect + self.script: Optional[ScriptDirectory] = opts.get("script") + as_sql: bool = opts.get("as_sql", False) + transactional_ddl = opts.get("transactional_ddl") + self._transaction_per_migration = opts.get( + "transaction_per_migration", False + ) + self.on_version_apply_callbacks = opts.get("on_version_apply", ()) + self._transaction: Optional[Transaction] = None + + if as_sql: + self.connection = cast( + Optional["Connection"], self._stdout_connection(connection) + ) + assert self.connection is not None + self._in_external_transaction = False + else: + self.connection = connection + self._in_external_transaction = ( + sqla_compat._get_connection_in_transaction(connection) + ) + + self._migrations_fn: Optional[ + Callable[..., Iterable[RevisionStep]] + ] = opts.get("fn") + self.as_sql = as_sql + + self.purge = opts.get("purge", False) + + if "output_encoding" in opts: + self.output_buffer = EncodedIO( + opts.get("output_buffer") + or sys.stdout, # type:ignore[arg-type] + opts["output_encoding"], + ) + else: + self.output_buffer = opts.get( + "output_buffer", sys.stdout + ) # type:ignore[assignment] # noqa: E501 + + self.transactional_ddl = transactional_ddl + + self._user_compare_type = opts.get("compare_type", True) + self._user_compare_server_default = opts.get( + "compare_server_default", False + ) + self.version_table = version_table = opts.get( + "version_table", "alembic_version" + ) + self.version_table_schema = version_table_schema = opts.get( + "version_table_schema", None + ) + + self._start_from_rev: Optional[str] = opts.get("starting_rev") + self.impl = ddl.DefaultImpl.get_by_dialect(dialect)( + dialect, + self.connection, + self.as_sql, + transactional_ddl, + self.output_buffer, + opts, + ) + + self._version = self.impl.version_table_impl( + version_table=version_table, + version_table_schema=version_table_schema, + version_table_pk=opts.get("version_table_pk", True), + ) + + log.info("Context impl %s.", self.impl.__class__.__name__) + if self.as_sql: + log.info("Generating static SQL") + log.info( + "Will assume %s DDL.", + ( + "transactional" + if self.impl.transactional_ddl + else "non-transactional" + ), + ) + + @classmethod + def configure( + cls, + connection: Optional[Connection] = None, + url: Optional[Union[str, URL]] = None, + dialect_name: Optional[str] = None, + dialect: Optional[Dialect] = None, + environment_context: Optional[EnvironmentContext] = None, + dialect_opts: Optional[Dict[str, str]] = None, + opts: Optional[Any] = None, + ) -> MigrationContext: + """Create a new :class:`.MigrationContext`. + + This is a factory method usually called + by :meth:`.EnvironmentContext.configure`. + + :param connection: a :class:`~sqlalchemy.engine.Connection` + to use for SQL execution in "online" mode. When present, + is also used to determine the type of dialect in use. + :param url: a string database url, or a + :class:`sqlalchemy.engine.url.URL` object. + The type of dialect to be used will be derived from this if + ``connection`` is not passed. + :param dialect_name: string name of a dialect, such as + "postgresql", "mssql", etc. The type of dialect to be used will be + derived from this if ``connection`` and ``url`` are not passed. + :param opts: dictionary of options. Most other options + accepted by :meth:`.EnvironmentContext.configure` are passed via + this dictionary. + + """ + if opts is None: + opts = {} + if dialect_opts is None: + dialect_opts = {} + + if connection: + if isinstance(connection, Engine): + raise util.CommandError( + "'connection' argument to configure() is expected " + "to be a sqlalchemy.engine.Connection instance, " + "got %r" % connection, + ) + + dialect = connection.dialect + elif url: + url_obj = sqla_url.make_url(url) + dialect = url_obj.get_dialect()(**dialect_opts) + elif dialect_name: + url_obj = sqla_url.make_url("%s://" % dialect_name) + dialect = url_obj.get_dialect()(**dialect_opts) + elif not dialect: + raise Exception("Connection, url, or dialect_name is required.") + assert dialect is not None + return MigrationContext(dialect, connection, opts, environment_context) + + @contextmanager + def autocommit_block(self) -> Iterator[None]: + """Enter an "autocommit" block, for databases that support AUTOCOMMIT + isolation levels. + + This special directive is intended to support the occasional database + DDL or system operation that specifically has to be run outside of + any kind of transaction block. The PostgreSQL database platform + is the most common target for this style of operation, as many + of its DDL operations must be run outside of transaction blocks, even + though the database overall supports transactional DDL. + + The method is used as a context manager within a migration script, by + calling on :meth:`.Operations.get_context` to retrieve the + :class:`.MigrationContext`, then invoking + :meth:`.MigrationContext.autocommit_block` using the ``with:`` + statement:: + + def upgrade(): + with op.get_context().autocommit_block(): + op.execute("ALTER TYPE mood ADD VALUE 'soso'") + + Above, a PostgreSQL "ALTER TYPE..ADD VALUE" directive is emitted, + which must be run outside of a transaction block at the database level. + The :meth:`.MigrationContext.autocommit_block` method makes use of the + SQLAlchemy ``AUTOCOMMIT`` isolation level setting, which against the + psycogp2 DBAPI corresponds to the ``connection.autocommit`` setting, + to ensure that the database driver is not inside of a DBAPI level + transaction block. + + .. warning:: + + As is necessary, **the database transaction preceding the block is + unconditionally committed**. This means that the run of migrations + preceding the operation will be committed, before the overall + migration operation is complete. + + It is recommended that when an application includes migrations with + "autocommit" blocks, that + :paramref:`.EnvironmentContext.transaction_per_migration` be used + so that the calling environment is tuned to expect short per-file + migrations whether or not one of them has an autocommit block. + + + """ + _in_connection_transaction = self._in_connection_transaction() + + if self.impl.transactional_ddl and self.as_sql: + self.impl.emit_commit() + + elif _in_connection_transaction: + assert self._transaction is not None + + self._transaction.commit() + self._transaction = None + + if not self.as_sql: + assert self.connection is not None + current_level = self.connection.get_isolation_level() + base_connection = self.connection + + # in 1.3 and 1.4 non-future mode, the connection gets switched + # out. we can use the base connection with the new mode + # except that it will not know it's in "autocommit" and will + # emit deprecation warnings when an autocommit action takes + # place. + self.connection = self.impl.connection = ( + base_connection.execution_options(isolation_level="AUTOCOMMIT") + ) + + # sqlalchemy future mode will "autobegin" in any case, so take + # control of that "transaction" here + fake_trans: Optional[Transaction] = self.connection.begin() + else: + fake_trans = None + try: + yield + finally: + if not self.as_sql: + assert self.connection is not None + if fake_trans is not None: + fake_trans.commit() + self.connection.execution_options( + isolation_level=current_level + ) + self.connection = self.impl.connection = base_connection + + if self.impl.transactional_ddl and self.as_sql: + self.impl.emit_begin() + + elif _in_connection_transaction: + assert self.connection is not None + self._transaction = self.connection.begin() + + def begin_transaction( + self, _per_migration: bool = False + ) -> Union[_ProxyTransaction, ContextManager[None, Optional[bool]]]: + """Begin a logical transaction for migration operations. + + This method is used within an ``env.py`` script to demarcate where + the outer "transaction" for a series of migrations begins. Example:: + + def run_migrations_online(): + connectable = create_engine(...) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + Above, :meth:`.MigrationContext.begin_transaction` is used to demarcate + where the outer logical transaction occurs around the + :meth:`.MigrationContext.run_migrations` operation. + + A "Logical" transaction means that the operation may or may not + correspond to a real database transaction. If the target database + supports transactional DDL (or + :paramref:`.EnvironmentContext.configure.transactional_ddl` is true), + the :paramref:`.EnvironmentContext.configure.transaction_per_migration` + flag is not set, and the migration is against a real database + connection (as opposed to using "offline" ``--sql`` mode), a real + transaction will be started. If ``--sql`` mode is in effect, the + operation would instead correspond to a string such as "BEGIN" being + emitted to the string output. + + The returned object is a Python context manager that should only be + used in the context of a ``with:`` statement as indicated above. + The object has no other guaranteed API features present. + + .. seealso:: + + :meth:`.MigrationContext.autocommit_block` + + """ + + if self._in_external_transaction: + return nullcontext() + + if self.impl.transactional_ddl: + transaction_now = _per_migration == self._transaction_per_migration + else: + transaction_now = _per_migration is True + + if not transaction_now: + return nullcontext() + + elif not self.impl.transactional_ddl: + assert _per_migration + + if self.as_sql: + return nullcontext() + else: + # track our own notion of a "transaction block", which must be + # committed when complete. Don't rely upon whether or not the + # SQLAlchemy connection reports as "in transaction"; this + # because SQLAlchemy future connection features autobegin + # behavior, so it may already be in a transaction from our + # emitting of queries like "has_version_table", etc. While we + # could track these operations as well, that leaves open the + # possibility of new operations or other things happening in + # the user environment that still may be triggering + # "autobegin". + + in_transaction = self._transaction is not None + + if in_transaction: + return nullcontext() + else: + assert self.connection is not None + self._transaction = ( + sqla_compat._safe_begin_connection_transaction( + self.connection + ) + ) + return _ProxyTransaction(self) + elif self.as_sql: + + @contextmanager + def begin_commit(): + self.impl.emit_begin() + yield + self.impl.emit_commit() + + return begin_commit() + else: + assert self.connection is not None + self._transaction = sqla_compat._safe_begin_connection_transaction( + self.connection + ) + return _ProxyTransaction(self) + + def get_current_revision(self) -> Optional[str]: + """Return the current revision, usually that which is present + in the ``alembic_version`` table in the database. + + This method intends to be used only for a migration stream that + does not contain unmerged branches in the target database; + if there are multiple branches present, an exception is raised. + The :meth:`.MigrationContext.get_current_heads` should be preferred + over this method going forward in order to be compatible with + branch migration support. + + If this :class:`.MigrationContext` was configured in "offline" + mode, that is with ``as_sql=True``, the ``starting_rev`` + parameter is returned instead, if any. + + """ + heads = self.get_current_heads() + if len(heads) == 0: + return None + elif len(heads) > 1: + raise util.CommandError( + "Version table '%s' has more than one head present; " + "please use get_current_heads()" % self.version_table + ) + else: + return heads[0] + + def get_current_heads(self) -> Tuple[str, ...]: + """Return a tuple of the current 'head versions' that are represented + in the target database. + + For a migration stream without branches, this will be a single + value, synonymous with that of + :meth:`.MigrationContext.get_current_revision`. However when multiple + unmerged branches exist within the target database, the returned tuple + will contain a value for each head. + + If this :class:`.MigrationContext` was configured in "offline" + mode, that is with ``as_sql=True``, the ``starting_rev`` + parameter is returned in a one-length tuple. + + If no version table is present, or if there are no revisions + present, an empty tuple is returned. + + """ + if self.as_sql: + start_from_rev: Any = self._start_from_rev + if start_from_rev == "base": + start_from_rev = None + elif start_from_rev is not None and self.script: + start_from_rev = [ + self.script.get_revision(sfr).revision + for sfr in util.to_list(start_from_rev) + if sfr not in (None, "base") + ] + return util.to_tuple(start_from_rev, default=()) + else: + if self._start_from_rev: + raise util.CommandError( + "Can't specify current_rev to context " + "when using a database connection" + ) + if not self._has_version_table(): + return () + assert self.connection is not None + return tuple( + row[0] + for row in self.connection.execute( + select(self._version.c.version_num) + ) + ) + + def _ensure_version_table(self, purge: bool = False) -> None: + with sqla_compat._ensure_scope_for_ddl(self.connection): + assert self.connection is not None + self._version.create(self.connection, checkfirst=True) + if purge: + assert self.connection is not None + self.connection.execute(self._version.delete()) + + def _has_version_table(self) -> bool: + assert self.connection is not None + return sqla_compat._connectable_has_table( + self.connection, self.version_table, self.version_table_schema + ) + + def stamp(self, script_directory: ScriptDirectory, revision: str) -> None: + """Stamp the version table with a specific revision. + + This method calculates those branches to which the given revision + can apply, and updates those branches as though they were migrated + towards that revision (either up or down). If no current branches + include the revision, it is added as a new branch head. + + """ + heads = self.get_current_heads() + if not self.as_sql and not heads: + self._ensure_version_table() + head_maintainer = HeadMaintainer(self, heads) + for step in script_directory._stamp_revs(revision, heads): + head_maintainer.update_to_step(step) + + def run_migrations(self, **kw: Any) -> None: + r"""Run the migration scripts established for this + :class:`.MigrationContext`, if any. + + The commands in :mod:`alembic.command` will set up a function + that is ultimately passed to the :class:`.MigrationContext` + as the ``fn`` argument. This function represents the "work" + that will be done when :meth:`.MigrationContext.run_migrations` + is called, typically from within the ``env.py`` script of the + migration environment. The "work function" then provides an iterable + of version callables and other version information which + in the case of the ``upgrade`` or ``downgrade`` commands are the + list of version scripts to invoke. Other commands yield nothing, + in the case that a command wants to run some other operation + against the database such as the ``current`` or ``stamp`` commands. + + :param \**kw: keyword arguments here will be passed to each + migration callable, that is the ``upgrade()`` or ``downgrade()`` + method within revision scripts. + + """ + self.impl.start_migrations() + + heads: Tuple[str, ...] + if self.purge: + if self.as_sql: + raise util.CommandError("Can't use --purge with --sql mode") + self._ensure_version_table(purge=True) + heads = () + else: + heads = self.get_current_heads() + + dont_mutate = self.opts.get("dont_mutate", False) + + if not self.as_sql and not heads and not dont_mutate: + self._ensure_version_table() + + head_maintainer = HeadMaintainer(self, heads) + + assert self._migrations_fn is not None + for step in self._migrations_fn(heads, self): + with self.begin_transaction(_per_migration=True): + if self.as_sql and not head_maintainer.heads: + # for offline mode, include a CREATE TABLE from + # the base + assert self.connection is not None + self._version.create(self.connection) + log.info("Running %s", step) + if self.as_sql: + self.impl.static_output( + "-- Running %s" % (step.short_log,) + ) + step.migration_fn(**kw) + + # previously, we wouldn't stamp per migration + # if we were in a transaction, however given the more + # complex model that involves any number of inserts + # and row-targeted updates and deletes, it's simpler for now + # just to run the operations on every version + head_maintainer.update_to_step(step) + for callback in self.on_version_apply_callbacks: + callback( + ctx=self, + step=step.info, + heads=set(head_maintainer.heads), + run_args=kw, + ) + + if self.as_sql and not head_maintainer.heads: + assert self.connection is not None + self._version.drop(self.connection) + + def _in_connection_transaction(self) -> bool: + try: + meth = self.connection.in_transaction # type:ignore[union-attr] + except AttributeError: + return False + else: + return meth() + + def execute( + self, + sql: Union[Executable, str], + execution_options: Optional[Dict[str, Any]] = None, + ) -> None: + """Execute a SQL construct or string statement. + + The underlying execution mechanics are used, that is + if this is "offline mode" the SQL is written to the + output buffer, otherwise the SQL is emitted on + the current SQLAlchemy connection. + + """ + self.impl._exec(sql, execution_options) + + def _stdout_connection( + self, connection: Optional[Connection] + ) -> MockConnection: + def dump(construct, *multiparams, **params): + self.impl._exec(construct) + + return MockEngineStrategy.MockConnection(self.dialect, dump) + + @property + def bind(self) -> Optional[Connection]: + """Return the current "bind". + + In online mode, this is an instance of + :class:`sqlalchemy.engine.Connection`, and is suitable + for ad-hoc execution of any kind of usage described + in SQLAlchemy Core documentation as well as + for usage with the :meth:`sqlalchemy.schema.Table.create` + and :meth:`sqlalchemy.schema.MetaData.create_all` methods + of :class:`~sqlalchemy.schema.Table`, + :class:`~sqlalchemy.schema.MetaData`. + + Note that when "standard output" mode is enabled, + this bind will be a "mock" connection handler that cannot + return results and is only appropriate for a very limited + subset of commands. + + """ + return self.connection + + @property + def config(self) -> Optional[Config]: + """Return the :class:`.Config` used by the current environment, + if any.""" + + if self.environment_context: + return self.environment_context.config + else: + return None + + +class HeadMaintainer: + def __init__(self, context: MigrationContext, heads: Any) -> None: + self.context = context + self.heads = set(heads) + + def _insert_version(self, version: str) -> None: + assert version not in self.heads + self.heads.add(version) + + self.context.impl._exec( + self.context._version.insert().values( + version_num=literal_column("'%s'" % version) + ) + ) + + def _delete_version(self, version: str) -> None: + self.heads.remove(version) + + ret = self.context.impl._exec( + self.context._version.delete().where( + self.context._version.c.version_num + == literal_column("'%s'" % version) + ) + ) + + if ( + not self.context.as_sql + and self.context.dialect.supports_sane_rowcount + and ret is not None + and ret.rowcount != 1 + ): + raise util.CommandError( + "Online migration expected to match one " + "row when deleting '%s' in '%s'; " + "%d found" + % (version, self.context.version_table, ret.rowcount) + ) + + def _update_version(self, from_: str, to_: str) -> None: + assert to_ not in self.heads + self.heads.remove(from_) + self.heads.add(to_) + + ret = self.context.impl._exec( + self.context._version.update() + .values(version_num=literal_column("'%s'" % to_)) + .where( + self.context._version.c.version_num + == literal_column("'%s'" % from_) + ) + ) + + if ( + not self.context.as_sql + and self.context.dialect.supports_sane_rowcount + and ret is not None + and ret.rowcount != 1 + ): + raise util.CommandError( + "Online migration expected to match one " + "row when updating '%s' to '%s' in '%s'; " + "%d found" + % (from_, to_, self.context.version_table, ret.rowcount) + ) + + def update_to_step(self, step: Union[RevisionStep, StampStep]) -> None: + if step.should_delete_branch(self.heads): + vers = step.delete_version_num + log.debug("branch delete %s", vers) + self._delete_version(vers) + elif step.should_create_branch(self.heads): + vers = step.insert_version_num + log.debug("new branch insert %s", vers) + self._insert_version(vers) + elif step.should_merge_branches(self.heads): + # delete revs, update from rev, update to rev + ( + delete_revs, + update_from_rev, + update_to_rev, + ) = step.merge_branch_idents(self.heads) + log.debug( + "merge, delete %s, update %s to %s", + delete_revs, + update_from_rev, + update_to_rev, + ) + for delrev in delete_revs: + self._delete_version(delrev) + self._update_version(update_from_rev, update_to_rev) + elif step.should_unmerge_branches(self.heads): + ( + update_from_rev, + update_to_rev, + insert_revs, + ) = step.unmerge_branch_idents(self.heads) + log.debug( + "unmerge, insert %s, update %s to %s", + insert_revs, + update_from_rev, + update_to_rev, + ) + for insrev in insert_revs: + self._insert_version(insrev) + self._update_version(update_from_rev, update_to_rev) + else: + from_, to_ = step.update_version_num(self.heads) + log.debug("update %s to %s", from_, to_) + self._update_version(from_, to_) + + +class MigrationInfo: + """Exposes information about a migration step to a callback listener. + + The :class:`.MigrationInfo` object is available exclusively for the + benefit of the :paramref:`.EnvironmentContext.on_version_apply` + callback hook. + + """ + + is_upgrade: bool + """True/False: indicates whether this operation ascends or descends the + version tree.""" + + is_stamp: bool + """True/False: indicates whether this operation is a stamp (i.e. whether + it results in any actual database operations).""" + + up_revision_id: Optional[str] + """Version string corresponding to :attr:`.Revision.revision`. + + In the case of a stamp operation, it is advised to use the + :attr:`.MigrationInfo.up_revision_ids` tuple as a stamp operation can + make a single movement from one or more branches down to a single + branchpoint, in which case there will be multiple "up" revisions. + + .. seealso:: + + :attr:`.MigrationInfo.up_revision_ids` + + """ + + up_revision_ids: Tuple[str, ...] + """Tuple of version strings corresponding to :attr:`.Revision.revision`. + + In the majority of cases, this tuple will be a single value, synonymous + with the scalar value of :attr:`.MigrationInfo.up_revision_id`. + It can be multiple revision identifiers only in the case of an + ``alembic stamp`` operation which is moving downwards from multiple + branches down to their common branch point. + + """ + + down_revision_ids: Tuple[str, ...] + """Tuple of strings representing the base revisions of this migration step. + + If empty, this represents a root revision; otherwise, the first item + corresponds to :attr:`.Revision.down_revision`, and the rest are inferred + from dependencies. + """ + + revision_map: RevisionMap + """The revision map inside of which this operation occurs.""" + + def __init__( + self, + revision_map: RevisionMap, + is_upgrade: bool, + is_stamp: bool, + up_revisions: Union[str, Tuple[str, ...]], + down_revisions: Union[str, Tuple[str, ...]], + ) -> None: + self.revision_map = revision_map + self.is_upgrade = is_upgrade + self.is_stamp = is_stamp + self.up_revision_ids = util.to_tuple(up_revisions, default=()) + if self.up_revision_ids: + self.up_revision_id = self.up_revision_ids[0] + else: + # this should never be the case with + # "upgrade", "downgrade", or "stamp" as we are always + # measuring movement in terms of at least one upgrade version + self.up_revision_id = None + self.down_revision_ids = util.to_tuple(down_revisions, default=()) + + @property + def is_migration(self) -> bool: + """True/False: indicates whether this operation is a migration. + + At present this is true if and only the migration is not a stamp. + If other operation types are added in the future, both this attribute + and :attr:`~.MigrationInfo.is_stamp` will be false. + """ + return not self.is_stamp + + @property + def source_revision_ids(self) -> Tuple[str, ...]: + """Active revisions before this migration step is applied.""" + return ( + self.down_revision_ids if self.is_upgrade else self.up_revision_ids + ) + + @property + def destination_revision_ids(self) -> Tuple[str, ...]: + """Active revisions after this migration step is applied.""" + return ( + self.up_revision_ids if self.is_upgrade else self.down_revision_ids + ) + + @property + def up_revision(self) -> Optional[Revision]: + """Get :attr:`~.MigrationInfo.up_revision_id` as + a :class:`.Revision`. + + """ + return self.revision_map.get_revision(self.up_revision_id) + + @property + def up_revisions(self) -> Tuple[Optional[_RevisionOrBase], ...]: + """Get :attr:`~.MigrationInfo.up_revision_ids` as a + :class:`.Revision`.""" + return self.revision_map.get_revisions(self.up_revision_ids) + + @property + def down_revisions(self) -> Tuple[Optional[_RevisionOrBase], ...]: + """Get :attr:`~.MigrationInfo.down_revision_ids` as a tuple of + :class:`Revisions <.Revision>`.""" + return self.revision_map.get_revisions(self.down_revision_ids) + + @property + def source_revisions(self) -> Tuple[Optional[_RevisionOrBase], ...]: + """Get :attr:`~MigrationInfo.source_revision_ids` as a tuple of + :class:`Revisions <.Revision>`.""" + return self.revision_map.get_revisions(self.source_revision_ids) + + @property + def destination_revisions(self) -> Tuple[Optional[_RevisionOrBase], ...]: + """Get :attr:`~MigrationInfo.destination_revision_ids` as a tuple of + :class:`Revisions <.Revision>`.""" + return self.revision_map.get_revisions(self.destination_revision_ids) + + +class MigrationStep: + from_revisions_no_deps: Tuple[str, ...] + to_revisions_no_deps: Tuple[str, ...] + is_upgrade: bool + migration_fn: Any + + if TYPE_CHECKING: + + @property + def doc(self) -> Optional[str]: ... + + @property + def name(self) -> str: + return self.migration_fn.__name__ + + @classmethod + def upgrade_from_script( + cls, revision_map: RevisionMap, script: Script + ) -> RevisionStep: + return RevisionStep(revision_map, script, True) + + @classmethod + def downgrade_from_script( + cls, revision_map: RevisionMap, script: Script + ) -> RevisionStep: + return RevisionStep(revision_map, script, False) + + @property + def is_downgrade(self) -> bool: + return not self.is_upgrade + + @property + def short_log(self) -> str: + return "%s %s -> %s" % ( + self.name, + util.format_as_comma(self.from_revisions_no_deps), + util.format_as_comma(self.to_revisions_no_deps), + ) + + def __str__(self): + if self.doc: + return "%s %s -> %s, %s" % ( + self.name, + util.format_as_comma(self.from_revisions_no_deps), + util.format_as_comma(self.to_revisions_no_deps), + self.doc, + ) + else: + return self.short_log + + +class RevisionStep(MigrationStep): + def __init__( + self, revision_map: RevisionMap, revision: Script, is_upgrade: bool + ) -> None: + self.revision_map = revision_map + self.revision = revision + self.is_upgrade = is_upgrade + if is_upgrade: + self.migration_fn = revision.module.upgrade + else: + self.migration_fn = revision.module.downgrade + + def __repr__(self): + return "RevisionStep(%r, is_upgrade=%r)" % ( + self.revision.revision, + self.is_upgrade, + ) + + def __eq__(self, other: object) -> bool: + return ( + isinstance(other, RevisionStep) + and other.revision == self.revision + and self.is_upgrade == other.is_upgrade + ) + + @property + def doc(self) -> Optional[str]: + return self.revision.doc + + @property + def from_revisions(self) -> Tuple[str, ...]: + if self.is_upgrade: + return self.revision._normalized_down_revisions + else: + return (self.revision.revision,) + + @property + def from_revisions_no_deps( # type:ignore[override] + self, + ) -> Tuple[str, ...]: + if self.is_upgrade: + return self.revision._versioned_down_revisions + else: + return (self.revision.revision,) + + @property + def to_revisions(self) -> Tuple[str, ...]: + if self.is_upgrade: + return (self.revision.revision,) + else: + return self.revision._normalized_down_revisions + + @property + def to_revisions_no_deps( # type:ignore[override] + self, + ) -> Tuple[str, ...]: + if self.is_upgrade: + return (self.revision.revision,) + else: + return self.revision._versioned_down_revisions + + @property + def _has_scalar_down_revision(self) -> bool: + return len(self.revision._normalized_down_revisions) == 1 + + def should_delete_branch(self, heads: Set[str]) -> bool: + """A delete is when we are a. in a downgrade and b. + we are going to the "base" or we are going to a version that + is implied as a dependency on another version that is remaining. + + """ + if not self.is_downgrade: + return False + + if self.revision.revision not in heads: + return False + + downrevs = self.revision._normalized_down_revisions + + if not downrevs: + # is a base + return True + else: + # determine what the ultimate "to_revisions" for an + # unmerge would be. If there are none, then we're a delete. + to_revisions = self._unmerge_to_revisions(heads) + return not to_revisions + + def merge_branch_idents( + self, heads: Set[str] + ) -> Tuple[List[str], str, str]: + other_heads = set(heads).difference(self.from_revisions) + + if other_heads: + ancestors = { + r.revision + for r in self.revision_map._get_ancestor_nodes( + self.revision_map.get_revisions(other_heads), check=False + ) + } + from_revisions = list( + set(self.from_revisions).difference(ancestors) + ) + else: + from_revisions = list(self.from_revisions) + + return ( + # delete revs, update from rev, update to rev + list(from_revisions[0:-1]), + from_revisions[-1], + self.to_revisions[0], + ) + + def _unmerge_to_revisions(self, heads: Set[str]) -> Tuple[str, ...]: + other_heads = set(heads).difference([self.revision.revision]) + if other_heads: + ancestors = { + r.revision + for r in self.revision_map._get_ancestor_nodes( + self.revision_map.get_revisions(other_heads), check=False + ) + } + return tuple(set(self.to_revisions).difference(ancestors)) + else: + # for each revision we plan to return, compute its ancestors + # (excluding self), and remove those from the final output since + # they are already accounted for. + ancestors = { + r.revision + for to_revision in self.to_revisions + for r in self.revision_map._get_ancestor_nodes( + self.revision_map.get_revisions(to_revision), check=False + ) + if r.revision != to_revision + } + return tuple(set(self.to_revisions).difference(ancestors)) + + def unmerge_branch_idents( + self, heads: Set[str] + ) -> Tuple[str, str, Tuple[str, ...]]: + to_revisions = self._unmerge_to_revisions(heads) + + return ( + # update from rev, update to rev, insert revs + self.from_revisions[0], + to_revisions[-1], + to_revisions[0:-1], + ) + + def should_create_branch(self, heads: Set[str]) -> bool: + if not self.is_upgrade: + return False + + downrevs = self.revision._normalized_down_revisions + + if not downrevs: + # is a base + return True + else: + # none of our downrevs are present, so... + # we have to insert our version. This is true whether + # or not there is only one downrev, or multiple (in the latter + # case, we're a merge point.) + if not heads.intersection(downrevs): + return True + else: + return False + + def should_merge_branches(self, heads: Set[str]) -> bool: + if not self.is_upgrade: + return False + + downrevs = self.revision._normalized_down_revisions + + if len(downrevs) > 1 and len(heads.intersection(downrevs)) > 1: + return True + + return False + + def should_unmerge_branches(self, heads: Set[str]) -> bool: + if not self.is_downgrade: + return False + + downrevs = self.revision._normalized_down_revisions + + if self.revision.revision in heads and len(downrevs) > 1: + return True + + return False + + def update_version_num(self, heads: Set[str]) -> Tuple[str, str]: + if not self._has_scalar_down_revision: + downrev = heads.intersection( + self.revision._normalized_down_revisions + ) + assert ( + len(downrev) == 1 + ), "Can't do an UPDATE because downrevision is ambiguous" + down_revision = list(downrev)[0] + else: + down_revision = self.revision._normalized_down_revisions[0] + + if self.is_upgrade: + return down_revision, self.revision.revision + else: + return self.revision.revision, down_revision + + @property + def delete_version_num(self) -> str: + return self.revision.revision + + @property + def insert_version_num(self) -> str: + return self.revision.revision + + @property + def info(self) -> MigrationInfo: + return MigrationInfo( + revision_map=self.revision_map, + up_revisions=self.revision.revision, + down_revisions=self.revision._normalized_down_revisions, + is_upgrade=self.is_upgrade, + is_stamp=False, + ) + + +class StampStep(MigrationStep): + def __init__( + self, + from_: Optional[Union[str, Collection[str]]], + to_: Optional[Union[str, Collection[str]]], + is_upgrade: bool, + branch_move: bool, + revision_map: Optional[RevisionMap] = None, + ) -> None: + self.from_: Tuple[str, ...] = util.to_tuple(from_, default=()) + self.to_: Tuple[str, ...] = util.to_tuple(to_, default=()) + self.is_upgrade = is_upgrade + self.branch_move = branch_move + self.migration_fn = self.stamp_revision + self.revision_map = revision_map + + doc: Optional[str] = None + + def stamp_revision(self, **kw: Any) -> None: + return None + + def __eq__(self, other): + return ( + isinstance(other, StampStep) + and other.from_revisions == self.from_revisions + and other.to_revisions == self.to_revisions + and other.branch_move == self.branch_move + and self.is_upgrade == other.is_upgrade + ) + + @property + def from_revisions(self): + return self.from_ + + @property + def to_revisions(self) -> Tuple[str, ...]: + return self.to_ + + @property + def from_revisions_no_deps( # type:ignore[override] + self, + ) -> Tuple[str, ...]: + return self.from_ + + @property + def to_revisions_no_deps( # type:ignore[override] + self, + ) -> Tuple[str, ...]: + return self.to_ + + @property + def delete_version_num(self) -> str: + assert len(self.from_) == 1 + return self.from_[0] + + @property + def insert_version_num(self) -> str: + assert len(self.to_) == 1 + return self.to_[0] + + def update_version_num(self, heads: Set[str]) -> Tuple[str, str]: + assert len(self.from_) == 1 + assert len(self.to_) == 1 + return self.from_[0], self.to_[0] + + def merge_branch_idents( + self, heads: Union[Set[str], List[str]] + ) -> Union[Tuple[List[Any], str, str], Tuple[List[str], str, str]]: + return ( + # delete revs, update from rev, update to rev + list(self.from_[0:-1]), + self.from_[-1], + self.to_[0], + ) + + def unmerge_branch_idents( + self, heads: Set[str] + ) -> Tuple[str, str, List[str]]: + return ( + # update from rev, update to rev, insert revs + self.from_[0], + self.to_[-1], + list(self.to_[0:-1]), + ) + + def should_delete_branch(self, heads: Set[str]) -> bool: + # TODO: we probably need to look for self.to_ inside of heads, + # in a similar manner as should_create_branch, however we have + # no tests for this yet (stamp downgrades w/ branches) + return self.is_downgrade and self.branch_move + + def should_create_branch(self, heads: Set[str]) -> Union[Set[str], bool]: + return ( + self.is_upgrade + and (self.branch_move or set(self.from_).difference(heads)) + and set(self.to_).difference(heads) + ) + + def should_merge_branches(self, heads: Set[str]) -> bool: + return len(self.from_) > 1 + + def should_unmerge_branches(self, heads: Set[str]) -> bool: + return len(self.to_) > 1 + + @property + def info(self) -> MigrationInfo: + up, down = ( + (self.to_, self.from_) + if self.is_upgrade + else (self.from_, self.to_) + ) + assert self.revision_map is not None + return MigrationInfo( + revision_map=self.revision_map, + up_revisions=up, + down_revisions=down, + is_upgrade=self.is_upgrade, + is_stamp=True, + ) diff --git a/venv/lib/python3.12/site-packages/alembic/runtime/plugins.py b/venv/lib/python3.12/site-packages/alembic/runtime/plugins.py new file mode 100644 index 00000000..be1d590f --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/runtime/plugins.py @@ -0,0 +1,179 @@ +from __future__ import annotations + +from importlib import metadata +import logging +import re +from types import ModuleType +from typing import Callable +from typing import Pattern +from typing import TYPE_CHECKING + +from .. import util +from ..util import DispatchPriority +from ..util import PriorityDispatcher + +if TYPE_CHECKING: + from ..util import PriorityDispatchResult + +_all_plugins = {} + + +log = logging.getLogger(__name__) + + +class Plugin: + """Describe a series of functions that are pulled in as a plugin. + + This is initially to provide for portable lists of autogenerate + comparison functions, however the setup for a plugin can run any + other kinds of global registration as well. + + .. versionadded:: 1.18.0 + + """ + + def __init__(self, name: str): + self.name = name + log.info("setup plugin %s", name) + if name in _all_plugins: + raise ValueError(f"A plugin named {name} is already registered") + _all_plugins[name] = self + self.autogenerate_comparators = PriorityDispatcher() + + def remove(self) -> None: + """remove this plugin""" + + del _all_plugins[self.name] + + def add_autogenerate_comparator( + self, + fn: Callable[..., PriorityDispatchResult], + compare_target: str, + compare_element: str | None = None, + *, + qualifier: str = "default", + priority: DispatchPriority = DispatchPriority.MEDIUM, + ) -> None: + """Register an autogenerate comparison function. + + See the section :ref:`plugins_registering_autogenerate` for detailed + examples on how to use this method. + + :param fn: The comparison function to register. The function receives + arguments specific to the type of comparison being performed and + should return a :class:`.PriorityDispatchResult` value. + + :param compare_target: The type of comparison being performed + (e.g., ``"table"``, ``"column"``, ``"type"``). + + :param compare_element: Optional sub-element being compared within + the target type. + + :param qualifier: Database dialect qualifier. Use ``"default"`` for + all dialects, or specify a dialect name like ``"postgresql"`` to + register a dialect-specific handler. Defaults to ``"default"``. + + :param priority: Execution priority for this comparison function. + Functions are executed in priority order from + :attr:`.DispatchPriority.FIRST` to :attr:`.DispatchPriority.LAST`. + Defaults to :attr:`.DispatchPriority.MEDIUM`. + + """ + self.autogenerate_comparators.dispatch_for( + compare_target, + subgroup=compare_element, + priority=priority, + qualifier=qualifier, + )(fn) + + @classmethod + def populate_autogenerate_priority_dispatch( + cls, comparators: PriorityDispatcher, include_plugins: list[str] + ) -> None: + """Populate all current autogenerate comparison functions into + a given PriorityDispatcher.""" + + exclude: set[Pattern[str]] = set() + include: dict[str, Pattern[str]] = {} + + matched_expressions: set[str] = set() + + for name in include_plugins: + if name.startswith("~"): + exclude.add(_make_re(name[1:])) + else: + include[name] = _make_re(name) + + for plugin in _all_plugins.values(): + if any(excl.match(plugin.name) for excl in exclude): + continue + + include_matches = [ + incl for incl in include if include[incl].match(plugin.name) + ] + if not include_matches: + continue + else: + matched_expressions.update(include_matches) + + log.info("setting up autogenerate plugin %s", plugin.name) + comparators.populate_with(plugin.autogenerate_comparators) + + never_matched = set(include).difference(matched_expressions) + if never_matched: + raise util.CommandError( + f"Did not locate plugins: {', '.join(never_matched)}" + ) + + @classmethod + def setup_plugin_from_module(cls, module: ModuleType, name: str) -> None: + """Call the ``setup()`` function of a plugin module, identified by + passing the module object itself. + + E.g.:: + + from alembic.runtime.plugins import Plugin + import myproject.alembic_plugin + + # Register the plugin manually + Plugin.setup_plugin_from_module( + myproject.alembic_plugin, + "myproject.custom_operations" + ) + + This will generate a new :class:`.Plugin` object with the given + name, which will register itself in the global list of plugins. + Then the module's ``setup()`` function is invoked, passing that + :class:`.Plugin` object. + + This exact process is invoked automatically at import time for any + plugin module that is published via the ``alembic.plugins`` entrypoint. + + """ + module.setup(Plugin(name)) + + +def _make_re(name: str) -> Pattern[str]: + tokens = name.split(".") + + reg = r"" + for token in tokens: + if token == "*": + reg += r"\..+?" + elif token.isidentifier(): + reg += r"\." + token + else: + raise ValueError(f"Invalid plugin expression {name!r}") + + # omit leading r'\.' + return re.compile(f"^{reg[2:]}$") + + +def _setup() -> None: + # setup third party plugins + for entrypoint in metadata.entry_points(group="alembic.plugins"): + for mod in entrypoint.load(): + Plugin.setup_plugin_from_module(mod, entrypoint.name) + + +_setup() diff --git a/venv/lib/python3.12/site-packages/alembic/script/__init__.py b/venv/lib/python3.12/site-packages/alembic/script/__init__.py new file mode 100644 index 00000000..d78f3f1d --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/script/__init__.py @@ -0,0 +1,4 @@ +from .base import Script +from .base import ScriptDirectory + +__all__ = ["ScriptDirectory", "Script"] diff --git a/venv/lib/python3.12/site-packages/alembic/script/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/script/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d2bad5af7a9157c4c275bcc164c67cd27e03196 GIT binary patch literal 304 zcmX@j%ge<81dVFEFQ_cZ$j>v@Gc?jK&MZmQ1?tI8Ob2R9%t_5n%1qWT20KYVK0Y%qvm`!Vub}c4 whfQvNN@-52T@ff0KyEJ<1ri^a85tSxGDzHIP<+5GGb7^ymwF?65eHBP0EBK)#sB~S literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/script/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/script/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b11f1fcbb3beadf152189d24b78e3a6b38ee8a6f GIT binary patch literal 42526 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

ragxeK^kAk=WauH0^hUU?;R6H14 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 Kz+23?%l`$WsX;OT literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/util/__pycache__/messaging.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/util/__pycache__/messaging.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e539aa2dce952c73514021da8d52958b7499c454 GIT binary patch literal 5144 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 Q1%1`x>e|MC)q$b^51nL36#xJL literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/util/__pycache__/pyfiles.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/util/__pycache__/pyfiles.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e7c0b738a940145f7d7d8c282807a7770ede8cd GIT binary patch literal 6377 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/util/__pycache__/sqla_compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/util/__pycache__/sqla_compat.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97b7ff7f3790399b4c65f6ac3f242c44266ea4ca GIT binary patch literal 20840 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/util/compat.py b/venv/lib/python3.12/site-packages/alembic/util/compat.py new file mode 100644 index 00000000..527ffdc9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/util/compat.py @@ -0,0 +1,130 @@ +# mypy: no-warn-unused-ignores + +from __future__ import annotations + +from configparser import ConfigParser +from importlib import metadata +from importlib.metadata import EntryPoint +import io +import os +from pathlib import Path +import sys +import typing +from typing import Any +from typing import Iterator +from typing import Sequence + +if True: + # zimports hack for too-long names + from sqlalchemy.util import ( # noqa: F401 + inspect_getfullargspec as inspect_getfullargspec, + ) + from sqlalchemy.util.compat import ( # noqa: F401 + inspect_formatargspec as inspect_formatargspec, + ) + +is_posix = os.name == "posix" + +py314 = sys.version_info >= (3, 14) +py313 = sys.version_info >= (3, 13) +py312 = sys.version_info >= (3, 12) +py311 = sys.version_info >= (3, 11) + + +# produce a wrapper that allows encoded text to stream +# into a given buffer, but doesn't close it. +# not sure of a more idiomatic approach to this. +class EncodedIO(io.TextIOWrapper): + def close(self) -> None: + pass + + +if py311: + import tomllib as tomllib +else: + import tomli as tomllib # type: ignore # noqa + + +if py312: + + def path_walk( + path: Path, *, top_down: bool = True + ) -> Iterator[tuple[Path, list[str], list[str]]]: + return Path.walk(path) + + def path_relative_to( + path: Path, other: Path, *, walk_up: bool = False + ) -> Path: + return path.relative_to(other, walk_up=walk_up) + +else: + + def path_walk( + path: Path, *, top_down: bool = True + ) -> Iterator[tuple[Path, list[str], list[str]]]: + for root, dirs, files in os.walk(path, topdown=top_down): + yield Path(root), dirs, files + + def path_relative_to( + path: Path, other: Path, *, walk_up: bool = False + ) -> Path: + """ + Calculate the relative path of 'path' with respect to 'other', + optionally allowing 'path' to be outside the subtree of 'other'. + + OK I used AI for this, sorry + + """ + try: + return path.relative_to(other) + except ValueError: + if walk_up: + other_ancestors = list(other.parents) + [other] + for ancestor in other_ancestors: + try: + return path.relative_to(ancestor) + except ValueError: + continue + raise ValueError( + f"{path} is not in the same subtree as {other}" + ) + else: + raise + + +def importlib_metadata_get(group: str) -> Sequence[EntryPoint]: + """provide a facade for metadata.entry_points(). + + This is no longer a "compat" function as of Python 3.10, however + the function is widely referenced in the test suite and elsewhere so is + still in this module for compatibility reasons. + + """ + return metadata.entry_points().select(group=group) + + +def formatannotation_fwdref( + annotation: Any, base_module: Any | None = None +) -> str: + """vendored from python 3.7""" + # copied over _formatannotation from sqlalchemy 2.0 + + if isinstance(annotation, str): + return annotation + + if getattr(annotation, "__module__", None) == "typing": + return repr(annotation).replace("typing.", "").replace("~", "") + if isinstance(annotation, type): + if annotation.__module__ in ("builtins", base_module): + return repr(annotation.__qualname__) + return annotation.__module__ + "." + annotation.__qualname__ + elif isinstance(annotation, typing.TypeVar): + return repr(annotation).replace("~", "") + return repr(annotation).replace("~", "") + + +def read_config_parser( + file_config: ConfigParser, + file_argument: list[str | os.PathLike[str]], +) -> list[str]: + return file_config.read(file_argument, encoding="locale") diff --git a/venv/lib/python3.12/site-packages/alembic/util/editor.py b/venv/lib/python3.12/site-packages/alembic/util/editor.py new file mode 100644 index 00000000..f1d1557f --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/util/editor.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import os +from os.path import exists +from os.path import join +from os.path import splitext +from subprocess import check_call +from typing import Dict +from typing import List +from typing import Mapping +from typing import Optional + +from .compat import is_posix +from .exc import CommandError + + +def open_in_editor( + filename: str, environ: Optional[Dict[str, str]] = None +) -> None: + """ + Opens the given file in a text editor. If the environment variable + ``EDITOR`` is set, this is taken as preference. + + Otherwise, a list of commonly installed editors is tried. + + If no editor matches, an :py:exc:`OSError` is raised. + + :param filename: The filename to open. Will be passed verbatim to the + editor command. + :param environ: An optional drop-in replacement for ``os.environ``. Used + mainly for testing. + """ + env = os.environ if environ is None else environ + try: + editor = _find_editor(env) + check_call([editor, filename]) + except Exception as exc: + raise CommandError("Error executing editor (%s)" % (exc,)) from exc + + +def _find_editor(environ: Mapping[str, str]) -> str: + candidates = _default_editors() + for i, var in enumerate(("EDITOR", "VISUAL")): + if var in environ: + user_choice = environ[var] + if exists(user_choice): + return user_choice + if os.sep not in user_choice: + candidates.insert(i, user_choice) + + for candidate in candidates: + path = _find_executable(candidate, environ) + if path is not None: + return path + raise OSError( + "No suitable editor found. Please set the " + '"EDITOR" or "VISUAL" environment variables' + ) + + +def _find_executable( + candidate: str, environ: Mapping[str, str] +) -> Optional[str]: + # Assuming this is on the PATH, we need to determine it's absolute + # location. Otherwise, ``check_call`` will fail + if not is_posix and splitext(candidate)[1] != ".exe": + candidate += ".exe" + for path in environ.get("PATH", "").split(os.pathsep): + value = join(path, candidate) + if exists(value): + return value + return None + + +def _default_editors() -> List[str]: + # Look for an editor. Prefer the user's choice by env-var, fall back to + # most commonly installed editor (nano/vim) + if is_posix: + return ["sensible-editor", "editor", "nano", "vim", "code"] + else: + return ["code.exe", "notepad++.exe", "notepad.exe"] diff --git a/venv/lib/python3.12/site-packages/alembic/util/exc.py b/venv/lib/python3.12/site-packages/alembic/util/exc.py new file mode 100644 index 00000000..4658f782 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/util/exc.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +from typing import Any +from typing import List +from typing import Tuple +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from alembic.autogenerate import RevisionContext + + +class CommandError(Exception): + """Base command error for all exceptions""" + + +class DatabaseNotAtHead(CommandError): + """Indicates the database is not at current head revisions. + + Raised by the :func:`.command.current` command when the + :paramref:`.command.current.check_heads` parameter is used. + + .. versionadded:: 1.17.1 + + """ + + +class AutogenerateDiffsDetected(CommandError): + """Raised when diffs were detected by the :func:`.command.check` + command. + + .. versionadded:: 1.9.0 + + """ + + def __init__( + self, + message: str, + revision_context: RevisionContext, + diffs: List[Tuple[Any, ...]], + ) -> None: + super().__init__(message) + self.revision_context = revision_context + self.diffs = diffs diff --git a/venv/lib/python3.12/site-packages/alembic/util/langhelpers.py b/venv/lib/python3.12/site-packages/alembic/util/langhelpers.py new file mode 100644 index 00000000..cf0df239 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/util/langhelpers.py @@ -0,0 +1,445 @@ +from __future__ import annotations + +import collections +from collections.abc import Iterable +import enum +import textwrap +from typing import Any +from typing import Callable +from typing import cast +from typing import Dict +from typing import List +from typing import Mapping +from typing import MutableMapping +from typing import NoReturn +from typing import Optional +from typing import overload +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import Type +from typing import TypeVar +import uuid +import warnings + +from sqlalchemy.util import asbool as asbool # noqa: F401 +from sqlalchemy.util import immutabledict as immutabledict # noqa: F401 +from sqlalchemy.util import to_list as to_list # noqa: F401 +from sqlalchemy.util import unique_list as unique_list + +from .compat import inspect_getfullargspec + +if True: + # zimports workaround :( + from sqlalchemy.util import ( # noqa: F401 + memoized_property as memoized_property, + ) + + +EMPTY_DICT: Mapping[Any, Any] = immutabledict() +_T = TypeVar("_T", bound=Any) + +_C = TypeVar("_C", bound=Callable[..., Any]) + + +class _ModuleClsMeta(type): + def __setattr__(cls, key: str, value: Callable[..., Any]) -> None: + super().__setattr__(key, value) + cls._update_module_proxies(key) # type: ignore + + +class ModuleClsProxy(metaclass=_ModuleClsMeta): + """Create module level proxy functions for the + methods on a given class. + + The functions will have a compatible signature + as the methods. + + """ + + _setups: Dict[ + Type[Any], + Tuple[ + Set[str], + List[Tuple[MutableMapping[str, Any], MutableMapping[str, Any]]], + ], + ] = collections.defaultdict(lambda: (set(), [])) + + @classmethod + def _update_module_proxies(cls, name: str) -> None: + attr_names, modules = cls._setups[cls] + for globals_, locals_ in modules: + cls._add_proxied_attribute(name, globals_, locals_, attr_names) + + def _install_proxy(self) -> None: + attr_names, modules = self._setups[self.__class__] + for globals_, locals_ in modules: + globals_["_proxy"] = self + for attr_name in attr_names: + globals_[attr_name] = getattr(self, attr_name) + + def _remove_proxy(self) -> None: + attr_names, modules = self._setups[self.__class__] + for globals_, locals_ in modules: + globals_["_proxy"] = None + for attr_name in attr_names: + del globals_[attr_name] + + @classmethod + def create_module_class_proxy( + cls, + globals_: MutableMapping[str, Any], + locals_: MutableMapping[str, Any], + ) -> None: + attr_names, modules = cls._setups[cls] + modules.append((globals_, locals_)) + cls._setup_proxy(globals_, locals_, attr_names) + + @classmethod + def _setup_proxy( + cls, + globals_: MutableMapping[str, Any], + locals_: MutableMapping[str, Any], + attr_names: Set[str], + ) -> None: + for methname in dir(cls): + cls._add_proxied_attribute(methname, globals_, locals_, attr_names) + + @classmethod + def _add_proxied_attribute( + cls, + methname: str, + globals_: MutableMapping[str, Any], + locals_: MutableMapping[str, Any], + attr_names: Set[str], + ) -> None: + if not methname.startswith("_"): + meth = getattr(cls, methname) + if callable(meth): + locals_[methname] = cls._create_method_proxy( + methname, globals_, locals_ + ) + else: + attr_names.add(methname) + + @classmethod + def _create_method_proxy( + cls, + name: str, + globals_: MutableMapping[str, Any], + locals_: MutableMapping[str, Any], + ) -> Callable[..., Any]: + fn = getattr(cls, name) + + def _name_error(name: str, from_: Exception) -> NoReturn: + raise NameError( + "Can't invoke function '%s', as the proxy object has " + "not yet been " + "established for the Alembic '%s' class. " + "Try placing this code inside a callable." + % (name, cls.__name__) + ) from from_ + + globals_["_name_error"] = _name_error + + translations = getattr(fn, "_legacy_translations", []) + if translations: + spec = inspect_getfullargspec(fn) + if spec[0] and spec[0][0] == "self": + spec[0].pop(0) + + outer_args = inner_args = "*args, **kw" + translate_str = "args, kw = _translate(%r, %r, %r, args, kw)" % ( + fn.__name__, + tuple(spec), + translations, + ) + + def translate( + fn_name: str, spec: Any, translations: Any, args: Any, kw: Any + ) -> Any: + return_kw = {} + return_args = [] + + for oldname, newname in translations: + if oldname in kw: + warnings.warn( + "Argument %r is now named %r " + "for method %s()." % (oldname, newname, fn_name) + ) + return_kw[newname] = kw.pop(oldname) + return_kw.update(kw) + + args = list(args) + if spec[3]: + pos_only = spec[0][: -len(spec[3])] + else: + pos_only = spec[0] + for arg in pos_only: + if arg not in return_kw: + try: + return_args.append(args.pop(0)) + except IndexError: + raise TypeError( + "missing required positional argument: %s" + % arg + ) + return_args.extend(args) + + return return_args, return_kw + + globals_["_translate"] = translate + else: + outer_args = "*args, **kw" + inner_args = "*args, **kw" + translate_str = "" + + func_text = textwrap.dedent( + """\ + def %(name)s(%(args)s): + %(doc)r + %(translate)s + try: + p = _proxy + except NameError as ne: + _name_error('%(name)s', ne) + return _proxy.%(name)s(%(apply_kw)s) + e + """ + % { + "name": name, + "translate": translate_str, + "args": outer_args, + "apply_kw": inner_args, + "doc": fn.__doc__, + } + ) + lcl: MutableMapping[str, Any] = {} + + exec(func_text, cast("Dict[str, Any]", globals_), lcl) + return cast("Callable[..., Any]", lcl[name]) + + +def _with_legacy_names(translations: Any) -> Any: + def decorate(fn: _C) -> _C: + fn._legacy_translations = translations # type: ignore[attr-defined] + return fn + + return decorate + + +def rev_id() -> str: + return uuid.uuid4().hex[-12:] + + +@overload +def to_tuple(x: Any, default: Tuple[Any, ...]) -> Tuple[Any, ...]: ... + + +@overload +def to_tuple(x: None, default: Optional[_T] = ...) -> _T: ... + + +@overload +def to_tuple( + x: Any, default: Optional[Tuple[Any, ...]] = None +) -> Tuple[Any, ...]: ... + + +def to_tuple( + x: Any, default: Optional[Tuple[Any, ...]] = None +) -> Optional[Tuple[Any, ...]]: + if x is None: + return default + elif isinstance(x, str): + return (x,) + elif isinstance(x, Iterable): + return tuple(x) + else: + return (x,) + + +def dedupe_tuple(tup: Tuple[str, ...]) -> Tuple[str, ...]: + return tuple(unique_list(tup)) + + +class PriorityDispatchResult(enum.Enum): + """indicate an action after running a function within a + :class:`.PriorityDispatcher` + + .. versionadded:: 1.18.0 + + """ + + CONTINUE = enum.auto() + """Continue running more functions. + + Any return value that is not PriorityDispatchResult.STOP is equivalent + to this. + + """ + + STOP = enum.auto() + """Stop running any additional functions within the subgroup""" + + +class DispatchPriority(enum.IntEnum): + """Indicate which of three sub-collections a function inside a + :class:`.PriorityDispatcher` should be placed. + + .. versionadded:: 1.18.0 + + """ + + FIRST = 50 + """Run the funciton in the first batch of functions (highest priority)""" + + MEDIUM = 25 + """Run the function at normal priority (this is the default)""" + + LAST = 10 + """Run the function in the last batch of functions""" + + +class Dispatcher: + def __init__(self) -> None: + self._registry: Dict[Tuple[Any, ...], Any] = {} + + def dispatch_for( + self, + target: Any, + *, + qualifier: str = "default", + replace: bool = False, + ) -> Callable[[_C], _C]: + def decorate(fn: _C) -> _C: + if (target, qualifier) in self._registry and not replace: + raise ValueError( + "Can not set dispatch function for object " + f"{target!r}: key already exists. To replace " + "existing function, use replace=True." + ) + self._registry[(target, qualifier)] = fn + return fn + + return decorate + + def dispatch(self, obj: Any, qualifier: str = "default") -> Any: + if isinstance(obj, str): + targets: Sequence[Any] = [obj] + elif isinstance(obj, type): + targets = obj.__mro__ + else: + targets = type(obj).__mro__ + + if qualifier != "default": + qualifiers = [qualifier, "default"] + else: + qualifiers = ["default"] + + for spcls in targets: + for qualifier in qualifiers: + if (spcls, qualifier) in self._registry: + return self._registry[(spcls, qualifier)] + else: + raise ValueError("no dispatch function for object: %s" % obj) + + def branch(self) -> Dispatcher: + """Return a copy of this dispatcher that is independently + writable.""" + + d = Dispatcher() + d._registry.update(self._registry) + return d + + +class PriorityDispatcher: + """registers lists of functions at multiple levels of priorty and provides + a target to invoke them in priority order. + + .. versionadded:: 1.18.0 - PriorityDispatcher replaces the job + of Dispatcher(uselist=True) + + """ + + def __init__(self) -> None: + self._registry: dict[tuple[Any, ...], Any] = collections.defaultdict( + list + ) + + def dispatch_for( + self, + target: str, + *, + priority: DispatchPriority = DispatchPriority.MEDIUM, + qualifier: str = "default", + subgroup: str | None = None, + ) -> Callable[[_C], _C]: + """return a decorator callable that registers a function at a + given priority, with a given qualifier, to fire off for a given + subgroup. + + It's important this remains as a decorator to support third party + plugins who are populating the dispatcher using that style. + + """ + + def decorate(fn: _C) -> _C: + self._registry[(target, qualifier, priority)].append( + (fn, subgroup) + ) + return fn + + return decorate + + def dispatch( + self, target: str, *, qualifier: str = "default" + ) -> Callable[..., None]: + """Provide a callable for the given target and qualifier.""" + + if qualifier != "default": + qualifiers = [qualifier, "default"] + else: + qualifiers = ["default"] + + def go(*arg: Any, **kw: Any) -> Any: + results_by_subgroup: dict[str, PriorityDispatchResult] = {} + for priority in DispatchPriority: + for qualifier in qualifiers: + for fn, subgroup in self._registry.get( + (target, qualifier, priority), () + ): + if ( + results_by_subgroup.get( + subgroup, PriorityDispatchResult.CONTINUE + ) + is PriorityDispatchResult.STOP + ): + continue + + result = fn(*arg, **kw) + results_by_subgroup[subgroup] = result + + return go + + def branch(self) -> PriorityDispatcher: + """Return a copy of this dispatcher that is independently + writable.""" + + d = PriorityDispatcher() + d.populate_with(self) + return d + + def populate_with(self, other: PriorityDispatcher) -> None: + """Populate this PriorityDispatcher with the contents of another one. + + Additive, does not remove existing contents. + """ + for k in other._registry: + new_list = other._registry[k] + self._registry[k].extend(new_list) + + +def not_none(value: Optional[_T]) -> _T: + assert value is not None + return value diff --git a/venv/lib/python3.12/site-packages/alembic/util/messaging.py b/venv/lib/python3.12/site-packages/alembic/util/messaging.py new file mode 100644 index 00000000..4c08f16e --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/util/messaging.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +from collections.abc import Iterable +from contextlib import contextmanager +import logging +import sys +import textwrap +from typing import Iterator +from typing import Optional +from typing import TextIO +from typing import Union +import warnings + +from sqlalchemy.engine import url + +log = logging.getLogger(__name__) + +# disable "no handler found" errors +logging.getLogger("alembic").addHandler(logging.NullHandler()) + + +try: + import fcntl + import termios + import struct + + ioctl = fcntl.ioctl(0, termios.TIOCGWINSZ, struct.pack("HHHH", 0, 0, 0, 0)) + _h, TERMWIDTH, _hp, _wp = struct.unpack("HHHH", ioctl) + if TERMWIDTH <= 0: # can occur if running in emacs pseudo-tty + TERMWIDTH = None +except (ImportError, OSError): + TERMWIDTH = None + + +def write_outstream( + stream: TextIO, *text: Union[str, bytes], quiet: bool = False +) -> None: + if quiet: + return + encoding = getattr(stream, "encoding", "ascii") or "ascii" + for t in text: + if not isinstance(t, bytes): + t = t.encode(encoding, "replace") + t = t.decode(encoding) + try: + stream.write(t) + except OSError: + # suppress "broken pipe" errors. + # no known way to handle this on Python 3 however + # as the exception is "ignored" (noisily) in TextIOWrapper. + break + + +@contextmanager +def status( + status_msg: str, newline: bool = False, quiet: bool = False +) -> Iterator[None]: + msg(status_msg + " ...", newline, flush=True, quiet=quiet) + try: + yield + except: + if not quiet: + write_outstream(sys.stdout, " FAILED\n") + raise + else: + if not quiet: + write_outstream(sys.stdout, " done\n") + + +def err(message: str, quiet: bool = False) -> None: + log.error(message) + msg(f"FAILED: {message}", quiet=quiet) + sys.exit(-1) + + +def obfuscate_url_pw(input_url: str) -> str: + return url.make_url(input_url).render_as_string(hide_password=True) + + +def warn(msg: str, stacklevel: int = 2) -> None: + warnings.warn(msg, UserWarning, stacklevel=stacklevel) + + +def warn_deprecated(msg: str, stacklevel: int = 2) -> None: + warnings.warn(msg, DeprecationWarning, stacklevel=stacklevel) + + +def msg( + msg: str, newline: bool = True, flush: bool = False, quiet: bool = False +) -> None: + if quiet: + return + if TERMWIDTH is None: + write_outstream(sys.stdout, msg) + if newline: + write_outstream(sys.stdout, "\n") + else: + # left indent output lines + indent = " " + lines = textwrap.wrap( + msg, + TERMWIDTH, + initial_indent=indent, + subsequent_indent=indent, + ) + if len(lines) > 1: + for line in lines[0:-1]: + write_outstream(sys.stdout, line, "\n") + write_outstream(sys.stdout, lines[-1], ("\n" if newline else "")) + if flush: + sys.stdout.flush() + + +def format_as_comma(value: Optional[Union[str, Iterable[str]]]) -> str: + if value is None: + return "" + elif isinstance(value, str): + return value + elif isinstance(value, Iterable): + return ", ".join(value) + else: + raise ValueError("Don't know how to comma-format %r" % value) diff --git a/venv/lib/python3.12/site-packages/alembic/util/pyfiles.py b/venv/lib/python3.12/site-packages/alembic/util/pyfiles.py new file mode 100644 index 00000000..135a42dc --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/util/pyfiles.py @@ -0,0 +1,153 @@ +from __future__ import annotations + +import atexit +from contextlib import ExitStack +import importlib +from importlib import resources +import importlib.machinery +import importlib.util +import os +import pathlib +import re +import tempfile +from types import ModuleType +from typing import Any +from typing import Optional +from typing import Union + +from mako import exceptions +from mako.template import Template + +from .exc import CommandError + + +def template_to_file( + template_file: Union[str, os.PathLike[str]], + dest: Union[str, os.PathLike[str]], + output_encoding: str, + *, + append_with_newlines: bool = False, + **kw: Any, +) -> None: + template = Template(filename=_preserving_path_as_str(template_file)) + try: + output = template.render_unicode(**kw).encode(output_encoding) + except: + with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as ntf: + ntf.write( + exceptions.text_error_template() + .render_unicode() + .encode(output_encoding) + ) + fname = ntf.name + raise CommandError( + "Template rendering failed; see %s for a " + "template-oriented traceback." % fname + ) + else: + with open(dest, "ab" if append_with_newlines else "wb") as f: + if append_with_newlines: + f.write("\n\n".encode(output_encoding)) + f.write(output) + + +def coerce_resource_to_filename(fname_or_resource: str) -> pathlib.Path: + """Interpret a filename as either a filesystem location or as a package + resource. + + Names that are non absolute paths and contain a colon + are interpreted as resources and coerced to a file location. + + """ + # TODO: there seem to be zero tests for the package resource codepath + if not os.path.isabs(fname_or_resource) and ":" in fname_or_resource: + tokens = fname_or_resource.split(":") + + # from https://importlib-resources.readthedocs.io/en/latest/migration.html#pkg-resources-resource-filename # noqa E501 + + file_manager = ExitStack() + atexit.register(file_manager.close) + + ref = resources.files(tokens[0]) + for tok in tokens[1:]: + ref = ref / tok + fname_or_resource = file_manager.enter_context( # type: ignore[assignment] # noqa: E501 + resources.as_file(ref) + ) + return pathlib.Path(fname_or_resource) + + +def pyc_file_from_path( + path: Union[str, os.PathLike[str]], +) -> Optional[pathlib.Path]: + """Given a python source path, locate the .pyc.""" + + pathpath = pathlib.Path(path) + candidate = pathlib.Path( + importlib.util.cache_from_source(pathpath.as_posix()) + ) + if candidate.exists(): + return candidate + + # even for pep3147, fall back to the old way of finding .pyc files, + # to support sourceless operation + ext = pathpath.suffix + for ext in importlib.machinery.BYTECODE_SUFFIXES: + if pathpath.with_suffix(ext).exists(): + return pathpath.with_suffix(ext) + else: + return None + + +def load_python_file( + dir_: Union[str, os.PathLike[str]], filename: Union[str, os.PathLike[str]] +) -> ModuleType: + """Load a file from the given path as a Python module.""" + + dir_ = pathlib.Path(dir_) + filename_as_path = pathlib.Path(filename) + filename = filename_as_path.name + + module_id = re.sub(r"\W", "_", filename) + path = dir_ / filename + ext = path.suffix + if ext == ".py": + if path.exists(): + module = load_module_py(module_id, path) + else: + pyc_path = pyc_file_from_path(path) + if pyc_path is None: + raise ImportError("Can't find Python file %s" % path) + else: + module = load_module_py(module_id, pyc_path) + elif ext in (".pyc", ".pyo"): + module = load_module_py(module_id, path) + else: + assert False + return module + + +def load_module_py( + module_id: str, path: Union[str, os.PathLike[str]] +) -> ModuleType: + spec = importlib.util.spec_from_file_location(module_id, path) + assert spec + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) # type: ignore + return module + + +def _preserving_path_as_str(path: Union[str, os.PathLike[str]]) -> str: + """receive str/pathlike and return a string. + + Does not convert an incoming string path to a Path first, to help with + unit tests that are doing string path round trips without OS-specific + processing if not necessary. + + """ + if isinstance(path, str): + return path + elif isinstance(path, pathlib.PurePath): + return str(path) + else: + return str(pathlib.Path(path)) diff --git a/venv/lib/python3.12/site-packages/alembic/util/sqla_compat.py b/venv/lib/python3.12/site-packages/alembic/util/sqla_compat.py new file mode 100644 index 00000000..ff2f2c93 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/util/sqla_compat.py @@ -0,0 +1,510 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +import contextlib +import re +from typing import Any +from typing import Callable +from typing import Dict +from typing import Iterable +from typing import Iterator +from typing import Optional +from typing import Protocol +from typing import Set +from typing import Type +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +from sqlalchemy import __version__ +from sqlalchemy import schema +from sqlalchemy import sql +from sqlalchemy import types as sqltypes +from sqlalchemy.schema import CheckConstraint +from sqlalchemy.schema import Column +from sqlalchemy.schema import ForeignKeyConstraint +from sqlalchemy.sql import visitors +from sqlalchemy.sql.base import DialectKWArgs +from sqlalchemy.sql.elements import BindParameter +from sqlalchemy.sql.elements import ColumnClause +from sqlalchemy.sql.elements import TextClause +from sqlalchemy.sql.elements import UnaryExpression +from sqlalchemy.sql.naming import _NONE_NAME as _NONE_NAME # type: ignore[attr-defined] # noqa: E501 +from sqlalchemy.sql.visitors import traverse +from typing_extensions import TypeGuard + +if TYPE_CHECKING: + from sqlalchemy import ClauseElement + from sqlalchemy import Identity + from sqlalchemy import Index + from sqlalchemy import Table + from sqlalchemy.engine import Connection + from sqlalchemy.engine import Dialect + from sqlalchemy.engine import Transaction + from sqlalchemy.sql.base import ColumnCollection + from sqlalchemy.sql.compiler import SQLCompiler + from sqlalchemy.sql.elements import ColumnElement + from sqlalchemy.sql.schema import Constraint + from sqlalchemy.sql.schema import SchemaItem + +_CE = TypeVar("_CE", bound=Union["ColumnElement[Any]", "SchemaItem"]) + + +class _CompilerProtocol(Protocol): + def __call__(self, element: Any, compiler: Any, **kw: Any) -> str: ... + + +def _safe_int(value: str) -> Union[int, str]: + try: + return int(value) + except: + return value + + +_vers = tuple( + [_safe_int(x) for x in re.findall(r"(\d+|[abc]\d)", __version__)] +) +# https://docs.sqlalchemy.org/en/latest/changelog/changelog_14.html#change-0c6e0cc67dfe6fac5164720e57ef307d +sqla_14_18 = _vers >= (1, 4, 18) +sqla_14_26 = _vers >= (1, 4, 26) +sqla_2 = _vers >= (2,) +sqla_2_0_25 = _vers >= (2, 25) +sqla_2_1 = _vers >= (2, 1) +sqlalchemy_version = __version__ + +if TYPE_CHECKING: + + def compiles( + element: Type[ClauseElement], *dialects: str + ) -> Callable[[_CompilerProtocol], _CompilerProtocol]: ... + +else: + from sqlalchemy.ext.compiler import compiles # noqa: I100,I202 + + +identity_has_dialect_kwargs = issubclass(schema.Identity, DialectKWArgs) + + +def _get_identity_options_dict( + identity: Union[Identity, schema.Sequence, None], + dialect_kwargs: bool = False, +) -> Dict[str, Any]: + if identity is None: + return {} + elif identity_has_dialect_kwargs: + assert hasattr(identity, "_as_dict") + as_dict = identity._as_dict() + if dialect_kwargs: + assert isinstance(identity, DialectKWArgs) + as_dict.update(identity.dialect_kwargs) + else: + as_dict = {} + if isinstance(identity, schema.Identity): + # always=None means something different than always=False + as_dict["always"] = identity.always + if identity.on_null is not None: + as_dict["on_null"] = identity.on_null + # attributes common to Identity and Sequence + attrs = ( + "start", + "increment", + "minvalue", + "maxvalue", + "nominvalue", + "nomaxvalue", + "cycle", + "cache", + "order", + ) + as_dict.update( + { + key: getattr(identity, key, None) + for key in attrs + if getattr(identity, key, None) is not None + } + ) + return as_dict + + +if sqla_2: + from sqlalchemy.sql.base import _NoneName +else: + from sqlalchemy.util import symbol as _NoneName # type: ignore[assignment] + + +_ConstraintName = Union[None, str, _NoneName] +_ConstraintNameDefined = Union[str, _NoneName] + + +def constraint_name_defined( + name: _ConstraintName, +) -> TypeGuard[_ConstraintNameDefined]: + return name is _NONE_NAME or isinstance(name, (str, _NoneName)) + + +def constraint_name_string(name: _ConstraintName) -> TypeGuard[str]: + return isinstance(name, str) + + +def constraint_name_or_none(name: _ConstraintName) -> Optional[str]: + return name if constraint_name_string(name) else None + + +AUTOINCREMENT_DEFAULT = "auto" + + +@contextlib.contextmanager +def _ensure_scope_for_ddl( + connection: Optional[Connection], +) -> Iterator[None]: + try: + in_transaction = connection.in_transaction # type: ignore[union-attr] + except AttributeError: + # catch for MockConnection, None + in_transaction = None + pass + + # yield outside the catch + if in_transaction is None: + yield + else: + if not in_transaction(): + assert connection is not None + with connection.begin(): + yield + else: + yield + + +def _safe_begin_connection_transaction( + connection: Connection, +) -> Transaction: + transaction = connection.get_transaction() + if transaction: + return transaction + else: + return connection.begin() + + +def _safe_commit_connection_transaction( + connection: Connection, +) -> None: + transaction = connection.get_transaction() + if transaction: + transaction.commit() + + +def _safe_rollback_connection_transaction( + connection: Connection, +) -> None: + transaction = connection.get_transaction() + if transaction: + transaction.rollback() + + +def _get_connection_in_transaction(connection: Optional[Connection]) -> bool: + try: + in_transaction = connection.in_transaction # type: ignore + except AttributeError: + # catch for MockConnection + return False + else: + return in_transaction() + + +def _idx_table_bound_expressions(idx: Index) -> Iterable[ColumnElement[Any]]: + return idx.expressions # type: ignore + + +def _copy(schema_item: _CE, **kw) -> _CE: + if hasattr(schema_item, "_copy"): + return schema_item._copy(**kw) + else: + return schema_item.copy(**kw) # type: ignore[union-attr] + + +def _connectable_has_table( + connectable: Connection, tablename: str, schemaname: Union[str, None] +) -> bool: + return connectable.dialect.has_table(connectable, tablename, schemaname) + + +def _exec_on_inspector(inspector, statement, **params): + with inspector._operation_context() as conn: + return conn.execute(statement, params) + + +def _nullability_might_be_unset(metadata_column): + from sqlalchemy.sql import schema + + return metadata_column._user_defined_nullable is schema.NULL_UNSPECIFIED + + +def _server_default_is_computed(*server_default) -> bool: + return any(isinstance(sd, schema.Computed) for sd in server_default) + + +def _server_default_is_identity(*server_default) -> bool: + return any(isinstance(sd, schema.Identity) for sd in server_default) + + +def _table_for_constraint(constraint: Constraint) -> Table: + if isinstance(constraint, ForeignKeyConstraint): + table = constraint.parent + assert table is not None + return table # type: ignore[return-value] + else: + return constraint.table + + +def _columns_for_constraint(constraint): + if isinstance(constraint, ForeignKeyConstraint): + return [fk.parent for fk in constraint.elements] + elif isinstance(constraint, CheckConstraint): + return _find_columns(constraint.sqltext) + else: + return list(constraint.columns) + + +def _resolve_for_variant(type_, dialect): + if _type_has_variants(type_): + base_type, mapping = _get_variant_mapping(type_) + return mapping.get(dialect.name, base_type) + else: + return type_ + + +if hasattr(sqltypes.TypeEngine, "_variant_mapping"): # 2.0 + + def _type_has_variants(type_): + return bool(type_._variant_mapping) + + def _get_variant_mapping(type_): + return type_, type_._variant_mapping + +else: + + def _type_has_variants(type_): + return type(type_) is sqltypes.Variant + + def _get_variant_mapping(type_): + return type_.impl, type_.mapping + + +def _get_table_key(name: str, schema: Optional[str]) -> str: + if schema is None: + return name + else: + return schema + "." + name + + +def _fk_spec(constraint: ForeignKeyConstraint) -> Any: + if TYPE_CHECKING: + assert constraint.columns is not None + assert constraint.elements is not None + assert isinstance(constraint.parent, Table) + + source_columns = [ + constraint.columns[key].name for key in constraint.column_keys + ] + + source_table = constraint.parent.name + source_schema = constraint.parent.schema + target_schema = constraint.elements[0].column.table.schema + target_table = constraint.elements[0].column.table.name + target_columns = [element.column.name for element in constraint.elements] + ondelete = constraint.ondelete + onupdate = constraint.onupdate + deferrable = constraint.deferrable + initially = constraint.initially + return ( + source_schema, + source_table, + source_columns, + target_schema, + target_table, + target_columns, + onupdate, + ondelete, + deferrable, + initially, + ) + + +def _fk_is_self_referential(constraint: ForeignKeyConstraint) -> bool: + spec = constraint.elements[0]._get_colspec() + tokens = spec.split(".") + tokens.pop(-1) # colname + tablekey = ".".join(tokens) + assert constraint.parent is not None + return tablekey == constraint.parent.key + + +def _is_type_bound(constraint: Constraint) -> bool: + # this deals with SQLAlchemy #3260, don't copy CHECK constraints + # that will be generated by the type. + # new feature added for #3260 + return constraint._type_bound + + +def _find_columns(clause): + """locate Column objects within the given expression.""" + + cols: Set[ColumnElement[Any]] = set() + traverse(clause, {}, {"column": cols.add}) + return cols + + +def _remove_column_from_collection( + collection: ColumnCollection, column: Union[Column[Any], ColumnClause[Any]] +) -> None: + """remove a column from a ColumnCollection.""" + + # workaround for older SQLAlchemy, remove the + # same object that's present + assert column.key is not None + to_remove = collection[column.key] + + # SQLAlchemy 2.0 will use more ReadOnlyColumnCollection + # (renamed from ImmutableColumnCollection) + if hasattr(collection, "_immutable") or hasattr(collection, "_readonly"): + collection._parent.remove(to_remove) + else: + collection.remove(to_remove) + + +def _textual_index_column( + table: Table, text_: Union[str, TextClause, ColumnElement[Any]] +) -> Union[ColumnElement[Any], Column[Any]]: + """a workaround for the Index construct's severe lack of flexibility""" + if isinstance(text_, str): + c = Column(text_, sqltypes.NULLTYPE) + table.append_column(c) + return c + elif isinstance(text_, TextClause): + return _textual_index_element(table, text_) + elif isinstance(text_, _textual_index_element): + return _textual_index_column(table, text_.text) + elif isinstance(text_, sql.ColumnElement): + return _copy_expression(text_, table) + else: + raise ValueError("String or text() construct expected") + + +def _copy_expression(expression: _CE, target_table: Table) -> _CE: + def replace(col): + if ( + isinstance(col, Column) + and col.table is not None + and col.table is not target_table + ): + if col.name in target_table.c: + return target_table.c[col.name] + else: + c = _copy(col) + target_table.append_column(c) + return c + else: + return None + + return visitors.replacement_traverse( # type: ignore[call-overload] + expression, {}, replace + ) + + +class _textual_index_element(sql.ColumnElement): + """Wrap around a sqlalchemy text() construct in such a way that + we appear like a column-oriented SQL expression to an Index + construct. + + The issue here is that currently the Postgresql dialect, the biggest + recipient of functional indexes, keys all the index expressions to + the corresponding column expressions when rendering CREATE INDEX, + so the Index we create here needs to have a .columns collection that + is the same length as the .expressions collection. Ultimately + SQLAlchemy should support text() expressions in indexes. + + See SQLAlchemy issue 3174. + + """ + + __visit_name__ = "_textual_idx_element" + + def __init__(self, table: Table, text: TextClause) -> None: + self.table = table + self.text = text + self.key = text.text + self.fake_column = schema.Column(self.text.text, sqltypes.NULLTYPE) + table.append_column(self.fake_column) + + def get_children(self, **kw): + return [self.fake_column] + + +@compiles(_textual_index_element) +def _render_textual_index_column( + element: _textual_index_element, compiler: SQLCompiler, **kw +) -> str: + return compiler.process(element.text, **kw) + + +class _literal_bindparam(BindParameter): + pass + + +@compiles(_literal_bindparam) +def _render_literal_bindparam( + element: _literal_bindparam, compiler: SQLCompiler, **kw +) -> str: + return compiler.render_literal_bindparam(element, **kw) + + +def _get_constraint_final_name( + constraint: Union[Index, Constraint], dialect: Optional[Dialect] +) -> Optional[str]: + if constraint.name is None: + return None + assert dialect is not None + # for SQLAlchemy 1.4 we would like to have the option to expand + # the use of "deferred" names for constraints as well as to have + # some flexibility with "None" name and similar; make use of new + # SQLAlchemy API to return what would be the final compiled form of + # the name for this dialect. + return dialect.identifier_preparer.format_constraint( + constraint, _alembic_quote=False + ) + + +def _constraint_is_named( + constraint: Union[Constraint, Index], dialect: Optional[Dialect] +) -> bool: + if constraint.name is None: + return False + assert dialect is not None + name = dialect.identifier_preparer.format_constraint( + constraint, _alembic_quote=False + ) + return name is not None + + +def is_expression_index(index: Index) -> bool: + for expr in index.expressions: + if is_expression(expr): + return True + return False + + +def is_expression(expr: Any) -> bool: + while isinstance(expr, UnaryExpression): + expr = expr.element + if not isinstance(expr, ColumnClause) or expr.is_literal: + return True + return False + + +def _inherit_schema_deprecated() -> bool: + # at some point in 2.1 inherit_schema was replaced with a property + # so that's preset at the class level, while before it wasn't. + return sqla_2_1 and hasattr(sqltypes.Enum, "inherit_schema") diff --git a/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/LICENSE.txt b/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/LICENSE.txt new file mode 100644 index 00000000..79c9825a --- /dev/null +++ b/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright 2010 Jason Kirtland + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/METADATA b/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/METADATA new file mode 100644 index 00000000..6d343f57 --- /dev/null +++ b/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/METADATA @@ -0,0 +1,60 @@ +Metadata-Version: 2.3 +Name: blinker +Version: 1.9.0 +Summary: Fast, simple object-to-object and broadcast signaling +Author: Jason Kirtland +Maintainer-email: Pallets Ecosystem +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Typing :: Typed +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://blinker.readthedocs.io +Project-URL: Source, https://github.com/pallets-eco/blinker/ + +# Blinker + +Blinker provides a fast dispatching system that allows any number of +interested parties to subscribe to events, or "signals". + + +## Pallets Community Ecosystem + +> [!IMPORTANT]\ +> This project is part of the Pallets Community Ecosystem. Pallets is the open +> source organization that maintains Flask; Pallets-Eco enables community +> maintenance of related projects. If you are interested in helping maintain +> this project, please reach out on [the Pallets Discord server][discord]. +> +> [discord]: https://discord.gg/pallets + + +## Example + +Signal receivers can subscribe to specific senders or receive signals +sent by any sender. + +```pycon +>>> from blinker import signal +>>> started = signal('round-started') +>>> def each(round): +... print(f"Round {round}") +... +>>> started.connect(each) + +>>> def round_two(round): +... print("This is round two.") +... +>>> started.connect(round_two, sender=2) + +>>> for round in range(1, 4): +... started.send(round) +... +Round 1! +Round 2! +This is round two. +Round 3! +``` + diff --git a/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/RECORD b/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/RECORD new file mode 100644 index 00000000..7cfb7148 --- /dev/null +++ b/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/RECORD @@ -0,0 +1,12 @@ +blinker-1.9.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +blinker-1.9.0.dist-info/LICENSE.txt,sha256=nrc6HzhZekqhcCXSrhvjg5Ykx5XphdTw6Xac4p-spGc,1054 +blinker-1.9.0.dist-info/METADATA,sha256=uIRiM8wjjbHkCtbCyTvctU37IAZk0kEe5kxAld1dvzA,1633 +blinker-1.9.0.dist-info/RECORD,, +blinker-1.9.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82 +blinker/__init__.py,sha256=I2EdZqpy4LyjX17Hn1yzJGWCjeLaVaPzsMgHkLfj_cQ,317 +blinker/__pycache__/__init__.cpython-312.pyc,, +blinker/__pycache__/_utilities.cpython-312.pyc,, +blinker/__pycache__/base.cpython-312.pyc,, +blinker/_utilities.py,sha256=0J7eeXXTUx0Ivf8asfpx0ycVkp0Eqfqnj117x2mYX9E,1675 +blinker/base.py,sha256=QpDuvXXcwJF49lUBcH5BiST46Rz9wSG7VW_p7N_027M,19132 +blinker/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/WHEEL b/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/WHEEL new file mode 100644 index 00000000..e3c6feef --- /dev/null +++ b/venv/lib/python3.12/site-packages/blinker-1.9.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.10.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/blinker/__init__.py b/venv/lib/python3.12/site-packages/blinker/__init__.py new file mode 100644 index 00000000..1772fa4a --- /dev/null +++ b/venv/lib/python3.12/site-packages/blinker/__init__.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from .base import ANY +from .base import default_namespace +from .base import NamedSignal +from .base import Namespace +from .base import Signal +from .base import signal + +__all__ = [ + "ANY", + "default_namespace", + "NamedSignal", + "Namespace", + "Signal", + "signal", +] diff --git a/venv/lib/python3.12/site-packages/blinker/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/blinker/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b73e6d84e11da8d4edfd489fe0c394fa85597d9 GIT binary patch literal 495 zcmaKo%}N6?5XUFo?S7RaJqUt$P(kRSUDT@zzCbU6;9g?dq-|(6TXwfCy$C*qcW*w6 zZ?Ld<5D&eHy?Ju7wH^)OpWjSoGLw02HUq%*aPlTE3BbECwyU-Qvc5-n0uG!gh)F~U z@EW&NEw&=7VjJ0x?27Bi^{8I4gX~03#V)cNxfOfJUgQxN0r#It`(&hl9*C`~Q&6?UIx;{UwDrIaZ}=?kne1-@^?MUqacKC*C- zaM4-FeB3eg>zoTc50&hP(`7zR^m+I6EX-ssI{2>%8;LCJE2$?U4Jnmc=9G4)%fh>e t6FyVoDs5pIGoI`a<ox literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/blinker/__pycache__/_utilities.cpython-312.pyc b/venv/lib/python3.12/site-packages/blinker/__pycache__/_utilities.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..743cf9b5171b2a1aa9924ea9abe04db2f39ab1c0 GIT binary patch literal 2722 zcmb7G&2JM&6rb^~z3Y$IApt^40t-P9qu`Kos8ph~h)RG&Beh9Cvf>PY+M&3kWV zfBU{>e`#rnAsAPW|7Ks0AoK@cG=>~7c)e%T%E>#+;H7IGnk!syQ7a5U6aW-~Sp`4}%+V^k zg0ry^lur1Ok<#2`-YNfdPBQ@zGhC*5Le!_Zwn?bQrVUq{n8;7g5R>($Cnhr5honFZ zMl8)V9LLirVMXeKA&F@)JY$Y%wm8cqnV66b4Gn3*n|e>=^7#{K&GvQ+gxM%9250=s zUZ`QGn9JQPbKWsL?_-0GS+>c>JVyKK=Z%m1O7I1qSr3WuANL|WcMOeLk_BB)%f4hfo-e!LL|+1@`%zst`M2u2$MYK;y!iL0{R??I>lF->c>QMHBBvH? zHhqe7b>Td5=ldOdvcFJ*lyc8x2Hx!VY(`GOo>^lGwoN*=J40rFxIZN^h8XI9VNI=db}wAsF&b8Bq9{a7t=>`}6Pd1^VoW^N>p zR;A6Z!&T{Kd^x`I-n!bknQE)Xw%`b~D<<5JA|`FX1QF4>kr*XJ$hJZ$xQ3tjv0-fQ5X3M?iQ37=ZA~ zlo$!1(0yP7cz93n_{JRsloaSL1c9K3TPA=2f>is>(sHS$9{3LsU10h;5INiogYr~t z29ebDDZ(I&3_1nLQr@@ijArq1zAG63YTdgUy%nuiYU)7&DJ=`<`Vv%=9|@|Os$g4J z&~D)AAt1gyoOek&Bo;zk=gWIovEYy~mO1B^`m{_YGoFs~2=RQ@bw8%-b9t-ia6X~y zpBD|Mu7L`$@}{l_Ws%bLrZx#uo|m+P6CNkZ{p7dQE5js?1EquM9*_!`yel$8U>Af( zz)J&p@ilr9Qo`!vHk3KNc%jyEazpNaCP%{UTdg?UxfKEO97vaNh}gR;Tf%UL$d;dh zSw(C+lV-MC0x0TZrzy~rjPDc!G7Rpmmq(+0?6Tv&e`z%Aa?nN?FAp&)i1AMpgYoE! zkqb5MBnmAF?3*w&&Vfq+mnSX|rWqP^|49d0w?$lL=WOWxxjY3qB_^@wVF@dbxE6t4 z%ktVJ(TW~i0)_`RybH&mH)*q^#O-XCA4ph62-B*sz>QFV8^l*_Z!WmzLbkRJTU*0$}P)iNe)PYCJo|T^4((RFz@;&9~V=!*7 z#$Zn(Sv9Y}wKyDgAC~x4@)brZQa1H!f&;c+Sgva9!T0{?&WRqteq&nAn)~;-%dTVO$ zqj<9Ftz6uQch}_ZMne$qhy<6vsP=Nq%v?0QX`U(pyodloc~aBUK*~qV%y|ICEyl(G z!`SHfc%Q~Q#<*4yb8r&07_LjV^0tsG!cPRW1qzNue#*={4rB}eNy-?LrY~`VP2mYY z!b%0(o$@jBl?C{1;@`=ABD~`(qoo3YKZ5`fuOX2j!V`B;P}sr6F&OlsA<7-ZOWAuH z$YTj({16@e6CJ#dlJ`;DL)7sQ9r|5vSv+^`(wCR2=2ydu_<7_Y?s|fNYz?Mx`?F&* IJ|xcm2PueOQ~&?~ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/blinker/__pycache__/base.cpython-312.pyc b/venv/lib/python3.12/site-packages/blinker/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab7ccb49bbe62426c93eb674859b39aef3687221 GIT binary patch literal 21965 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�*hv7)y8-3bq=9<*#!EjOl>p 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 Symbol('foo') is Symbol('foo') + True + >>> Symbol('foo') + foo + """ + + symbols: t.ClassVar[dict[str, Symbol]] = {} + + def __new__(cls, name: str) -> Symbol: + if name in cls.symbols: + return cls.symbols[name] + + obj = super().__new__(cls) + cls.symbols[name] = obj + return obj + + def __init__(self, name: str) -> None: + self.name = name + + def __repr__(self) -> str: + return self.name + + def __getnewargs__(self) -> tuple[t.Any, ...]: + return (self.name,) + + +def make_id(obj: object) -> c.Hashable: + """Get a stable identifier for a receiver or sender, to be used as a dict + key or in a set. + """ + if inspect.ismethod(obj): + # The id of a bound method is not stable, but the id of the unbound + # function and instance are. + return id(obj.__func__), id(obj.__self__) + + if isinstance(obj, (str, int)): + # Instances with the same value always compare equal and have the same + # hash, even if the id may change. + return obj + + # Assume other types are not hashable but will always be the same instance. + return id(obj) + + +def make_ref(obj: T, callback: c.Callable[[ref[T]], None] | None = None) -> ref[T]: + if inspect.ismethod(obj): + return WeakMethod(obj, callback) # type: ignore[arg-type, return-value] + + return ref(obj, callback) diff --git a/venv/lib/python3.12/site-packages/blinker/base.py b/venv/lib/python3.12/site-packages/blinker/base.py new file mode 100644 index 00000000..d051b94a --- /dev/null +++ b/venv/lib/python3.12/site-packages/blinker/base.py @@ -0,0 +1,512 @@ +from __future__ import annotations + +import collections.abc as c +import sys +import typing as t +import weakref +from collections import defaultdict +from contextlib import contextmanager +from functools import cached_property +from inspect import iscoroutinefunction + +from ._utilities import make_id +from ._utilities import make_ref +from ._utilities import Symbol + +F = t.TypeVar("F", bound=c.Callable[..., t.Any]) + +ANY = Symbol("ANY") +"""Symbol for "any sender".""" + +ANY_ID = 0 + + +class Signal: + """A notification emitter. + + :param doc: The docstring for the signal. + """ + + ANY = ANY + """An alias for the :data:`~blinker.ANY` sender symbol.""" + + set_class: type[set[t.Any]] = set + """The set class to use for tracking connected receivers and senders. + Python's ``set`` is unordered. If receivers must be dispatched in the order + they were connected, an ordered set implementation can be used. + + .. versionadded:: 1.7 + """ + + @cached_property + def receiver_connected(self) -> Signal: + """Emitted at the end of each :meth:`connect` call. + + The signal sender is the signal instance, and the :meth:`connect` + arguments are passed through: ``receiver``, ``sender``, and ``weak``. + + .. versionadded:: 1.2 + """ + return Signal(doc="Emitted after a receiver connects.") + + @cached_property + def receiver_disconnected(self) -> Signal: + """Emitted at the end of each :meth:`disconnect` call. + + The sender is the signal instance, and the :meth:`disconnect` arguments + are passed through: ``receiver`` and ``sender``. + + This signal is emitted **only** when :meth:`disconnect` is called + explicitly. This signal cannot be emitted by an automatic disconnect + when a weakly referenced receiver or sender goes out of scope, as the + instance is no longer be available to be used as the sender for this + signal. + + An alternative approach is available by subscribing to + :attr:`receiver_connected` and setting up a custom weakref cleanup + callback on weak receivers and senders. + + .. versionadded:: 1.2 + """ + return Signal(doc="Emitted after a receiver disconnects.") + + def __init__(self, doc: str | None = None) -> None: + if doc: + self.__doc__ = doc + + self.receivers: dict[ + t.Any, weakref.ref[c.Callable[..., t.Any]] | c.Callable[..., t.Any] + ] = {} + """The map of connected receivers. Useful to quickly check if any + receivers are connected to the signal: ``if s.receivers:``. The + structure and data is not part of the public API, but checking its + boolean value is. + """ + + self.is_muted: bool = False + self._by_receiver: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) + self._by_sender: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) + self._weak_senders: dict[t.Any, weakref.ref[t.Any]] = {} + + def connect(self, receiver: F, sender: t.Any = ANY, weak: bool = True) -> F: + """Connect ``receiver`` to be called when the signal is sent by + ``sender``. + + :param receiver: The callable to call when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument + along with any extra keyword arguments. + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. A receiver may be connected + to multiple senders by calling :meth:`connect` multiple times. + :param weak: Track the receiver with a :mod:`weakref`. The receiver will + be automatically disconnected when it is garbage collected. When + connecting a receiver defined within a function, set to ``False``, + otherwise it will be disconnected when the function scope ends. + """ + receiver_id = make_id(receiver) + sender_id = ANY_ID if sender is ANY else make_id(sender) + + if weak: + self.receivers[receiver_id] = make_ref( + receiver, self._make_cleanup_receiver(receiver_id) + ) + else: + self.receivers[receiver_id] = receiver + + self._by_sender[sender_id].add(receiver_id) + self._by_receiver[receiver_id].add(sender_id) + + if sender is not ANY and sender_id not in self._weak_senders: + # store a cleanup for weakref-able senders + try: + self._weak_senders[sender_id] = make_ref( + sender, self._make_cleanup_sender(sender_id) + ) + except TypeError: + pass + + if "receiver_connected" in self.__dict__ and self.receiver_connected.receivers: + try: + self.receiver_connected.send( + self, receiver=receiver, sender=sender, weak=weak + ) + except TypeError: + # TODO no explanation or test for this + self.disconnect(receiver, sender) + raise + + return receiver + + def connect_via(self, sender: t.Any, weak: bool = False) -> c.Callable[[F], F]: + """Connect the decorated function to be called when the signal is sent + by ``sender``. + + The decorated function will be called when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument along + with any extra keyword arguments. + + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. A receiver may be connected + to multiple senders by calling :meth:`connect` multiple times. + :param weak: Track the receiver with a :mod:`weakref`. The receiver will + be automatically disconnected when it is garbage collected. When + connecting a receiver defined within a function, set to ``False``, + otherwise it will be disconnected when the function scope ends.= + + .. versionadded:: 1.1 + """ + + def decorator(fn: F) -> F: + self.connect(fn, sender, weak) + return fn + + return decorator + + @contextmanager + def connected_to( + self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY + ) -> c.Generator[None, None, None]: + """A context manager that temporarily connects ``receiver`` to the + signal while a ``with`` block executes. When the block exits, the + receiver is disconnected. Useful for tests. + + :param receiver: The callable to call when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument + along with any extra keyword arguments. + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. + + .. versionadded:: 1.1 + """ + self.connect(receiver, sender=sender, weak=False) + + try: + yield None + finally: + self.disconnect(receiver) + + @contextmanager + def muted(self) -> c.Generator[None, None, None]: + """A context manager that temporarily disables the signal. No receivers + will be called if the signal is sent, until the ``with`` block exits. + Useful for tests. + """ + self.is_muted = True + + try: + yield None + finally: + self.is_muted = False + + def send( + self, + sender: t.Any | None = None, + /, + *, + _async_wrapper: c.Callable[ + [c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]]], c.Callable[..., t.Any] + ] + | None = None, + **kwargs: t.Any, + ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: + """Call all receivers that are connected to the given ``sender`` + or :data:`ANY`. Each receiver is called with ``sender`` as a positional + argument along with any extra keyword arguments. Return a list of + ``(receiver, return value)`` tuples. + + The order receivers are called is undefined, but can be influenced by + setting :attr:`set_class`. + + If a receiver raises an exception, that exception will propagate up. + This makes debugging straightforward, with an assumption that correctly + implemented receivers will not raise. + + :param sender: Call receivers connected to this sender, in addition to + those connected to :data:`ANY`. + :param _async_wrapper: Will be called on any receivers that are async + coroutines to turn them into sync callables. For example, could run + the receiver with an event loop. + :param kwargs: Extra keyword arguments to pass to each receiver. + + .. versionchanged:: 1.7 + Added the ``_async_wrapper`` argument. + """ + if self.is_muted: + return [] + + results = [] + + for receiver in self.receivers_for(sender): + if iscoroutinefunction(receiver): + if _async_wrapper is None: + raise RuntimeError("Cannot send to a coroutine function.") + + result = _async_wrapper(receiver)(sender, **kwargs) + else: + result = receiver(sender, **kwargs) + + results.append((receiver, result)) + + return results + + async def send_async( + self, + sender: t.Any | None = None, + /, + *, + _sync_wrapper: c.Callable[ + [c.Callable[..., t.Any]], c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]] + ] + | None = None, + **kwargs: t.Any, + ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: + """Await all receivers that are connected to the given ``sender`` + or :data:`ANY`. Each receiver is called with ``sender`` as a positional + argument along with any extra keyword arguments. Return a list of + ``(receiver, return value)`` tuples. + + The order receivers are called is undefined, but can be influenced by + setting :attr:`set_class`. + + If a receiver raises an exception, that exception will propagate up. + This makes debugging straightforward, with an assumption that correctly + implemented receivers will not raise. + + :param sender: Call receivers connected to this sender, in addition to + those connected to :data:`ANY`. + :param _sync_wrapper: Will be called on any receivers that are sync + callables to turn them into async coroutines. For example, + could call the receiver in a thread. + :param kwargs: Extra keyword arguments to pass to each receiver. + + .. versionadded:: 1.7 + """ + if self.is_muted: + return [] + + results = [] + + for receiver in self.receivers_for(sender): + if not iscoroutinefunction(receiver): + if _sync_wrapper is None: + raise RuntimeError("Cannot send to a non-coroutine function.") + + result = await _sync_wrapper(receiver)(sender, **kwargs) + else: + result = await receiver(sender, **kwargs) + + results.append((receiver, result)) + + return results + + def has_receivers_for(self, sender: t.Any) -> bool: + """Check if there is at least one receiver that will be called with the + given ``sender``. A receiver connected to :data:`ANY` will always be + called, regardless of sender. Does not check if weakly referenced + receivers are still live. See :meth:`receivers_for` for a stronger + search. + + :param sender: Check for receivers connected to this sender, in addition + to those connected to :data:`ANY`. + """ + if not self.receivers: + return False + + if self._by_sender[ANY_ID]: + return True + + if sender is ANY: + return False + + return make_id(sender) in self._by_sender + + def receivers_for( + self, sender: t.Any + ) -> c.Generator[c.Callable[..., t.Any], None, None]: + """Yield each receiver to be called for ``sender``, in addition to those + to be called for :data:`ANY`. Weakly referenced receivers that are not + live will be disconnected and skipped. + + :param sender: Yield receivers connected to this sender, in addition + to those connected to :data:`ANY`. + """ + # TODO: test receivers_for(ANY) + if not self.receivers: + return + + sender_id = make_id(sender) + + if sender_id in self._by_sender: + ids = self._by_sender[ANY_ID] | self._by_sender[sender_id] + else: + ids = self._by_sender[ANY_ID].copy() + + for receiver_id in ids: + receiver = self.receivers.get(receiver_id) + + if receiver is None: + continue + + if isinstance(receiver, weakref.ref): + strong = receiver() + + if strong is None: + self._disconnect(receiver_id, ANY_ID) + continue + + yield strong + else: + yield receiver + + def disconnect(self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY) -> None: + """Disconnect ``receiver`` from being called when the signal is sent by + ``sender``. + + :param receiver: A connected receiver callable. + :param sender: Disconnect from only this sender. By default, disconnect + from all senders. + """ + sender_id: c.Hashable + + if sender is ANY: + sender_id = ANY_ID + else: + sender_id = make_id(sender) + + receiver_id = make_id(receiver) + self._disconnect(receiver_id, sender_id) + + if ( + "receiver_disconnected" in self.__dict__ + and self.receiver_disconnected.receivers + ): + self.receiver_disconnected.send(self, receiver=receiver, sender=sender) + + def _disconnect(self, receiver_id: c.Hashable, sender_id: c.Hashable) -> None: + if sender_id == ANY_ID: + if self._by_receiver.pop(receiver_id, None) is not None: + for bucket in self._by_sender.values(): + bucket.discard(receiver_id) + + self.receivers.pop(receiver_id, None) + else: + self._by_sender[sender_id].discard(receiver_id) + self._by_receiver[receiver_id].discard(sender_id) + + def _make_cleanup_receiver( + self, receiver_id: c.Hashable + ) -> c.Callable[[weakref.ref[c.Callable[..., t.Any]]], None]: + """Create a callback function to disconnect a weakly referenced + receiver when it is garbage collected. + """ + + def cleanup(ref: weakref.ref[c.Callable[..., t.Any]]) -> None: + # If the interpreter is shutting down, disconnecting can result in a + # weird ignored exception. Don't call it in that case. + if not sys.is_finalizing(): + self._disconnect(receiver_id, ANY_ID) + + return cleanup + + def _make_cleanup_sender( + self, sender_id: c.Hashable + ) -> c.Callable[[weakref.ref[t.Any]], None]: + """Create a callback function to disconnect all receivers for a weakly + referenced sender when it is garbage collected. + """ + assert sender_id != ANY_ID + + def cleanup(ref: weakref.ref[t.Any]) -> None: + self._weak_senders.pop(sender_id, None) + + for receiver_id in self._by_sender.pop(sender_id, ()): + self._by_receiver[receiver_id].discard(sender_id) + + return cleanup + + def _cleanup_bookkeeping(self) -> None: + """Prune unused sender/receiver bookkeeping. Not threadsafe. + + Connecting & disconnecting leaves behind a small amount of bookkeeping + data. Typical workloads using Blinker, for example in most web apps, + Flask, CLI scripts, etc., are not adversely affected by this + bookkeeping. + + With a long-running process performing dynamic signal routing with high + volume, e.g. connecting to function closures, senders are all unique + object instances. Doing all of this over and over may cause memory usage + to grow due to extraneous bookkeeping. (An empty ``set`` for each stale + sender/receiver pair.) + + This method will prune that bookkeeping away, with the caveat that such + pruning is not threadsafe. The risk is that cleanup of a fully + disconnected receiver/sender pair occurs while another thread is + connecting that same pair. If you are in the highly dynamic, unique + receiver/sender situation that has lead you to this method, that failure + mode is perhaps not a big deal for you. + """ + for mapping in (self._by_sender, self._by_receiver): + for ident, bucket in list(mapping.items()): + if not bucket: + mapping.pop(ident, None) + + def _clear_state(self) -> None: + """Disconnect all receivers and senders. Useful for tests.""" + self._weak_senders.clear() + self.receivers.clear() + self._by_sender.clear() + self._by_receiver.clear() + + +class NamedSignal(Signal): + """A named generic notification emitter. The name is not used by the signal + itself, but matches the key in the :class:`Namespace` that it belongs to. + + :param name: The name of the signal within the namespace. + :param doc: The docstring for the signal. + """ + + def __init__(self, name: str, doc: str | None = None) -> None: + super().__init__(doc) + + #: The name of this signal. + self.name: str = name + + def __repr__(self) -> str: + base = super().__repr__() + return f"{base[:-1]}; {self.name!r}>" # noqa: E702 + + +class Namespace(dict[str, NamedSignal]): + """A dict mapping names to signals.""" + + def signal(self, name: str, doc: str | None = None) -> NamedSignal: + """Return the :class:`NamedSignal` for the given ``name``, creating it + if required. Repeated calls with the same name return the same signal. + + :param name: The name of the signal. + :param doc: The docstring of the signal. + """ + if name not in self: + self[name] = NamedSignal(name, doc) + + return self[name] + + +class _PNamespaceSignal(t.Protocol): + def __call__(self, name: str, doc: str | None = None) -> NamedSignal: ... + + +default_namespace: Namespace = Namespace() +"""A default :class:`Namespace` for creating named signals. :func:`signal` +creates a :class:`NamedSignal` in this namespace. +""" + +signal: _PNamespaceSignal = default_namespace.signal +"""Return a :class:`NamedSignal` in :data:`default_namespace` with the given +``name``, creating it if required. Repeated calls with the same name return the +same signal. +""" diff --git a/venv/lib/python3.12/site-packages/blinker/py.typed b/venv/lib/python3.12/site-packages/blinker/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/INSTALLER b/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/METADATA b/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/METADATA new file mode 100644 index 00000000..00bad311 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/METADATA @@ -0,0 +1,84 @@ +Metadata-Version: 2.4 +Name: click +Version: 8.3.2 +Summary: Composable command line interface toolkit +Maintainer-email: Pallets +Requires-Python: >=3.10 +Description-Content-Type: text/markdown +License-Expression: BSD-3-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Typing :: Typed +License-File: LICENSE.txt +Requires-Dist: colorama; platform_system == 'Windows' +Project-URL: Changes, https://click.palletsprojects.com/page/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/click/ + +

+ +# Click + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +## A Simple Example + +```python +import click + +@click.command() +@click.option("--count", default=1, help="Number of greetings.") +@click.option("--name", prompt="Your name", help="The person to greet.") +def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + +if __name__ == '__main__': + hello() +``` + +``` +$ python hello.py --count=3 +Your name: Click +Hello, Click! +Hello, Click! +Hello, Click! +``` + + +## Donate + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + +## Contributing + +See our [detailed contributing documentation][contrib] for many ways to +contribute, including reporting issues, requesting features, asking or answering +questions, and making PRs. + +[contrib]: https://palletsprojects.com/contributing/ + diff --git a/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/RECORD b/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/RECORD new file mode 100644 index 00000000..5bd5e90f --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/RECORD @@ -0,0 +1,40 @@ +click-8.3.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.3.2.dist-info/METADATA,sha256=yA3Hu5bMxtntTd4QrI8hTFAc58rHSPhDbP6bklbUQkA,2621 +click-8.3.2.dist-info/RECORD,, +click-8.3.2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +click-8.3.2.dist-info/licenses/LICENSE.txt,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click/__init__.py,sha256=6YyS1aeyknZ0LYweWozNZy0A9nZ_11wmYIhv3cbQrYo,4473 +click/__pycache__/__init__.cpython-312.pyc,, +click/__pycache__/_compat.cpython-312.pyc,, +click/__pycache__/_termui_impl.cpython-312.pyc,, +click/__pycache__/_textwrap.cpython-312.pyc,, +click/__pycache__/_utils.cpython-312.pyc,, +click/__pycache__/_winconsole.cpython-312.pyc,, +click/__pycache__/core.cpython-312.pyc,, +click/__pycache__/decorators.cpython-312.pyc,, +click/__pycache__/exceptions.cpython-312.pyc,, +click/__pycache__/formatting.cpython-312.pyc,, +click/__pycache__/globals.cpython-312.pyc,, +click/__pycache__/parser.cpython-312.pyc,, +click/__pycache__/shell_completion.cpython-312.pyc,, +click/__pycache__/termui.cpython-312.pyc,, +click/__pycache__/testing.cpython-312.pyc,, +click/__pycache__/types.cpython-312.pyc,, +click/__pycache__/utils.cpython-312.pyc,, +click/_compat.py,sha256=v3xBZkFbvA1BXPRkFfBJc6-pIwPI7345m-kQEnpVAs4,18693 +click/_termui_impl.py,sha256=rgCb3On8X5A4200rA5L6i13u5iapmFer7sru57Jy6zA,27093 +click/_textwrap.py,sha256=BOae0RQ6vg3FkNgSJyOoGzG1meGMxJ_ukWVZKx_v-0o,1400 +click/_utils.py,sha256=kZwtTf5gMuCilJJceS2iTCvRvCY-0aN5rJq8gKw7p8g,943 +click/_winconsole.py,sha256=_vxUuUaxwBhoR0vUWCNuHY8VUefiMdCIyU2SXPqoF-A,8465 +click/core.py,sha256=7db9qr_wqXbQriDHCDc26OK0MsaLCSt4yrz14Kn7AEQ,132905 +click/decorators.py,sha256=5P7abhJtAQYp_KHgjUvhMv464ERwOzrv2enNknlwHyQ,18461 +click/exceptions.py,sha256=8utf8w6V5hJXMnO_ic1FNrtbwuEn1NUu1aDwV8UqnG4,9954 +click/formatting.py,sha256=RVfwwr0rwWNpgGr8NaHodPzkIr7_tUyVh_nDdanLMNc,9730 +click/globals.py,sha256=gM-Nh6A4M0HB_SgkaF5M4ncGGMDHc_flHXu9_oh4GEU,1923 +click/parser.py,sha256=Q31pH0FlQZEq-UXE_ABRzlygEfvxPTuZbWNh4xfXmzw,19010 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=Cc4GQUFuWpfQBa9sF5qXeeYI7n3tI_1k6ZdSn4BZbT0,20994 +click/termui.py,sha256=hqCEjNndU-nzW08nRAkBaVgfZp_FdCA9KxfIWlKYaMc,31037 +click/testing.py,sha256=LjNfHqNctxc3GfRkLgifO6gnRetblh8yGXzjw4FPFCQ,18978 +click/types.py,sha256=ek54BNSFwPKsqtfT7jsqcc4WHui8AIFVMKM4oVZIXhc,39927 +click/utils.py,sha256=gCUoewdAhA-QLBUUHxrLh4uj6m7T1WjZZMNPvR0I7YA,20257 diff --git a/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/WHEEL b/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/WHEEL new file mode 100644 index 00000000..d8b9936d --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/licenses/LICENSE.txt b/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/licenses/LICENSE.txt new file mode 100644 index 00000000..d12a8491 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click-8.3.2.dist-info/licenses/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2014 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.12/site-packages/click/__init__.py b/venv/lib/python3.12/site-packages/click/__init__.py new file mode 100644 index 00000000..1aa547c5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/click/__init__.py @@ -0,0 +1,123 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" + +from __future__ import annotations + +from .core import Argument as Argument +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + + +def __getattr__(name: str) -> object: + import warnings + + if name == "BaseCommand": + from .core import _BaseCommand + + warnings.warn( + "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Command' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _BaseCommand + + if name == "MultiCommand": + from .core import _MultiCommand + + warnings.warn( + "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Group' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _MultiCommand + + if name == "OptionParser": + from .parser import _OptionParser + + warnings.warn( + "'OptionParser' is deprecated and will be removed in Click 9.0. The" + " old parser is available in 'optparse'.", + DeprecationWarning, + stacklevel=2, + ) + return _OptionParser + + if name == "__version__": + import importlib.metadata + import warnings + + warnings.warn( + "The '__version__' attribute is deprecated and will be removed in" + " Click 9.1. Use feature detection or" + " 'importlib.metadata.version(\"click\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("click") + + raise AttributeError(name) diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e715ae21eaa24e7083fded7a79d0b8675bd5ab2f GIT binary patch literal 4067 zcmbW4NpBm;6~~(r_f?A*+1ApMY*Dl&YOyU_w!CPyHJOnNOU59TL!-%VNo_T|iRz{_ zGOR%mnnAbQ)p1vl13z z<_I_k!DVVGa|#B0cS;)ISbB- z9CHrbCAyfqz}=#oxf|RgdYF5_y`q=77koyXVLk&sE6y^X1)me=n9tegD}ADm`8@c7 zxWL>8z9=p-UjSbcmzXbt`$a$VCGdb4VD1MGib3W9@Q@f{9t5jGWgfDJDb`2Yrl&ftPtHyC(DL`NEY_`+_ptb`RaR+5%S)oa>Uw&~HjBwO z^m8wSf|TxHs_t8^YmCN8_SZI)?9qu=q(&uD1m5q4Mw zL^rF=-oz7K>J`&BCCqWX%`Nsqfq=QU>^eJ^tneN*M6TgDvfK2Q+B5CyslAutsa18) z(+sohO5JzKdGusrn)~3ixve<2-O_W6$`K#B(m3^(Jusz*B~LAp#gZ%OKwV3^(F(;g zPv13R%fi+;Udxh(rTv&$er8rbE1RvOB-Sk3T$PeH+VzR$d3Y4sr=&OBE!@^g*plqK zH6!yF0a$Y>LXAKs4y9hzn#b$iHGQpIlM?r(ot`>UwoRP^6KjT-_HLHPFR z;ib*Zk70@~@3~f)I^!$4Z*E%^;xNSo-;uSCHy5`7$u-;6{inLaVeVYtAY**pIWaY! zrR}3CPk*BOw8!Yy_S5x^N91U`hMy-O^=#wG)6M18t*zA+G{euHt@C$ttGOMHq_%_g z9Xy*fgF>iRtD0d^#JZ_jvK(C=X`U}ly#iy7j1=-yQ^Kv94*Mz752y8s08m0h?>>hW zg*0R+wsIVLsTdvmSRx;6HjS7T8SN_ynkR%nGbpjv!^V3y36N{A4g8k{o^Oheb8Iv zc~9fC?fU#|d#k}l*O$$>Zz=xyR{u+-W&KsC(7hq)y(UO4A%!;6mkh%=9%#uwZ9 zwpN}pzF}6SS;iBKoQwQq#Kz9DaYfVeMW0Hys+}^ z&7Z#JHVU6>8)}vPbG1CsEt?7rg(>egt6{zvq%^ISZ?zY{r-}JCOI}5alUAwbW8zP_ zftkh)?3lV=lP0dtZxkrim1-V!8~Ge%xrj=n8@jI-n|l}^DN}tPDYV53i4&$mI*1?Y z(m|s3f-uUADOc%13X?JDS!q5Ss2iU)M#**Rjy6Ide@2a-|6VwVx3&<(ntKSci>)&^ z5`HuYQ(lrA@WG3+>xRq3iAyhvZk0d(-kw59+xMz^+4QE$u3=8nOTZ*i(|BMy2UCc^ zRF&Uet`}!!r##CyCoyLqwE)xT)Ki*fIhL=V3h^9$_)jq<1?|aBEmDl0k z?_#On9Dg0_ugAx}i+BAl^V`hZj!q+gZNt?c8$R$V-hVp0&^CPTe+{S2yp9jQj-Nl7(UEx=dLLCXL$sw7Zz+l`WtFZA z-{ywD$_;;;D}0qJe3AbqH~T6+`$Mwtk7vF~4!#NxzQZwBW9sVS0jqUm-N^*^4f@G4U z2nixSO13Ab$S_SZLo!Qpo#Y0|O_Dj1TO_wh=1K05+$C9n1d*C=VRQG$aG&G>$wQJw zk|h#)SpTHRUl9Ks62uo8uj}`vg(ejZ@2`-zouN?ZpB<4<_+MEiH2M!^tgc+HE4NwI zx-wE%#_P&NUAbCUmg>|y%K+mIw%_5wurDych`B{>f~V`s95)}ZD-44XZX2t>IYweR q0RF)^R98mpN}gf3u3W1t1KcdMx?r4S#>kt@^9+Ue(V5T?pZ7l$k!P|1 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_compat.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bdde04660f23207e6df35d31f2c5d6611f92d532 GIT binary patch literal 24186 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_termui_impl.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_termui_impl.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26e4766c06d359948aa933de8b57134fde62097d GIT binary patch literal 31555 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 C@d}mz literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_textwrap.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_textwrap.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3be274c77500219da86ecf58e2981404028d8779 GIT binary patch literal 2415 zcmaJ@TW=dh6rR0!y}l$)oJ;E@PTiylF>T`B5p4xZ8-XIE1Oy1-Qmr=LZL`jLozAY~ zI9;O(<-q}@=7|_pNZ|=k#E_c`GFw z@--DF8bde%nC*+Ox`I>`XA~4?J?Pn=;!>DJKF+DUA}H)4zvje&lHCdi8X?C=MdEu0 zQoV=e*;z>C?7e2mgM@U<_II^Z7UTFMo42_II6sA1W9w^qmyMDR-m#)jJZLit8v>LU zY(YV`s|)4bwqV1l0TOLC*_hFBwcTq@E(X~mjc@@pIb^eDDF=k96CNfykHA5xt8d447%4&)=O}yr;oKPiQy=oC&mraXsi3|v=nx+TRM|Q@O zcVm!ore%soQC5ji8Z`85B{nq5tWbx8Hf27hkP?#)U z-0&azsWnt_6qDtiSg9v=x97yB|74Xz!LDt{iV}?kqU7tRzL962 zPrW>6qj~0e9yYVkM6*e|k!qHW*&WTU9c5_ zdmm+)Z@DZJWhSC5Y1RzQ(qvuI3`I38(rRX=O!ayOu(K59aWj-f1jizn))i3g@#>&! z&v=^jEGgUY0UE#$9Ko^^b>UzmB^ou|U~A5R_yaw@iMGOr%i-gt@bPuAs}eaM5rh29OZcXhBN4sD9PRi5{pzwd9|>N{N+yXQYt88}xM z+w}K6h8N#|X3x^!W-sLXXp|d0#@rG3(Ga@h4Nx3v8I5vxqI7)^Noen=b*LWzte|Ts z&o*=^Ch0UwQ0|WXJz)%8c?qEp;N|DJJP%Z&hqZ4&cjFXhW(@k7A1s7@YM6oro21{* zzG1s=L$|z*zF?pBpif`s;TB2y_L;)e-nDw0b}395Nf>WbARg7oq*N?hD(x$}X3>^o zV)~A74;6lD$|uvB5f$n@;b|`*ES@8rrp!YN0uLMpGn3X&>99ixU!74>+R$?g2O9a% zpctZ_2@p3?r72Kge+%`kp8o39FJHaefBMJJnWgcLWw1f`iL{}@=R!A2IB>+n zfEL;k@>F7E+@i;r#+Rwl6|fql{xK@3RSiYmM3f{$PN|YaypohkD;emc67fsY^^C07 zHTWR4r*y#)DqaNf4XV{%`_M2w=b8`!m~VnuL{+xMb!scrS9QU&DxkKWs+%Ga1v{!9 zioB?$t?Hx5kG##>O%xqLLi?Yr*L8lI15~Bx(LtXpTu(;xzzqe3Ud_M(K%tltp9B(V zT~`yezZD~}cfwJ@(_xJl8N7nefr9oqvmZp2 kV;JTE>iZq_-baB4XyB1>lnE~nm4kz&;NV{fCbb~{0;&u)nE(I) literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_utils.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc9a4d3bc828c4250617f3711cd7bdbe32c02bcb GIT binary patch literal 1195 zcmZ`&%}*0S6rb6Twv>X<3PcUMaxhs#cfk)JNQfj>F% zNjg)XlM57h@rZ&QfPn@LKpTV_&(Q)7s=k`|j}|fngWC|aaFLfqKQf7J&$O|%_AX@? za#Dd{L-HKSzzdX>NYyH>=vDy}B^s6JQWR>yx<#D9!GK}x+(c?Z>g(&3l2R?HG*bza zhL$`pPX<*rkMl&y7riCNRvdSc>B$}%qYlp@rII@X>sn+-KtFgfUg_`+$krKaFk78N zO=tTqJ$ULZ)N=ve1)upzAaiLSDl+dkAe6CmLJ;yJ2-$iLQzWpRj$RGw-@ z)p3_eUe92nW*h@2S50pz$xxMC!S;%3ne%GU-$SMMfl5pdCuz*GzR1EXGqbFsf@fN6 z3ha+A&btV$wbmx4)F^g)NEa3B;3VF@qiie3arr1Nms;=d-Q62Ke6jyx?@8%ee<{*` z!8%LJoF8(rS8#2gC!Q-u+y+YfQG{}iQLtEFF@#!c{6BAe0aW| z`qt6;>2*oCeknk4Ac$l^a*P$UP@rYff?RgKzJTe)^Fv-SZ`zAK^n8Kc#NZ!o)WL0M zUg2>%N>{i1@DvK_j8CB}m3A=yzjgYNp`iC+SE2@Xkc7RUG7lmA0lLmW<986h48`EU zdvB|_S-b=kmT!ZG)~)8v=5_G|44fuz9Vfbv65S=xQsx9Glw+VVwk2=MA5%xso9p2d a@c6W?eP?!iwsc25YU?S1_z8GgDdG=XgBP{{ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/_winconsole.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/_winconsole.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc70f5a155b5749211ed00edde8b3609131b9751 GIT binary patch literal 11760 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/click/__pycache__/core.cpython-312.pyc b/venv/lib/python3.12/site-packages/click/__pycache__/core.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d67a3af66c9e77589ce7d3618f44a47d15c7a0c3 GIT binary patch literal 134887 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

t9yz7!Bgmi!iZT$ifH+cth z1TeQCVc@j2ODfy6Eej;uD^e_{O<4#b$XCK_lS+%IwzEPry=Cl{BNPl$IhH+=e$$|E5sC4S z6aQ}n@R+63eayA|4cGot&izx)`Y{(k_*2gLQ_l7=SN}0r^D*cDn5$DlMDQf=F&9Gk zHxv(k%y~cNnpgyHxBSfD`PkUT{_v9bSSV{gG|)cHO(Hh*l4U^WI? zmW7MS3(3`%k&OS~D(8d7;wAmtw#&BF)_s}4&?;A*a|M=8zA<@zay8hKarM4p&)KV& zw!ATLeqi}j#@_IXB^T^oABdP&4^g{)_eEkK|GC*QyodYoo{AB(;V(=&g#Ql}>sE6B literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/script/__pycache__/revision.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/script/__pycache__/revision.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2325c8a3cb05c2130389063a28a30e559956aac4 GIT binary patch literal 62503 zcmd?S33OZ6nI`zK5-S0cAOR8}`M48YMUmQfi`uu?vMaGH7Y!4TM4F5I09qykR%FNB zp&X@5*-43xoid%IJ5Qm?RllXAodw2cs_V4#UI-NES*K^JPY3SSv$Ng{gAYW!Npj#O>aNJcc z%!PF$+^Fu9PDf$=i2jtG{Tfag@M{<`j+#!H*f%4>=2K=CHX&Ses)&Wn2wP5BShxsb z>nSS>TM)LLvazrgVf!gN3)>KOoN}61r%GA41mUt%Wh`8Zu;-MAh072wKUL1c9)v4SRj_b5!roI}7OoiajaHtj zLf9Lw81W-q$-=&o%F)27fR2ll3ueHg5^1VWRpZ$ksfiRvYAONBh$m7Lt~#Tq@EHT$ zPt~&Tj)+T)!D2bYuoQ!5F*GT|dg)7D#2fZUYQ+3b)#E9^@@$CMP>x@E--st~SdUtG z!qsZY5m(sHo>*;8HR-uXMY!fIo!H~2n%Q$L>Qzs%-sa!ZOJ7>>#W4H@@kx6R*D0xk zXSs0wcXg-Q^xOe1-0%VyZj6+?g)v0esdo0Z32&Pt9m?BI_O=CYTO*yy+b;Gth_`K; zx83Y*JKlC^-mYVBJMp$l^R|b*?Z(@6ksc-A_3UjA-mcfY?PYI!@wP9rKGOS^QL1qt zd%pqiH!5)0z}{`bJHPU7W5gZa9^U+d@hveScWP6_ZRNy&;T_>E+Aqrco#Cx77*A~q z?+S0j@8(H;aQ81LeNdORhsMUnW1-m4_*nEfo(+4(Ch=q08yXo2JvkCV*f-dyD(+$mj{JEfORZdj)xT*d?qsXO!vsplilYhW9P=ldb@hocSiyK zPNaD%bQZ}%BazW3hX%W&gTm1HShpbcNZ0wvtYu(e5Cd^wV5)x2#&^ka`cSQ?6}OAr zaxr&6cUXtrhU1Q3a|l8FX1xP4;0}c&W3iz#LlJ>SjnIa#!g{*3Q#|97jSa)cFeV?9vwXaR7hlQ)`!S`o6T$<4CL(lobv^z#IuflK3ynqw z2C}w+fzk2s#0Z5Q0|QS_ghs?KLN^sky(X-qTNmA$9B6e)x2z? zz_OWhw=5U2poR1JmaQyk<9vZ-I}19vx~63(3l?*>eY$%t3X7#s0Xf4}TC8;|4t!CE z>!dEI&pPF%?-PV^;dsy>Y(OMoBi%O9EwA=l=pEg5;xB5#?FBAlw1_3s`<#+7g6R?` z%GWeyYo|z9Kf|5TVTw3T8D_X=b*GFcxu79i{!nP_Y~=WOY!4QXC>9=8!$B!amTwX6 zc-F0)M&Mhzt6W?c=LXm(y2bT)k{Zu(F=>_NxJ!mO_YEfpEG4dcRu|P{XV$-_3mf8k z_31lCd6&{*@&%2@Keoz{;xV=`6sCB7D9U3!;zMKjFV>MCKf?>s(C33jaZs{xYl=?L zbj7F{8pG5az{gh9Q4_<8DKLV`IxrBWs$yzh6i3W%JWbUU1P7*#nB88)iBhlq8uP}? zRrqIμ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 nqabglW0|X4<9Y5Fhb{gBBjHL5O None: + self.dir = _preserving_path_as_str(dir) + self.version_locations = [ + _preserving_path_as_str(p) for p in version_locations or () + ] + self.file_template = file_template + self.truncate_slug_length = truncate_slug_length or 40 + self.sourceless = sourceless + self.output_encoding = output_encoding + self.revision_map = revision.RevisionMap(self._load_revisions) + self.timezone = timezone + self.hooks = hooks + self.recursive_version_locations = recursive_version_locations + self.messaging_opts = messaging_opts + + if not os.access(dir, os.F_OK): + raise util.CommandError( + f"Path doesn't exist: {dir}. Please use " + "the 'init' command to create a new " + "scripts folder." + ) + + @property + def versions(self) -> str: + """return a single version location based on the sole path passed + within version_locations. + + If multiple version locations are configured, an error is raised. + + + """ + return str(self._singular_version_location) + + @util.memoized_property + def _singular_version_location(self) -> Path: + loc = self._version_locations + if len(loc) > 1: + raise util.CommandError("Multiple version_locations present") + else: + return loc[0] + + @util.memoized_property + def _version_locations(self) -> Sequence[Path]: + if self.version_locations: + return [ + util.coerce_resource_to_filename(location).absolute() + for location in self.version_locations + ] + else: + return [Path(self.dir, "versions").absolute()] + + def _load_revisions(self) -> Iterator[Script]: + paths = [vers for vers in self._version_locations if vers.exists()] + + dupes = set() + for vers in paths: + for file_path in Script._list_py_dir(self, vers): + real_path = file_path.resolve() + if real_path in dupes: + util.warn( + f"File {real_path} loaded twice! ignoring. " + "Please ensure version_locations is unique." + ) + continue + dupes.add(real_path) + + script = Script._from_path(self, real_path) + if script is None: + continue + yield script + + @classmethod + def from_config(cls, config: Config) -> ScriptDirectory: + """Produce a new :class:`.ScriptDirectory` given a :class:`.Config` + instance. + + The :class:`.Config` need only have the ``script_location`` key + present. + + """ + script_location = config.get_alembic_option("script_location") + if script_location is None: + raise util.CommandError( + "No 'script_location' key found in configuration." + ) + truncate_slug_length: Optional[int] + tsl = config.get_alembic_option("truncate_slug_length") + if tsl is not None: + truncate_slug_length = int(tsl) + else: + truncate_slug_length = None + + prepend_sys_path = config.get_prepend_sys_paths_list() + if prepend_sys_path: + sys.path[:0] = prepend_sys_path + + rvl = config.get_alembic_boolean_option("recursive_version_locations") + return ScriptDirectory( + util.coerce_resource_to_filename(script_location), + file_template=config.get_alembic_option( + "file_template", _default_file_template + ), + truncate_slug_length=truncate_slug_length, + sourceless=config.get_alembic_boolean_option("sourceless"), + output_encoding=config.get_alembic_option( + "output_encoding", "utf-8" + ), + version_locations=config.get_version_locations_list(), + timezone=config.get_alembic_option("timezone"), + hooks=config.get_hooks_list(), + recursive_version_locations=rvl, + messaging_opts=config.messaging_opts, + ) + + @contextmanager + def _catch_revision_errors( + self, + ancestor: Optional[str] = None, + multiple_heads: Optional[str] = None, + start: Optional[str] = None, + end: Optional[str] = None, + resolution: Optional[str] = None, + ) -> Iterator[None]: + try: + yield + except revision.RangeNotAncestorError as rna: + if start is None: + start = cast(Any, rna.lower) + if end is None: + end = cast(Any, rna.upper) + if not ancestor: + ancestor = ( + "Requested range %(start)s:%(end)s does not refer to " + "ancestor/descendant revisions along the same branch" + ) + ancestor = ancestor % {"start": start, "end": end} + raise util.CommandError(ancestor) from rna + except revision.MultipleHeads as mh: + if not multiple_heads: + multiple_heads = ( + "Multiple head revisions are present for given " + "argument '%(head_arg)s'; please " + "specify a specific target revision, " + "'@%(head_arg)s' to " + "narrow to a specific head, or 'heads' for all heads" + ) + multiple_heads = multiple_heads % { + "head_arg": end or mh.argument, + "heads": util.format_as_comma(mh.heads), + } + raise util.CommandError(multiple_heads) from mh + except revision.ResolutionError as re: + if resolution is None: + resolution = "Can't locate revision identified by '%s'" % ( + re.argument + ) + raise util.CommandError(resolution) from re + except revision.RevisionError as err: + raise util.CommandError(err.args[0]) from err + + def walk_revisions( + self, base: str = "base", head: str = "heads" + ) -> Iterator[Script]: + """Iterate through all revisions. + + :param base: the base revision, or "base" to start from the + empty revision. + + :param head: the head revision; defaults to "heads" to indicate + all head revisions. May also be "head" to indicate a single + head revision. + + """ + with self._catch_revision_errors(start=base, end=head): + for rev in self.revision_map.iterate_revisions( + head, base, inclusive=True, assert_relative_length=False + ): + yield cast(Script, rev) + + def get_revisions(self, id_: _GetRevArg) -> Tuple[Script, ...]: + """Return the :class:`.Script` instance with the given rev identifier, + symbolic name, or sequence of identifiers. + + """ + with self._catch_revision_errors(): + return cast( + Tuple[Script, ...], + self.revision_map.get_revisions(id_), + ) + + def get_all_current(self, id_: Tuple[str, ...]) -> Set[Script]: + with self._catch_revision_errors(): + return cast(Set[Script], self.revision_map._get_all_current(id_)) + + def get_revision(self, id_: str) -> Script: + """Return the :class:`.Script` instance with the given rev id. + + .. seealso:: + + :meth:`.ScriptDirectory.get_revisions` + + """ + + with self._catch_revision_errors(): + return cast(Script, self.revision_map.get_revision(id_)) + + def as_revision_number( + self, id_: Optional[str] + ) -> Optional[Union[str, Tuple[str, ...]]]: + """Convert a symbolic revision, i.e. 'head' or 'base', into + an actual revision number.""" + + with self._catch_revision_errors(): + rev, branch_name = self.revision_map._resolve_revision_number(id_) + + if not rev: + # convert () to None + return None + elif id_ == "heads": + return rev + else: + return rev[0] + + def iterate_revisions( + self, + upper: Union[str, Tuple[str, ...], None], + lower: Union[str, Tuple[str, ...], None], + **kw: Any, + ) -> Iterator[Script]: + """Iterate through script revisions, starting at the given + upper revision identifier and ending at the lower. + + The traversal uses strictly the `down_revision` + marker inside each migration script, so + it is a requirement that upper >= lower, + else you'll get nothing back. + + The iterator yields :class:`.Script` objects. + + .. seealso:: + + :meth:`.RevisionMap.iterate_revisions` + + """ + return cast( + Iterator[Script], + self.revision_map.iterate_revisions(upper, lower, **kw), + ) + + def get_current_head(self) -> Optional[str]: + """Return the current head revision. + + If the script directory has multiple heads + due to branching, an error is raised; + :meth:`.ScriptDirectory.get_heads` should be + preferred. + + :return: a string revision number. + + .. seealso:: + + :meth:`.ScriptDirectory.get_heads` + + """ + with self._catch_revision_errors( + multiple_heads=( + "The script directory has multiple heads (due to branching)." + "Please use get_heads(), or merge the branches using " + "alembic merge." + ) + ): + return self.revision_map.get_current_head() + + def get_heads(self) -> List[str]: + """Return all "versioned head" revisions as strings. + + This is normally a list of length one, + unless branches are present. The + :meth:`.ScriptDirectory.get_current_head()` method + can be used normally when a script directory + has only one head. + + :return: a tuple of string revision numbers. + """ + return list(self.revision_map.heads) + + def get_base(self) -> Optional[str]: + """Return the "base" revision as a string. + + This is the revision number of the script that + has a ``down_revision`` of None. + + If the script directory has multiple bases, an error is raised; + :meth:`.ScriptDirectory.get_bases` should be + preferred. + + """ + bases = self.get_bases() + if len(bases) > 1: + raise util.CommandError( + "The script directory has multiple bases. " + "Please use get_bases()." + ) + elif bases: + return bases[0] + else: + return None + + def get_bases(self) -> List[str]: + """return all "base" revisions as strings. + + This is the revision number of all scripts that + have a ``down_revision`` of None. + + """ + return list(self.revision_map.bases) + + def _upgrade_revs( + self, destination: str, current_rev: str + ) -> List[RevisionStep]: + with self._catch_revision_errors( + ancestor="Destination %(end)s is not a valid upgrade " + "target from current head(s)", + end=destination, + ): + revs = self.iterate_revisions( + destination, current_rev, implicit_base=True + ) + return [ + migration.MigrationStep.upgrade_from_script( + self.revision_map, script + ) + for script in reversed(list(revs)) + ] + + def _downgrade_revs( + self, destination: str, current_rev: Optional[str] + ) -> List[RevisionStep]: + with self._catch_revision_errors( + ancestor="Destination %(end)s is not a valid downgrade " + "target from current head(s)", + end=destination, + ): + revs = self.iterate_revisions( + current_rev, destination, select_for_downgrade=True + ) + return [ + migration.MigrationStep.downgrade_from_script( + self.revision_map, script + ) + for script in revs + ] + + def _stamp_revs( + self, revision: _RevIdType, heads: _RevIdType + ) -> List[StampStep]: + with self._catch_revision_errors( + multiple_heads="Multiple heads are present; please specify a " + "single target revision" + ): + heads_revs = self.get_revisions(heads) + + steps = [] + + if not revision: + revision = "base" + + filtered_heads: List[Script] = [] + for rev in util.to_tuple(revision): + if rev: + filtered_heads.extend( + self.revision_map.filter_for_lineage( + cast(Sequence[Script], heads_revs), + rev, + include_dependencies=True, + ) + ) + filtered_heads = util.unique_list(filtered_heads) + + dests = self.get_revisions(revision) or [None] + + for dest in dests: + if dest is None: + # dest is 'base'. Return a "delete branch" migration + # for all applicable heads. + steps.extend( + [ + migration.StampStep( + head.revision, + None, + False, + True, + self.revision_map, + ) + for head in filtered_heads + ] + ) + continue + elif dest in filtered_heads: + # the dest is already in the version table, do nothing. + continue + + # figure out if the dest is a descendant or an + # ancestor of the selected nodes + descendants = set( + self.revision_map._get_descendant_nodes([dest]) + ) + ancestors = set(self.revision_map._get_ancestor_nodes([dest])) + + if descendants.intersection(filtered_heads): + # heads are above the target, so this is a downgrade. + # we can treat them as a "merge", single step. + assert not ancestors.intersection(filtered_heads) + todo_heads = [head.revision for head in filtered_heads] + step = migration.StampStep( + todo_heads, + dest.revision, + False, + False, + self.revision_map, + ) + steps.append(step) + continue + elif ancestors.intersection(filtered_heads): + # heads are below the target, so this is an upgrade. + # we can treat them as a "merge", single step. + todo_heads = [head.revision for head in filtered_heads] + step = migration.StampStep( + todo_heads, + dest.revision, + True, + False, + self.revision_map, + ) + steps.append(step) + continue + else: + # destination is in a branch not represented, + # treat it as new branch + step = migration.StampStep( + (), dest.revision, True, True, self.revision_map + ) + steps.append(step) + continue + + return steps + + def run_env(self) -> None: + """Run the script environment. + + This basically runs the ``env.py`` script present + in the migration environment. It is called exclusively + by the command functions in :mod:`alembic.command`. + + + """ + util.load_python_file(self.dir, "env.py") + + @property + def env_py_location(self) -> str: + return str(Path(self.dir, "env.py")) + + def _append_template(self, src: Path, dest: Path, **kw: Any) -> None: + with util.status( + f"Appending to existing {dest.absolute()}", + **self.messaging_opts, + ): + util.template_to_file( + src, + dest, + self.output_encoding, + append_with_newlines=True, + **kw, + ) + + def _generate_template(self, src: Path, dest: Path, **kw: Any) -> None: + with util.status( + f"Generating {dest.absolute()}", **self.messaging_opts + ): + util.template_to_file(src, dest, self.output_encoding, **kw) + + def _copy_file(self, src: Path, dest: Path) -> None: + with util.status( + f"Generating {dest.absolute()}", **self.messaging_opts + ): + shutil.copy(src, dest) + + def _ensure_directory(self, path: Path) -> None: + path = path.absolute() + if not path.exists(): + with util.status( + f"Creating directory {path}", **self.messaging_opts + ): + os.makedirs(path) + + def _generate_create_date(self) -> datetime.datetime: + if self.timezone is not None: + if ZoneInfo is None: + raise util.CommandError( + "Python >= 3.9 is required for timezone support or " + "the 'backports.zoneinfo' package must be installed." + ) + # First, assume correct capitalization + try: + tzinfo = ZoneInfo(self.timezone) + except ZoneInfoNotFoundError: + tzinfo = None + if tzinfo is None: + try: + tzinfo = ZoneInfo(self.timezone.upper()) + except ZoneInfoNotFoundError: + raise util.CommandError( + "Can't locate timezone: %s" % self.timezone + ) from None + + create_date = datetime.datetime.now( + tz=datetime.timezone.utc + ).astimezone(tzinfo) + else: + create_date = datetime.datetime.now() + return create_date + + def generate_revision( + self, + revid: str, + message: Optional[str], + head: Optional[_RevIdType] = None, + splice: Optional[bool] = False, + branch_labels: Optional[_RevIdType] = None, + version_path: Union[str, os.PathLike[str], None] = None, + file_template: Optional[str] = None, + depends_on: Optional[_RevIdType] = None, + **kw: Any, + ) -> Optional[Script]: + """Generate a new revision file. + + This runs the ``script.py.mako`` template, given + template arguments, and creates a new file. + + :param revid: String revision id. Typically this + comes from ``alembic.util.rev_id()``. + :param message: the revision message, the one passed + by the -m argument to the ``revision`` command. + :param head: the head revision to generate against. Defaults + to the current "head" if no branches are present, else raises + an exception. + :param splice: if True, allow the "head" version to not be an + actual head; otherwise, the selected head must be a head + (e.g. endpoint) revision. + + """ + if head is None: + head = "head" + + try: + Script.verify_rev_id(revid) + except revision.RevisionError as err: + raise util.CommandError(err.args[0]) from err + + with self._catch_revision_errors( + multiple_heads=( + "Multiple heads are present; please specify the head " + "revision on which the new revision should be based, " + "or perform a merge." + ) + ): + heads = cast( + Tuple[Optional["Revision"], ...], + self.revision_map.get_revisions(head), + ) + for h in heads: + assert h != "base" # type: ignore[comparison-overlap] + + if len(set(heads)) != len(heads): + raise util.CommandError("Duplicate head revisions specified") + + create_date = self._generate_create_date() + + if version_path is None: + if len(self._version_locations) > 1: + for head_ in heads: + if head_ is not None: + assert isinstance(head_, Script) + version_path = head_._script_path.parent + break + else: + raise util.CommandError( + "Multiple version locations present, " + "please specify --version-path" + ) + else: + version_path = self._singular_version_location + else: + version_path = Path(version_path) + + assert isinstance(version_path, Path) + norm_path = version_path.absolute() + for vers_path in self._version_locations: + if vers_path.absolute() == norm_path: + break + else: + raise util.CommandError( + f"Path {version_path} is not represented in current " + "version locations" + ) + + if self.version_locations: + self._ensure_directory(version_path) + + path = self._rev_path(version_path, revid, message, create_date) + self._ensure_directory(path.parent) + + if not splice: + for head_ in heads: + if head_ is not None and not head_.is_head: + raise util.CommandError( + "Revision %s is not a head revision; please specify " + "--splice to create a new branch from this revision" + % head_.revision + ) + + resolved_depends_on: Optional[List[str]] + if depends_on: + with self._catch_revision_errors(): + resolved_depends_on = [ + ( + dep + if dep in rev.branch_labels # maintain branch labels + else rev.revision + ) # resolve partial revision identifiers + for rev, dep in [ + (not_none(self.revision_map.get_revision(dep)), dep) + for dep in util.to_list(depends_on) + ] + ] + else: + resolved_depends_on = None + + self._generate_template( + Path(self.dir, "script.py.mako"), + path, + up_revision=str(revid), + down_revision=revision.tuple_rev_as_scalar( + tuple(h.revision if h is not None else None for h in heads) + ), + branch_labels=util.to_tuple(branch_labels), + depends_on=revision.tuple_rev_as_scalar(resolved_depends_on), + create_date=create_date, + comma=util.format_as_comma, + message=message if message is not None else ("empty message"), + **kw, + ) + + post_write_hooks = self.hooks + if post_write_hooks: + write_hooks._run_hooks(path, post_write_hooks) + + try: + script = Script._from_path(self, path) + except revision.RevisionError as err: + raise util.CommandError(err.args[0]) from err + if script is None: + return None + if branch_labels and not script.branch_labels: + raise util.CommandError( + "Version %s specified branch_labels %s, however the " + "migration file %s does not have them; have you upgraded " + "your script.py.mako to include the " + "'branch_labels' section?" + % (script.revision, branch_labels, script.path) + ) + self.revision_map.add_revision(script) + return script + + def _rev_path( + self, + path: Union[str, os.PathLike[str]], + rev_id: str, + message: Optional[str], + create_date: datetime.datetime, + ) -> Path: + epoch = int(create_date.timestamp()) + slug = "_".join(_slug_re.findall(message or "")).lower() + if len(slug) > self.truncate_slug_length: + slug = slug[: self.truncate_slug_length].rsplit("_", 1)[0] + "_" + filename = "%s.py" % ( + self.file_template + % { + "rev": rev_id, + "slug": slug, + "epoch": epoch, + "year": create_date.year, + "month": create_date.month, + "day": create_date.day, + "hour": create_date.hour, + "minute": create_date.minute, + "second": create_date.second, + } + ) + return Path(path) / filename + + +class Script(revision.Revision): + """Represent a single revision file in a ``versions/`` directory. + + The :class:`.Script` instance is returned by methods + such as :meth:`.ScriptDirectory.iterate_revisions`. + + """ + + def __init__( + self, + module: ModuleType, + rev_id: str, + path: Union[str, os.PathLike[str]], + ): + self.module = module + self.path = _preserving_path_as_str(path) + super().__init__( + rev_id, + module.down_revision, + branch_labels=util.to_tuple( + getattr(module, "branch_labels", None), default=() + ), + dependencies=util.to_tuple( + getattr(module, "depends_on", None), default=() + ), + ) + + module: ModuleType + """The Python module representing the actual script itself.""" + + path: str + """Filesystem path of the script.""" + + @property + def _script_path(self) -> Path: + return Path(self.path) + + _db_current_indicator: Optional[bool] = None + """Utility variable which when set will cause string output to indicate + this is a "current" version in some database""" + + @property + def doc(self) -> str: + """Return the docstring given in the script.""" + + return re.split("\n\n", self.longdoc)[0] + + @property + def longdoc(self) -> str: + """Return the docstring given in the script.""" + + doc = self.module.__doc__ + if doc: + if hasattr(self.module, "_alembic_source_encoding"): + doc = doc.decode( # type: ignore[attr-defined] + self.module._alembic_source_encoding + ) + return doc.strip() + else: + return "" + + @property + def log_entry(self) -> str: + entry = "Rev: %s%s%s%s%s\n" % ( + self.revision, + " (head)" if self.is_head else "", + " (branchpoint)" if self.is_branch_point else "", + " (mergepoint)" if self.is_merge_point else "", + " (current)" if self._db_current_indicator else "", + ) + if self.is_merge_point: + entry += "Merges: %s\n" % (self._format_down_revision(),) + else: + entry += "Parent: %s\n" % (self._format_down_revision(),) + + if self.dependencies: + entry += "Also depends on: %s\n" % ( + util.format_as_comma(self.dependencies) + ) + + if self.is_branch_point: + entry += "Branches into: %s\n" % ( + util.format_as_comma(self.nextrev) + ) + + if self.branch_labels: + entry += "Branch names: %s\n" % ( + util.format_as_comma(self.branch_labels), + ) + + entry += "Path: %s\n" % (self.path,) + + entry += "\n%s\n" % ( + "\n".join(" %s" % para for para in self.longdoc.splitlines()) + ) + return entry + + def __str__(self) -> str: + return "%s -> %s%s%s%s, %s" % ( + self._format_down_revision(), + self.revision, + " (head)" if self.is_head else "", + " (branchpoint)" if self.is_branch_point else "", + " (mergepoint)" if self.is_merge_point else "", + self.doc, + ) + + def _head_only( + self, + include_branches: bool = False, + include_doc: bool = False, + include_parents: bool = False, + tree_indicators: bool = True, + head_indicators: bool = True, + ) -> str: + text = self.revision + if include_parents: + if self.dependencies: + text = "%s (%s) -> %s" % ( + self._format_down_revision(), + util.format_as_comma(self.dependencies), + text, + ) + else: + text = "%s -> %s" % (self._format_down_revision(), text) + assert text is not None + if include_branches and self.branch_labels: + text += " (%s)" % util.format_as_comma(self.branch_labels) + if head_indicators or tree_indicators: + text += "%s%s%s" % ( + " (head)" if self._is_real_head else "", + ( + " (effective head)" + if self.is_head and not self._is_real_head + else "" + ), + " (current)" if self._db_current_indicator else "", + ) + if tree_indicators: + text += "%s%s" % ( + " (branchpoint)" if self.is_branch_point else "", + " (mergepoint)" if self.is_merge_point else "", + ) + if include_doc: + text += ", %s" % self.doc + return text + + def cmd_format( + self, + verbose: bool, + include_branches: bool = False, + include_doc: bool = False, + include_parents: bool = False, + tree_indicators: bool = True, + ) -> str: + if verbose: + return self.log_entry + else: + return self._head_only( + include_branches, include_doc, include_parents, tree_indicators + ) + + def _format_down_revision(self) -> str: + if not self.down_revision: + return "" + else: + return util.format_as_comma(self._versioned_down_revisions) + + @classmethod + def _list_py_dir( + cls, scriptdir: ScriptDirectory, path: Path + ) -> List[Path]: + paths = [] + for root, dirs, files in compat.path_walk(path, top_down=True): + if root.name.endswith("__pycache__"): + # a special case - we may include these files + # if a `sourceless` option is specified + continue + + for filename in sorted(files): + paths.append(root / filename) + + if scriptdir.sourceless: + # look for __pycache__ + py_cache_path = root / "__pycache__" + if py_cache_path.exists(): + # add all files from __pycache__ whose filename is not + # already in the names we got from the version directory. + # add as relative paths including __pycache__ token + names = { + Path(filename).name.split(".")[0] for filename in files + } + paths.extend( + py_cache_path / pyc + for pyc in py_cache_path.iterdir() + if pyc.name.split(".")[0] not in names + ) + + if not scriptdir.recursive_version_locations: + break + + # the real script order is defined by revision, + # but it may be undefined if there are many files with a same + # `down_revision`, for a better user experience (ex. debugging), + # we use a deterministic order + dirs.sort() + + return paths + + @classmethod + def _from_path( + cls, scriptdir: ScriptDirectory, path: Union[str, os.PathLike[str]] + ) -> Optional[Script]: + + path = Path(path) + dir_, filename = path.parent, path.name + + if scriptdir.sourceless: + py_match = _sourceless_rev_file.match(filename) + else: + py_match = _only_source_rev_file.match(filename) + + if not py_match: + return None + + py_filename = py_match.group(1) + + if scriptdir.sourceless: + is_c = py_match.group(2) == "c" + is_o = py_match.group(2) == "o" + else: + is_c = is_o = False + + if is_o or is_c: + py_exists = (dir_ / py_filename).exists() + pyc_exists = (dir_ / (py_filename + "c")).exists() + + # prefer .py over .pyc because we'd like to get the + # source encoding; prefer .pyc over .pyo because we'd like to + # have the docstrings which a -OO file would not have + if py_exists or is_o and pyc_exists: + return None + + module = util.load_python_file(dir_, filename) + + if not hasattr(module, "revision"): + # attempt to get the revision id from the script name, + # this for legacy only + m = _legacy_rev.match(filename) + if not m: + raise util.CommandError( + "Could not determine revision id from " + f"filename {filename}. " + "Be sure the 'revision' variable is " + "declared inside the script (please see 'Upgrading " + "from Alembic 0.1 to 0.2' in the documentation)." + ) + else: + revision = m.group(1) + else: + revision = module.revision + return Script(module, revision, dir_ / filename) diff --git a/venv/lib/python3.12/site-packages/alembic/script/revision.py b/venv/lib/python3.12/site-packages/alembic/script/revision.py new file mode 100644 index 00000000..5825da34 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/script/revision.py @@ -0,0 +1,1728 @@ +from __future__ import annotations + +import collections +import re +from typing import Any +from typing import Callable +from typing import cast +from typing import Collection +from typing import Deque +from typing import Dict +from typing import FrozenSet +from typing import Iterable +from typing import Iterator +from typing import List +from typing import Optional +from typing import overload +from typing import Protocol +from typing import Sequence +from typing import Set +from typing import Tuple +from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union + +from sqlalchemy import util as sqlautil + +from .. import util +from ..util import not_none + +if TYPE_CHECKING: + from typing import Literal + +_RevIdType = Union[str, List[str], Tuple[str, ...]] +_GetRevArg = Union[ + str, + Iterable[Optional[str]], + Iterable[str], +] +_RevisionIdentifierType = Union[str, Tuple[str, ...], None] +_RevisionOrStr = Union["Revision", str] +_RevisionOrBase = Union["Revision", "Literal['base']"] +_InterimRevisionMapType = Dict[str, "Revision"] +_RevisionMapType = Dict[Union[None, str, Tuple[()]], Optional["Revision"]] +_T = TypeVar("_T") +_TR = TypeVar("_TR", bound=Optional[_RevisionOrStr]) + +_relative_destination = re.compile(r"(?:(.+?)@)?(\w+)?((?:\+|-)\d+)") +_revision_illegal_chars = ["@", "-", "+", ":"] + + +class _CollectRevisionsProtocol(Protocol): + def __call__( + self, + upper: _RevisionIdentifierType, + lower: _RevisionIdentifierType, + inclusive: bool, + implicit_base: bool, + assert_relative_length: bool, + ) -> Tuple[Set[Revision], Tuple[Optional[_RevisionOrBase], ...]]: ... + + +class RevisionError(Exception): + pass + + +class RangeNotAncestorError(RevisionError): + def __init__( + self, lower: _RevisionIdentifierType, upper: _RevisionIdentifierType + ) -> None: + self.lower = lower + self.upper = upper + super().__init__( + "Revision %s is not an ancestor of revision %s" + % (lower or "base", upper or "base") + ) + + +class MultipleHeads(RevisionError): + def __init__(self, heads: Sequence[str], argument: Optional[str]) -> None: + self.heads = heads + self.argument = argument + super().__init__( + "Multiple heads are present for given argument '%s'; " + "%s" % (argument, ", ".join(heads)) + ) + + +class ResolutionError(RevisionError): + def __init__(self, message: str, argument: str) -> None: + super().__init__(message) + self.argument = argument + + +class CycleDetected(RevisionError): + kind = "Cycle" + + def __init__(self, revisions: Sequence[str]) -> None: + self.revisions = revisions + super().__init__( + "%s is detected in revisions (%s)" + % (self.kind, ", ".join(revisions)) + ) + + +class DependencyCycleDetected(CycleDetected): + kind = "Dependency cycle" + + def __init__(self, revisions: Sequence[str]) -> None: + super().__init__(revisions) + + +class LoopDetected(CycleDetected): + kind = "Self-loop" + + def __init__(self, revision: str) -> None: + super().__init__([revision]) + + +class DependencyLoopDetected(DependencyCycleDetected, LoopDetected): + kind = "Dependency self-loop" + + def __init__(self, revision: Sequence[str]) -> None: + super().__init__(revision) + + +class RevisionMap: + """Maintains a map of :class:`.Revision` objects. + + :class:`.RevisionMap` is used by :class:`.ScriptDirectory` to maintain + and traverse the collection of :class:`.Script` objects, which are + themselves instances of :class:`.Revision`. + + """ + + def __init__(self, generator: Callable[[], Iterable[Revision]]) -> None: + """Construct a new :class:`.RevisionMap`. + + :param generator: a zero-arg callable that will generate an iterable + of :class:`.Revision` instances to be used. These are typically + :class:`.Script` subclasses within regular Alembic use. + + """ + self._generator = generator + + @util.memoized_property + def heads(self) -> Tuple[str, ...]: + """All "head" revisions as strings. + + This is normally a tuple of length one, + unless unmerged branches are present. + + :return: a tuple of string revision numbers. + + """ + self._revision_map + return self.heads + + @util.memoized_property + def bases(self) -> Tuple[str, ...]: + """All "base" revisions as strings. + + These are revisions that have a ``down_revision`` of None, + or empty tuple. + + :return: a tuple of string revision numbers. + + """ + self._revision_map + return self.bases + + @util.memoized_property + def _real_heads(self) -> Tuple[str, ...]: + """All "real" head revisions as strings. + + :return: a tuple of string revision numbers. + + """ + self._revision_map + return self._real_heads + + @util.memoized_property + def _real_bases(self) -> Tuple[str, ...]: + """All "real" base revisions as strings. + + :return: a tuple of string revision numbers. + + """ + self._revision_map + return self._real_bases + + @util.memoized_property + def _revision_map(self) -> _RevisionMapType: + """memoized attribute, initializes the revision map from the + initial collection. + + """ + # Ordering required for some tests to pass (but not required in + # general) + map_: _InterimRevisionMapType = sqlautil.OrderedDict() + + heads: Set[Revision] = sqlautil.OrderedSet() + _real_heads: Set[Revision] = sqlautil.OrderedSet() + bases: Tuple[Revision, ...] = () + _real_bases: Tuple[Revision, ...] = () + + has_branch_labels = set() + all_revisions = set() + + for revision in self._generator(): + all_revisions.add(revision) + + if revision.revision in map_: + util.warn( + "Revision %s is present more than once" % revision.revision + ) + map_[revision.revision] = revision + if revision.branch_labels: + has_branch_labels.add(revision) + + heads.add(revision) + _real_heads.add(revision) + if revision.is_base: + bases += (revision,) + if revision._is_real_base: + _real_bases += (revision,) + + # add the branch_labels to the map_. We'll need these + # to resolve the dependencies. + rev_map = map_.copy() + self._map_branch_labels( + has_branch_labels, cast(_RevisionMapType, map_) + ) + + # resolve dependency names from branch labels and symbolic + # names + self._add_depends_on(all_revisions, cast(_RevisionMapType, map_)) + + for rev in map_.values(): + for downrev in rev._all_down_revisions: + if downrev not in map_: + util.warn( + "Revision %s referenced from %s is not present" + % (downrev, rev) + ) + down_revision = map_[downrev] + down_revision.add_nextrev(rev) + if downrev in rev._versioned_down_revisions: + heads.discard(down_revision) + _real_heads.discard(down_revision) + + # once the map has downrevisions populated, the dependencies + # can be further refined to include only those which are not + # already ancestors + self._normalize_depends_on(all_revisions, cast(_RevisionMapType, map_)) + self._detect_cycles(rev_map, heads, bases, _real_heads, _real_bases) + + revision_map: _RevisionMapType = dict(map_.items()) + revision_map[None] = revision_map[()] = None + self.heads = tuple(rev.revision for rev in heads) + self._real_heads = tuple(rev.revision for rev in _real_heads) + self.bases = tuple(rev.revision for rev in bases) + self._real_bases = tuple(rev.revision for rev in _real_bases) + + self._add_branches(has_branch_labels, revision_map) + return revision_map + + def _detect_cycles( + self, + rev_map: _InterimRevisionMapType, + heads: Set[Revision], + bases: Tuple[Revision, ...], + _real_heads: Set[Revision], + _real_bases: Tuple[Revision, ...], + ) -> None: + if not rev_map: + return + if not heads or not bases: + raise CycleDetected(list(rev_map)) + total_space = { + rev.revision + for rev in self._iterate_related_revisions( + lambda r: r._versioned_down_revisions, + heads, + map_=cast(_RevisionMapType, rev_map), + ) + }.intersection( + rev.revision + for rev in self._iterate_related_revisions( + lambda r: r.nextrev, + bases, + map_=cast(_RevisionMapType, rev_map), + ) + ) + deleted_revs = set(rev_map.keys()) - total_space + if deleted_revs: + raise CycleDetected(sorted(deleted_revs)) + + if not _real_heads or not _real_bases: + raise DependencyCycleDetected(list(rev_map)) + total_space = { + rev.revision + for rev in self._iterate_related_revisions( + lambda r: r._all_down_revisions, + _real_heads, + map_=cast(_RevisionMapType, rev_map), + ) + }.intersection( + rev.revision + for rev in self._iterate_related_revisions( + lambda r: r._all_nextrev, + _real_bases, + map_=cast(_RevisionMapType, rev_map), + ) + ) + deleted_revs = set(rev_map.keys()) - total_space + if deleted_revs: + raise DependencyCycleDetected(sorted(deleted_revs)) + + def _map_branch_labels( + self, revisions: Collection[Revision], map_: _RevisionMapType + ) -> None: + for revision in revisions: + if revision.branch_labels: + assert revision._orig_branch_labels is not None + for branch_label in revision._orig_branch_labels: + if branch_label in map_: + map_rev = map_[branch_label] + assert map_rev is not None + raise RevisionError( + "Branch name '%s' in revision %s already " + "used by revision %s" + % ( + branch_label, + revision.revision, + map_rev.revision, + ) + ) + map_[branch_label] = revision + + def _add_branches( + self, revisions: Collection[Revision], map_: _RevisionMapType + ) -> None: + for revision in revisions: + if revision.branch_labels: + revision.branch_labels.update(revision.branch_labels) + for node in self._get_descendant_nodes( + [revision], map_, include_dependencies=False + ): + node.branch_labels.update(revision.branch_labels) + + parent = node + while ( + parent + and not parent._is_real_branch_point + and not parent.is_merge_point + ): + parent.branch_labels.update(revision.branch_labels) + if parent.down_revision: + parent = map_[parent.down_revision] + else: + break + + def _add_depends_on( + self, revisions: Collection[Revision], map_: _RevisionMapType + ) -> None: + """Resolve the 'dependencies' for each revision in a collection + in terms of actual revision ids, as opposed to branch labels or other + symbolic names. + + The collection is then assigned to the _resolved_dependencies + attribute on each revision object. + + """ + + for revision in revisions: + if revision.dependencies: + deps = [ + map_[dep] for dep in util.to_tuple(revision.dependencies) + ] + revision._resolved_dependencies = tuple( + [d.revision for d in deps if d is not None] + ) + else: + revision._resolved_dependencies = () + + def _normalize_depends_on( + self, revisions: Collection[Revision], map_: _RevisionMapType + ) -> None: + """Create a collection of "dependencies" that omits dependencies + that are already ancestor nodes for each revision in a given + collection. + + This builds upon the _resolved_dependencies collection created in the + _add_depends_on() method, looking in the fully populated revision map + for ancestors, and omitting them as the _resolved_dependencies + collection as it is copied to a new collection. The new collection is + then assigned to the _normalized_resolved_dependencies attribute on + each revision object. + + The collection is then used to determine the immediate "down revision" + identifiers for this revision. + + """ + + for revision in revisions: + if revision._resolved_dependencies: + normalized_resolved = set(revision._resolved_dependencies) + for rev in self._get_ancestor_nodes( + [revision], + include_dependencies=False, + map_=map_, + ): + if rev is revision: + continue + elif rev._resolved_dependencies: + normalized_resolved.difference_update( + rev._resolved_dependencies + ) + + revision._normalized_resolved_dependencies = tuple( + normalized_resolved + ) + else: + revision._normalized_resolved_dependencies = () + + def add_revision(self, revision: Revision, _replace: bool = False) -> None: + """add a single revision to an existing map. + + This method is for single-revision use cases, it's not + appropriate for fully populating an entire revision map. + + """ + map_ = self._revision_map + if not _replace and revision.revision in map_: + util.warn( + "Revision %s is present more than once" % revision.revision + ) + elif _replace and revision.revision not in map_: + raise Exception("revision %s not in map" % revision.revision) + + map_[revision.revision] = revision + + revisions = [revision] + self._add_branches(revisions, map_) + self._map_branch_labels(revisions, map_) + self._add_depends_on(revisions, map_) + + if revision.is_base: + self.bases += (revision.revision,) + if revision._is_real_base: + self._real_bases += (revision.revision,) + + for downrev in revision._all_down_revisions: + if downrev not in map_: + util.warn( + "Revision %s referenced from %s is not present" + % (downrev, revision) + ) + not_none(map_[downrev]).add_nextrev(revision) + + self._normalize_depends_on(revisions, map_) + + if revision._is_real_head: + self._real_heads = tuple( + head + for head in self._real_heads + if head + not in set(revision._all_down_revisions).union( + [revision.revision] + ) + ) + (revision.revision,) + if revision.is_head: + self.heads = tuple( + head + for head in self.heads + if head + not in set(revision._versioned_down_revisions).union( + [revision.revision] + ) + ) + (revision.revision,) + + def get_current_head( + self, branch_label: Optional[str] = None + ) -> Optional[str]: + """Return the current head revision. + + If the script directory has multiple heads + due to branching, an error is raised; + :meth:`.ScriptDirectory.get_heads` should be + preferred. + + :param branch_label: optional branch name which will limit the + heads considered to those which include that branch_label. + + :return: a string revision number. + + .. seealso:: + + :meth:`.ScriptDirectory.get_heads` + + """ + current_heads: Sequence[str] = self.heads + if branch_label: + current_heads = self.filter_for_lineage( + current_heads, branch_label + ) + if len(current_heads) > 1: + raise MultipleHeads( + current_heads, + "%s@head" % branch_label if branch_label else "head", + ) + + if current_heads: + return current_heads[0] + else: + return None + + def _get_base_revisions(self, identifier: str) -> Tuple[str, ...]: + return self.filter_for_lineage(self.bases, identifier) + + def get_revisions( + self, id_: Optional[_GetRevArg] + ) -> Tuple[Optional[_RevisionOrBase], ...]: + """Return the :class:`.Revision` instances with the given rev id + or identifiers. + + May be given a single identifier, a sequence of identifiers, or the + special symbols "head" or "base". The result is a tuple of one + or more identifiers, or an empty tuple in the case of "base". + + In the cases where 'head', 'heads' is requested and the + revision map is empty, returns an empty tuple. + + Supports partial identifiers, where the given identifier + is matched against all identifiers that start with the given + characters; if there is exactly one match, that determines the + full revision. + + """ + + if isinstance(id_, (list, tuple, set, frozenset)): + return sum([self.get_revisions(id_elem) for id_elem in id_], ()) + else: + resolved_id, branch_label = self._resolve_revision_number(id_) + if len(resolved_id) == 1: + try: + rint = int(resolved_id[0]) + if rint < 0: + # branch@-n -> walk down from heads + select_heads = self.get_revisions("heads") + if branch_label is not None: + select_heads = tuple( + head + for head in select_heads + if branch_label + in is_revision(head).branch_labels + ) + return tuple( + self._walk(head, steps=rint) + for head in select_heads + ) + except ValueError: + # couldn't resolve as integer + pass + return tuple( + self._revision_for_ident(rev_id, branch_label) + for rev_id in resolved_id + ) + + def get_revision(self, id_: Optional[str]) -> Optional[Revision]: + """Return the :class:`.Revision` instance with the given rev id. + + If a symbolic name such as "head" or "base" is given, resolves + the identifier into the current head or base revision. If the symbolic + name refers to multiples, :class:`.MultipleHeads` is raised. + + Supports partial identifiers, where the given identifier + is matched against all identifiers that start with the given + characters; if there is exactly one match, that determines the + full revision. + + """ + + resolved_id, branch_label = self._resolve_revision_number(id_) + if len(resolved_id) > 1: + raise MultipleHeads(resolved_id, id_) + + resolved: Union[str, Tuple[()]] = resolved_id[0] if resolved_id else () + return self._revision_for_ident(resolved, branch_label) + + def _resolve_branch(self, branch_label: str) -> Optional[Revision]: + try: + branch_rev = self._revision_map[branch_label] + except KeyError: + try: + nonbranch_rev = self._revision_for_ident(branch_label) + except ResolutionError as re: + raise ResolutionError( + "No such branch: '%s'" % branch_label, branch_label + ) from re + + else: + return nonbranch_rev + else: + return branch_rev + + def _revision_for_ident( + self, + resolved_id: Union[str, Tuple[()], None], + check_branch: Optional[str] = None, + ) -> Optional[Revision]: + branch_rev: Optional[Revision] + if check_branch: + branch_rev = self._resolve_branch(check_branch) + else: + branch_rev = None + + revision: Union[Optional[Revision], Literal[False]] + try: + revision = self._revision_map[resolved_id] + except KeyError: + # break out to avoid misleading py3k stack traces + revision = False + revs: Sequence[str] + if revision is False: + assert resolved_id + # do a partial lookup + revs = [ + x + for x in self._revision_map + if x and len(x) > 3 and x.startswith(resolved_id) + ] + + if branch_rev: + revs = self.filter_for_lineage(revs, check_branch) + if not revs: + raise ResolutionError( + "No such revision or branch '%s'%s" + % ( + resolved_id, + ( + "; please ensure at least four characters are " + "present for partial revision identifier matches" + if len(resolved_id) < 4 + else "" + ), + ), + resolved_id, + ) + elif len(revs) > 1: + raise ResolutionError( + "Multiple revisions start " + "with '%s': %s..." + % (resolved_id, ", ".join("'%s'" % r for r in revs[0:3])), + resolved_id, + ) + else: + revision = self._revision_map[revs[0]] + + if check_branch and revision is not None: + assert branch_rev is not None + assert resolved_id + if not self._shares_lineage( + revision.revision, branch_rev.revision + ): + raise ResolutionError( + "Revision %s is not a member of branch '%s'" + % (revision.revision, check_branch), + resolved_id, + ) + return revision + + def _filter_into_branch_heads( + self, targets: Iterable[Optional[_RevisionOrBase]] + ) -> Set[Optional[_RevisionOrBase]]: + targets = set(targets) + + for rev in list(targets): + assert rev + if targets.intersection( + self._get_descendant_nodes([rev], include_dependencies=False) + ).difference([rev]): + targets.discard(rev) + return targets + + def filter_for_lineage( + self, + targets: Iterable[_TR], + check_against: Optional[str], + include_dependencies: bool = False, + ) -> Tuple[_TR, ...]: + id_, branch_label = self._resolve_revision_number(check_against) + + shares = [] + if branch_label: + shares.append(branch_label) + if id_: + shares.extend(id_) + + return tuple( + tg + for tg in targets + if self._shares_lineage( + tg, shares, include_dependencies=include_dependencies + ) + ) + + def _shares_lineage( + self, + target: Optional[_RevisionOrStr], + test_against_revs: Sequence[_RevisionOrStr], + include_dependencies: bool = False, + ) -> bool: + if not test_against_revs: + return True + if not isinstance(target, Revision): + resolved_target = not_none(self._revision_for_ident(target)) + else: + resolved_target = target + + resolved_test_against_revs = [ + ( + self._revision_for_ident(test_against_rev) + if not isinstance(test_against_rev, Revision) + else test_against_rev + ) + for test_against_rev in util.to_tuple( + test_against_revs, default=() + ) + ] + + return bool( + set( + self._get_descendant_nodes( + [resolved_target], + include_dependencies=include_dependencies, + ) + ) + .union( + self._get_ancestor_nodes( + [resolved_target], + include_dependencies=include_dependencies, + ) + ) + .intersection(resolved_test_against_revs) + ) + + def _resolve_revision_number( + self, id_: Optional[_GetRevArg] + ) -> Tuple[Tuple[str, ...], Optional[str]]: + branch_label: Optional[str] + if isinstance(id_, str) and "@" in id_: + branch_label, id_ = id_.split("@", 1) + + elif id_ is not None and ( + (isinstance(id_, tuple) and id_ and not isinstance(id_[0], str)) + or not isinstance(id_, (str, tuple)) + ): + raise RevisionError( + "revision identifier %r is not a string; ensure database " + "driver settings are correct" % (id_,) + ) + + else: + branch_label = None + + # ensure map is loaded + self._revision_map + if id_ == "heads": + if branch_label: + return ( + self.filter_for_lineage(self.heads, branch_label), + branch_label, + ) + else: + return self._real_heads, branch_label + elif id_ == "head": + current_head = self.get_current_head(branch_label) + if current_head: + return (current_head,), branch_label + else: + return (), branch_label + elif id_ == "base" or id_ is None: + return (), branch_label + else: + return util.to_tuple(id_, default=None), branch_label + + def iterate_revisions( + self, + upper: _RevisionIdentifierType, + lower: _RevisionIdentifierType, + implicit_base: bool = False, + inclusive: bool = False, + assert_relative_length: bool = True, + select_for_downgrade: bool = False, + ) -> Iterator[Revision]: + """Iterate through script revisions, starting at the given + upper revision identifier and ending at the lower. + + The traversal uses strictly the `down_revision` + marker inside each migration script, so + it is a requirement that upper >= lower, + else you'll get nothing back. + + The iterator yields :class:`.Revision` objects. + + """ + fn: _CollectRevisionsProtocol + if select_for_downgrade: + fn = self._collect_downgrade_revisions + else: + fn = self._collect_upgrade_revisions + + revisions, heads = fn( + upper, + lower, + inclusive=inclusive, + implicit_base=implicit_base, + assert_relative_length=assert_relative_length, + ) + + for node in self._topological_sort(revisions, heads): + yield not_none(self.get_revision(node)) + + def _get_descendant_nodes( + self, + targets: Collection[Optional[_RevisionOrBase]], + map_: Optional[_RevisionMapType] = None, + check: bool = False, + omit_immediate_dependencies: bool = False, + include_dependencies: bool = True, + ) -> Iterator[Any]: + if omit_immediate_dependencies: + + def fn(rev: Revision) -> Iterable[str]: + if rev not in targets: + return rev._all_nextrev + else: + return rev.nextrev + + elif include_dependencies: + + def fn(rev: Revision) -> Iterable[str]: + return rev._all_nextrev + + else: + + def fn(rev: Revision) -> Iterable[str]: + return rev.nextrev + + return self._iterate_related_revisions( + fn, targets, map_=map_, check=check + ) + + def _get_ancestor_nodes( + self, + targets: Collection[Optional[_RevisionOrBase]], + map_: Optional[_RevisionMapType] = None, + check: bool = False, + include_dependencies: bool = True, + ) -> Iterator[Revision]: + if include_dependencies: + + def fn(rev: Revision) -> Iterable[str]: + return rev._normalized_down_revisions + + else: + + def fn(rev: Revision) -> Iterable[str]: + return rev._versioned_down_revisions + + return self._iterate_related_revisions( + fn, targets, map_=map_, check=check + ) + + def _iterate_related_revisions( + self, + fn: Callable[[Revision], Iterable[str]], + targets: Collection[Optional[_RevisionOrBase]], + map_: Optional[_RevisionMapType], + check: bool = False, + ) -> Iterator[Revision]: + if map_ is None: + map_ = self._revision_map + + seen = set() + todo: Deque[Revision] = collections.deque() + for target_for in targets: + target = is_revision(target_for) + todo.append(target) + if check: + per_target = set() + + while todo: + rev = todo.pop() + if check: + per_target.add(rev) + + if rev in seen: + continue + seen.add(rev) + # Check for map errors before collecting. + for rev_id in fn(rev): + next_rev = map_[rev_id] + assert next_rev is not None + if next_rev.revision != rev_id: + raise RevisionError( + "Dependency resolution failed; broken map" + ) + todo.append(next_rev) + yield rev + if check: + overlaps = per_target.intersection(targets).difference( + [target] + ) + if overlaps: + raise RevisionError( + "Requested revision %s overlaps with " + "other requested revisions %s" + % ( + target.revision, + ", ".join(r.revision for r in overlaps), + ) + ) + + def _topological_sort( + self, + revisions: Collection[Revision], + heads: Any, + ) -> List[str]: + """Yield revision ids of a collection of Revision objects in + topological sorted order (i.e. revisions always come after their + down_revisions and dependencies). Uses the order of keys in + _revision_map to sort. + + """ + + id_to_rev = self._revision_map + + def get_ancestors(rev_id: str) -> Set[str]: + return { + r.revision + for r in self._get_ancestor_nodes([id_to_rev[rev_id]]) + } + + todo = {d.revision for d in revisions} + + # Use revision map (ordered dict) key order to pre-sort. + inserted_order = list(self._revision_map) + + current_heads = list( + sorted( + {d.revision for d in heads if d.revision in todo}, + key=inserted_order.index, + ) + ) + ancestors_by_idx = [get_ancestors(rev_id) for rev_id in current_heads] + + output = [] + + current_candidate_idx = 0 + while current_heads: + candidate = current_heads[current_candidate_idx] + + for check_head_index, ancestors in enumerate(ancestors_by_idx): + # scan all the heads. see if we can continue walking + # down the current branch indicated by current_candidate_idx. + if ( + check_head_index != current_candidate_idx + and candidate in ancestors + ): + current_candidate_idx = check_head_index + # nope, another head is dependent on us, they have + # to be traversed first + break + else: + # yup, we can emit + if candidate in todo: + output.append(candidate) + todo.remove(candidate) + + # now update the heads with our ancestors. + + candidate_rev = id_to_rev[candidate] + assert candidate_rev is not None + + heads_to_add = [ + r + for r in candidate_rev._normalized_down_revisions + if r in todo and r not in current_heads + ] + + if not heads_to_add: + # no ancestors, so remove this head from the list + del current_heads[current_candidate_idx] + del ancestors_by_idx[current_candidate_idx] + current_candidate_idx = max(current_candidate_idx - 1, 0) + else: + if ( + not candidate_rev._normalized_resolved_dependencies + and len(candidate_rev._versioned_down_revisions) == 1 + ): + current_heads[current_candidate_idx] = heads_to_add[0] + + # for plain movement down a revision line without + # any mergepoints, branchpoints, or deps, we + # can update the ancestors collection directly + # by popping out the candidate we just emitted + ancestors_by_idx[current_candidate_idx].discard( + candidate + ) + + else: + # otherwise recalculate it again, things get + # complicated otherwise. This can possibly be + # improved to not run the whole ancestor thing + # each time but it was getting complicated + current_heads[current_candidate_idx] = heads_to_add[0] + current_heads.extend(heads_to_add[1:]) + ancestors_by_idx[current_candidate_idx] = ( + get_ancestors(heads_to_add[0]) + ) + ancestors_by_idx.extend( + get_ancestors(head) for head in heads_to_add[1:] + ) + + assert not todo + return output + + def _walk( + self, + start: Optional[Union[str, Revision]], + steps: int, + branch_label: Optional[str] = None, + no_overwalk: bool = True, + ) -> Optional[_RevisionOrBase]: + """ + Walk the requested number of :steps up (steps > 0) or down (steps < 0) + the revision tree. + + :branch_label is used to select branches only when walking up. + + If the walk goes past the boundaries of the tree and :no_overwalk is + True, None is returned, otherwise the walk terminates early. + + A RevisionError is raised if there is no unambiguous revision to + walk to. + """ + initial: Optional[_RevisionOrBase] + if isinstance(start, str): + initial = self.get_revision(start) + else: + initial = start + + children: Sequence[Optional[_RevisionOrBase]] + for _ in range(abs(steps)): + if steps > 0: + assert initial != "base" # type: ignore[comparison-overlap] + # Walk up + walk_up = [ + is_revision(rev) + for rev in self.get_revisions( + self.bases if initial is None else initial.nextrev + ) + ] + if branch_label: + children = self.filter_for_lineage(walk_up, branch_label) + else: + children = walk_up + else: + # Walk down + if initial == "base": # type: ignore[comparison-overlap] + children = () + else: + children = self.get_revisions( + self.heads + if initial is None + else initial.down_revision + ) + if not children: + children = ("base",) + if not children: + # This will return an invalid result if no_overwalk, otherwise + # further steps will stay where we are. + ret = None if no_overwalk else initial + return ret + elif len(children) > 1: + raise RevisionError("Ambiguous walk") + initial = children[0] + + return initial + + def _parse_downgrade_target( + self, + current_revisions: _RevisionIdentifierType, + target: _RevisionIdentifierType, + assert_relative_length: bool, + ) -> Tuple[Optional[str], Optional[_RevisionOrBase]]: + """ + Parse downgrade command syntax :target to retrieve the target revision + and branch label (if any) given the :current_revisions stamp of the + database. + + Returns a tuple (branch_label, target_revision) where branch_label + is a string from the command specifying the branch to consider (or + None if no branch given), and target_revision is a Revision object + which the command refers to. target_revisions is None if the command + refers to 'base'. The target may be specified in absolute form, or + relative to :current_revisions. + """ + if target is None: + return None, None + assert isinstance( + target, str + ), "Expected downgrade target in string form" + match = _relative_destination.match(target) + if match: + branch_label, symbol, relative = match.groups() + rel_int = int(relative) + if rel_int >= 0: + if symbol is None: + # Downgrading to current + n is not valid. + raise RevisionError( + "Relative revision %s didn't " + "produce %d migrations" % (relative, abs(rel_int)) + ) + # Find target revision relative to given symbol. + rev = self._walk( + symbol, + rel_int, + branch_label, + no_overwalk=assert_relative_length, + ) + if rev is None: + raise RevisionError("Walked too far") + return branch_label, rev + else: + relative_revision = symbol is None + if relative_revision: + # Find target revision relative to current state. + if branch_label: + cr_tuple = util.to_tuple(current_revisions) + symbol_list: Sequence[str] + symbol_list = self.filter_for_lineage( + cr_tuple, branch_label + ) + if not symbol_list: + # check the case where there are multiple branches + # but there is currently a single heads, since all + # other branch heads are dependent of the current + # single heads. + all_current = cast( + Set[Revision], self._get_all_current(cr_tuple) + ) + sl_all_current = self.filter_for_lineage( + all_current, branch_label + ) + symbol_list = [ + r.revision if r else r # type: ignore[misc] + for r in sl_all_current + ] + + assert len(symbol_list) == 1 + symbol = symbol_list[0] + else: + current_revisions = util.to_tuple(current_revisions) + if not current_revisions: + raise RevisionError( + "Relative revision %s didn't " + "produce %d migrations" + % (relative, abs(rel_int)) + ) + # Have to check uniques here for duplicate rows test. + if len(set(current_revisions)) > 1: + util.warn( + "downgrade -1 from multiple heads is " + "ambiguous; " + "this usage will be disallowed in a future " + "release." + ) + symbol = current_revisions[0] + # Restrict iteration to just the selected branch when + # ambiguous branches are involved. + branch_label = symbol + # Walk down the tree to find downgrade target. + rev = self._walk( + start=( + self.get_revision(symbol) + if branch_label is None + else self.get_revision( + "%s@%s" % (branch_label, symbol) + ) + ), + steps=rel_int, + no_overwalk=assert_relative_length, + ) + if rev is None: + if relative_revision: + raise RevisionError( + "Relative revision %s didn't " + "produce %d migrations" % (relative, abs(rel_int)) + ) + else: + raise RevisionError("Walked too far") + return branch_label, rev + + # No relative destination given, revision specified is absolute. + branch_label, _, symbol = target.rpartition("@") + if not branch_label: + branch_label = None + return branch_label, self.get_revision(symbol) + + def _parse_upgrade_target( + self, + current_revisions: _RevisionIdentifierType, + target: _RevisionIdentifierType, + assert_relative_length: bool, + ) -> Tuple[Optional[_RevisionOrBase], ...]: + """ + Parse upgrade command syntax :target to retrieve the target revision + and given the :current_revisions stamp of the database. + + Returns a tuple of Revision objects which should be iterated/upgraded + to. The target may be specified in absolute form, or relative to + :current_revisions. + """ + if isinstance(target, str): + match = _relative_destination.match(target) + else: + match = None + + if not match: + # No relative destination, target is absolute. + return self.get_revisions(target) + + current_revisions_tup: Union[str, Tuple[Optional[str], ...], None] + current_revisions_tup = util.to_tuple(current_revisions) + + branch_label, symbol, relative_str = match.groups() + relative = int(relative_str) + if relative > 0: + if symbol is None: + if not current_revisions_tup: + current_revisions_tup = (None,) + # Try to filter to a single target (avoid ambiguous branches). + start_revs = current_revisions_tup + if branch_label: + start_revs = self.filter_for_lineage( + self.get_revisions(current_revisions_tup), # type: ignore[arg-type] # noqa: E501 + branch_label, + ) + if not start_revs: + # The requested branch is not a head, so we need to + # backtrack to find a branchpoint. + active_on_branch = self.filter_for_lineage( + self._get_ancestor_nodes( + self.get_revisions(current_revisions_tup) + ), + branch_label, + ) + # Find the tips of this set of revisions (revisions + # without children within the set). + start_revs = tuple( + {rev.revision for rev in active_on_branch} + - { + down + for rev in active_on_branch + for down in rev._normalized_down_revisions + } + ) + if not start_revs: + # We must need to go right back to base to find + # a starting point for this branch. + start_revs = (None,) + if len(start_revs) > 1: + raise RevisionError( + "Ambiguous upgrade from multiple current revisions" + ) + # Walk up from unique target revision. + rev = self._walk( + start=start_revs[0], + steps=relative, + branch_label=branch_label, + no_overwalk=assert_relative_length, + ) + if rev is None: + raise RevisionError( + "Relative revision %s didn't " + "produce %d migrations" % (relative_str, abs(relative)) + ) + return (rev,) + else: + # Walk is relative to a given revision, not the current state. + return ( + self._walk( + start=self.get_revision(symbol), + steps=relative, + branch_label=branch_label, + no_overwalk=assert_relative_length, + ), + ) + else: + if symbol is None: + # Upgrading to current - n is not valid. + raise RevisionError( + "Relative revision %s didn't " + "produce %d migrations" % (relative, abs(relative)) + ) + return ( + self._walk( + start=( + self.get_revision(symbol) + if branch_label is None + else self.get_revision( + "%s@%s" % (branch_label, symbol) + ) + ), + steps=relative, + no_overwalk=assert_relative_length, + ), + ) + + def _collect_downgrade_revisions( + self, + upper: _RevisionIdentifierType, + lower: _RevisionIdentifierType, + inclusive: bool, + implicit_base: bool, + assert_relative_length: bool, + ) -> Tuple[Set[Revision], Tuple[Optional[_RevisionOrBase], ...]]: + """ + Compute the set of current revisions specified by :upper, and the + downgrade target specified by :target. Return all dependents of target + which are currently active. + + :inclusive=True includes the target revision in the set + """ + + branch_label, target_revision = self._parse_downgrade_target( + current_revisions=upper, + target=lower, + assert_relative_length=assert_relative_length, + ) + if target_revision == "base": + target_revision = None + assert target_revision is None or isinstance(target_revision, Revision) + + roots: List[Revision] + # Find candidates to drop. + if target_revision is None: + # Downgrading back to base: find all tree roots. + roots = [ + rev + for rev in self._revision_map.values() + if rev is not None and rev.down_revision is None + ] + elif inclusive: + # inclusive implies target revision should also be dropped + roots = [target_revision] + else: + # Downgrading to fixed target: find all direct children. + roots = [ + is_revision(rev) + for rev in self.get_revisions(target_revision.nextrev) + ] + + if branch_label and len(roots) > 1: + # Need to filter roots. + ancestors = { + rev.revision + for rev in self._get_ancestor_nodes( + [self._resolve_branch(branch_label)], + include_dependencies=False, + ) + } + # Intersection gives the root revisions we are trying to + # rollback with the downgrade. + roots = [ + is_revision(rev) + for rev in self.get_revisions( + {rev.revision for rev in roots}.intersection(ancestors) + ) + ] + + # Ensure we didn't throw everything away when filtering branches. + if len(roots) == 0: + raise RevisionError( + "Not a valid downgrade target from current heads" + ) + + heads = self.get_revisions(upper) + + # Aim is to drop :branch_revision; to do so we also need to drop its + # descendents and anything dependent on it. + downgrade_revisions = set( + self._get_descendant_nodes( + roots, + include_dependencies=True, + omit_immediate_dependencies=False, + ) + ) + active_revisions = set( + self._get_ancestor_nodes(heads, include_dependencies=True) + ) + + # Emit revisions to drop in reverse topological sorted order. + downgrade_revisions.intersection_update(active_revisions) + + if implicit_base: + # Wind other branches back to base. + downgrade_revisions.update( + active_revisions.difference(self._get_ancestor_nodes(roots)) + ) + + if ( + target_revision is not None + and not downgrade_revisions + and target_revision not in heads + ): + # Empty intersection: target revs are not present. + + raise RangeNotAncestorError("Nothing to drop", upper) + + return downgrade_revisions, heads + + def _collect_upgrade_revisions( + self, + upper: _RevisionIdentifierType, + lower: _RevisionIdentifierType, + inclusive: bool, + implicit_base: bool, + assert_relative_length: bool, + ) -> Tuple[Set[Revision], Tuple[Revision, ...]]: + """ + Compute the set of required revisions specified by :upper, and the + current set of active revisions specified by :lower. Find the + difference between the two to compute the required upgrades. + + :inclusive=True includes the current/lower revisions in the set + + :implicit_base=False only returns revisions which are downstream + of the current/lower revisions. Dependencies from branches with + different bases will not be included. + """ + targets: Collection[Revision] = [ + is_revision(rev) + for rev in self._parse_upgrade_target( + current_revisions=lower, + target=upper, + assert_relative_length=assert_relative_length, + ) + ] + + # assert type(targets) is tuple, "targets should be a tuple" + + # Handled named bases (e.g. branch@... -> heads should only produce + # targets on the given branch) + if isinstance(lower, str) and "@" in lower: + branch, _, _ = lower.partition("@") + branch_rev = self.get_revision(branch) + if branch_rev is not None and branch_rev.revision == branch: + # A revision was used as a label; get its branch instead + assert len(branch_rev.branch_labels) == 1 + branch = next(iter(branch_rev.branch_labels)) + targets = { + need for need in targets if branch in need.branch_labels + } + + required_node_set = set( + self._get_ancestor_nodes( + targets, check=True, include_dependencies=True + ) + ).union(targets) + + current_revisions = self.get_revisions(lower) + if not implicit_base and any( + rev not in required_node_set + for rev in current_revisions + if rev is not None + ): + raise RangeNotAncestorError(lower, upper) + assert ( + type(current_revisions) is tuple + ), "current_revisions should be a tuple" + + # Special case where lower = a relative value (get_revisions can't + # find it) + if current_revisions and current_revisions[0] is None: + _, rev = self._parse_downgrade_target( + current_revisions=upper, + target=lower, + assert_relative_length=assert_relative_length, + ) + assert rev + if rev == "base": + current_revisions = tuple() + lower = None + else: + current_revisions = (rev,) + lower = rev.revision + + current_node_set = set( + self._get_ancestor_nodes( + current_revisions, check=True, include_dependencies=True + ) + ).union(current_revisions) + + needs = required_node_set.difference(current_node_set) + + # Include the lower revision (=current_revisions?) in the iteration + if inclusive: + needs.update(is_revision(rev) for rev in self.get_revisions(lower)) + # By default, base is implicit as we want all dependencies returned. + # Base is also implicit if lower = base + # implicit_base=False -> only return direct downstreams of + # current_revisions + if current_revisions and not implicit_base: + lower_descendents = self._get_descendant_nodes( + [is_revision(rev) for rev in current_revisions], + check=True, + include_dependencies=False, + ) + needs.intersection_update(lower_descendents) + + return needs, tuple(targets) + + def _get_all_current( + self, id_: Tuple[str, ...] + ) -> Set[Optional[_RevisionOrBase]]: + top_revs: Set[Optional[_RevisionOrBase]] + top_revs = set(self.get_revisions(id_)) + top_revs.update( + self._get_ancestor_nodes(list(top_revs), include_dependencies=True) + ) + return self._filter_into_branch_heads(top_revs) + + +class Revision: + """Base class for revisioned objects. + + The :class:`.Revision` class is the base of the more public-facing + :class:`.Script` object, which represents a migration script. + The mechanics of revision management and traversal are encapsulated + within :class:`.Revision`, while :class:`.Script` applies this logic + to Python files in a version directory. + + """ + + nextrev: FrozenSet[str] = frozenset() + """following revisions, based on down_revision only.""" + + _all_nextrev: FrozenSet[str] = frozenset() + + revision: str = None # type: ignore[assignment] + """The string revision number.""" + + down_revision: Optional[_RevIdType] = None + """The ``down_revision`` identifier(s) within the migration script. + + Note that the total set of "down" revisions is + down_revision + dependencies. + + """ + + dependencies: Optional[_RevIdType] = None + """Additional revisions which this revision is dependent on. + + From a migration standpoint, these dependencies are added to the + down_revision to form the full iteration. However, the separation + of down_revision from "dependencies" is to assist in navigating + a history that contains many branches, typically a multi-root scenario. + + """ + + branch_labels: Set[str] = None # type: ignore[assignment] + """Optional string/tuple of symbolic names to apply to this + revision's branch""" + + _resolved_dependencies: Tuple[str, ...] + _normalized_resolved_dependencies: Tuple[str, ...] + + @classmethod + def verify_rev_id(cls, revision: str) -> None: + illegal_chars = set(revision).intersection(_revision_illegal_chars) + if illegal_chars: + raise RevisionError( + "Character(s) '%s' not allowed in revision identifier '%s'" + % (", ".join(sorted(illegal_chars)), revision) + ) + + def __init__( + self, + revision: str, + down_revision: Optional[Union[str, Tuple[str, ...]]], + dependencies: Optional[Union[str, Tuple[str, ...]]] = None, + branch_labels: Optional[Union[str, Tuple[str, ...]]] = None, + ) -> None: + if down_revision and revision in util.to_tuple(down_revision): + raise LoopDetected(revision) + elif dependencies is not None and revision in util.to_tuple( + dependencies + ): + raise DependencyLoopDetected(revision) + + self.verify_rev_id(revision) + self.revision = revision + self.down_revision = tuple_rev_as_scalar(util.to_tuple(down_revision)) + self.dependencies = tuple_rev_as_scalar(util.to_tuple(dependencies)) + self._orig_branch_labels = util.to_tuple(branch_labels, default=()) + self.branch_labels = set(self._orig_branch_labels) + + def __repr__(self) -> str: + args = [repr(self.revision), repr(self.down_revision)] + if self.dependencies: + args.append("dependencies=%r" % (self.dependencies,)) + if self.branch_labels: + args.append("branch_labels=%r" % (self.branch_labels,)) + return "%s(%s)" % (self.__class__.__name__, ", ".join(args)) + + def add_nextrev(self, revision: Revision) -> None: + self._all_nextrev = self._all_nextrev.union([revision.revision]) + if self.revision in revision._versioned_down_revisions: + self.nextrev = self.nextrev.union([revision.revision]) + + @property + def _all_down_revisions(self) -> Tuple[str, ...]: + return util.dedupe_tuple( + util.to_tuple(self.down_revision, default=()) + + self._resolved_dependencies + ) + + @property + def _normalized_down_revisions(self) -> Tuple[str, ...]: + """return immediate down revisions for a rev, omitting dependencies + that are still dependencies of ancestors. + + """ + return util.dedupe_tuple( + util.to_tuple(self.down_revision, default=()) + + self._normalized_resolved_dependencies + ) + + @property + def _versioned_down_revisions(self) -> Tuple[str, ...]: + return util.to_tuple(self.down_revision, default=()) + + @property + def is_head(self) -> bool: + """Return True if this :class:`.Revision` is a 'head' revision. + + This is determined based on whether any other :class:`.Script` + within the :class:`.ScriptDirectory` refers to this + :class:`.Script`. Multiple heads can be present. + + """ + return not bool(self.nextrev) + + @property + def _is_real_head(self) -> bool: + return not bool(self._all_nextrev) + + @property + def is_base(self) -> bool: + """Return True if this :class:`.Revision` is a 'base' revision.""" + + return self.down_revision is None + + @property + def _is_real_base(self) -> bool: + """Return True if this :class:`.Revision` is a "real" base revision, + e.g. that it has no dependencies either.""" + + # we use self.dependencies here because this is called up + # in initialization where _real_dependencies isn't set up + # yet + return self.down_revision is None and self.dependencies is None + + @property + def is_branch_point(self) -> bool: + """Return True if this :class:`.Script` is a branch point. + + A branchpoint is defined as a :class:`.Script` which is referred + to by more than one succeeding :class:`.Script`, that is more + than one :class:`.Script` has a `down_revision` identifier pointing + here. + + """ + return len(self.nextrev) > 1 + + @property + def _is_real_branch_point(self) -> bool: + """Return True if this :class:`.Script` is a 'real' branch point, + taking into account dependencies as well. + + """ + return len(self._all_nextrev) > 1 + + @property + def is_merge_point(self) -> bool: + """Return True if this :class:`.Script` is a merge point.""" + + return len(self._versioned_down_revisions) > 1 + + +@overload +def tuple_rev_as_scalar(rev: None) -> None: ... + + +@overload +def tuple_rev_as_scalar( + rev: Union[Tuple[_T, ...], List[_T]], +) -> Union[_T, Tuple[_T, ...], List[_T]]: ... + + +def tuple_rev_as_scalar( + rev: Optional[Sequence[_T]], +) -> Union[_T, Sequence[_T], None]: + if not rev: + return None + elif len(rev) == 1: + return rev[0] + else: + return rev + + +def is_revision(rev: Any) -> Revision: + assert isinstance(rev, Revision) + return rev diff --git a/venv/lib/python3.12/site-packages/alembic/script/write_hooks.py b/venv/lib/python3.12/site-packages/alembic/script/write_hooks.py new file mode 100644 index 00000000..3dd49d91 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/script/write_hooks.py @@ -0,0 +1,181 @@ +# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls +# mypy: no-warn-return-any, allow-any-generics + +from __future__ import annotations + +import importlib.util +import os +import shlex +import subprocess +import sys +from typing import Any +from typing import Callable +from typing import TYPE_CHECKING + +from .. import util +from ..util import compat +from ..util.pyfiles import _preserving_path_as_str + +if TYPE_CHECKING: + from ..config import PostWriteHookConfig + +REVISION_SCRIPT_TOKEN = "REVISION_SCRIPT_FILENAME" + +_registry: dict = {} + + +def register(name: str) -> Callable: + """A function decorator that will register that function as a write hook. + + See the documentation linked below for an example. + + .. seealso:: + + :ref:`post_write_hooks_custom` + + + """ + + def decorate(fn): + _registry[name] = fn + return fn + + return decorate + + +def _invoke( + name: str, + revision_path: str | os.PathLike[str], + options: PostWriteHookConfig, +) -> Any: + """Invokes the formatter registered for the given name. + + :param name: The name of a formatter in the registry + :param revision: string path to the revision file + :param options: A dict containing kwargs passed to the + specified formatter. + :raises: :class:`alembic.util.CommandError` + """ + revision_path = _preserving_path_as_str(revision_path) + try: + hook = _registry[name] + except KeyError as ke: + raise util.CommandError( + f"No formatter with name '{name}' registered" + ) from ke + else: + return hook(revision_path, options) + + +def _run_hooks( + path: str | os.PathLike[str], hooks: list[PostWriteHookConfig] +) -> None: + """Invoke hooks for a generated revision.""" + + for hook in hooks: + name = hook["_hook_name"] + try: + type_ = hook["type"] + except KeyError as ke: + raise util.CommandError( + f"Key '{name}.type' (or 'type' in toml) is required " + f"for post write hook {name!r}" + ) from ke + else: + with util.status( + f"Running post write hook {name!r}", newline=True + ): + _invoke(type_, path, hook) + + +def _parse_cmdline_options(cmdline_options_str: str, path: str) -> list[str]: + """Parse options from a string into a list. + + Also substitutes the revision script token with the actual filename of + the revision script. + + If the revision script token doesn't occur in the options string, it is + automatically prepended. + """ + if REVISION_SCRIPT_TOKEN not in cmdline_options_str: + cmdline_options_str = REVISION_SCRIPT_TOKEN + " " + cmdline_options_str + cmdline_options_list = shlex.split( + cmdline_options_str, posix=compat.is_posix + ) + cmdline_options_list = [ + option.replace(REVISION_SCRIPT_TOKEN, path) + for option in cmdline_options_list + ] + return cmdline_options_list + + +def _get_required_option(options: dict, name: str) -> str: + try: + return options[name] + except KeyError as ke: + raise util.CommandError( + f"Key {options['_hook_name']}.{name} is required for post " + f"write hook {options['_hook_name']!r}" + ) from ke + + +def _run_hook( + path: str, options: dict, ignore_output: bool, command: list[str] +) -> None: + cwd: str | None = options.get("cwd", None) + cmdline_options_str = options.get("options", "") + cmdline_options_list = _parse_cmdline_options(cmdline_options_str, path) + + kw: dict[str, Any] = {} + if ignore_output: + kw["stdout"] = kw["stderr"] = subprocess.DEVNULL + + subprocess.run([*command, *cmdline_options_list], cwd=cwd, **kw) + + +@register("console_scripts") +def console_scripts( + path: str, + options: dict, + ignore_output: bool = False, + verify_version: tuple[int, ...] | None = None, +) -> None: + entrypoint_name = _get_required_option(options, "entrypoint") + for entry in compat.importlib_metadata_get("console_scripts"): + if entry.name == entrypoint_name: + impl: Any = entry + break + else: + raise util.CommandError( + f"Could not find entrypoint console_scripts.{entrypoint_name}" + ) + + if verify_version: + pyscript = ( + f"import {impl.module}; " + f"assert tuple(int(x) for x in {impl.module}.__version__.split('.')) >= {verify_version}, " # noqa: E501 + f"'need exactly version {verify_version} of {impl.name}'; " + f"{impl.module}.{impl.attr}()" + ) + else: + pyscript = f"import {impl.module}; {impl.module}.{impl.attr}()" + + command = [sys.executable, "-c", pyscript] + _run_hook(path, options, ignore_output, command) + + +@register("exec") +def exec_(path: str, options: dict, ignore_output: bool = False) -> None: + executable = _get_required_option(options, "executable") + _run_hook(path, options, ignore_output, command=[executable]) + + +@register("module") +def module(path: str, options: dict, ignore_output: bool = False) -> None: + module_name = _get_required_option(options, "module") + + if importlib.util.find_spec(module_name) is None: + raise util.CommandError(f"Could not find module {module_name}") + + command = [sys.executable, "-m", module_name] + _run_hook(path, options, ignore_output, command) diff --git a/venv/lib/python3.12/site-packages/alembic/templates/async/README b/venv/lib/python3.12/site-packages/alembic/templates/async/README new file mode 100644 index 00000000..e0d0858f --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/async/README @@ -0,0 +1 @@ +Generic single-database configuration with an async dbapi. \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/alembic/templates/async/__pycache__/env.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/templates/async/__pycache__/env.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d9da40d2da4b52608ea6b5684077399d43cea02 GIT binary patch literal 3340 zcmai0O>Eo96&{L|L{YY6$Fbws*<|c0cC?M{P2#RwY`cJ!c8hIVBM5d6tBXLBW0?v? zszWNa)1+|fqCda_MGxIv_K;%>r$7!p?!~_L62Ug)WgQenf6(3xC`E&u`rc5Kdo=ZD>*Ew5f5k!6ls3^)B|;t(lbDiCirB|&sTwQBB&20q zt|~=E(252uq# zCv0F}D)yWGrutK*m@!i_5$z3=VzfU6*piAECH zkYGI6JI-hRg|bU|DbQ9do85Gs6{`#*rRKUea)IM8!?#=qMx(m7;TR?6lr4vqR=8U& z8IeX3D4(tSL4q^C&K;w7M@bmS@U;E`ugApi%vF&4Ml+?z9S?~gk0vXLi0bwah_6N? zGiJ)BVo6Wq+wD+Gz1ku%BH6Hy9Pu@9d(S$3C#7%{NkVuM_}h+!GT_q0SI9NuNo$Zk zpMV|YtjnK}H7TFeKY*y-sXMf4mAQ^C^r+>~X?JDC2LDads%x@YO{360TD3eH`m)ZM zNBvcXLv$!zv;0+Bse3-v>Bo1zFXVN{q>c+5hki#y<0`1Q_3~<$L&2hM7_8>&%Qh2Q ztBfeV)Zk3)R;a8oYP!y}PuV@@P=_%ScJb}P;_VNq z@6u&3cTcx$T%r9XH+9>F{kzfjXW2S4>OQ-Yr($_lwS2K!*+Pq+&&BdC^2Cas!L6E4 z-MU|^`)#B5Gu{tv-8NQPbz`>9?T-SjrgOdO`5QJ1;*MTrW?l(omJ}kcTx*U+Z&gF2C#5bc1t#Q+VwdPwmbtmcl9!;!G5dDmNDnEYR%Stc9Kq=l z?xYB>P4aS-qz9WCZD{oQ#I-%``sU3eCEZkp8_MuuU-oEp`fz0IaAfkycb^Ym{dX!J zPrS?!ZLpb|Y@{X+Qn@2F+f*+$)QkHn{f|tv%gt1-k;=XH@N0Kc!;91M&UAe7on)Ya z&)TSAw2=)&2*DB-Fz^Q;00ug9m3Bd)P}HEfBg`gGrEbafXq^HHQK^ce?%MCbL}dL$ zQ6s98?g|^^e~Oy4i#oQHkMVP0L?lB#E^>sQi5Pqcsk88cBtWq+3HUIO!Jz4uPDYBK z2mS(zy$i2R(h0|tu`fyo$`AgQ8uFcv?ROSKI=0 z>UFx`aeAeLvK>v*Gkc1gt&Y1)sf)ptQ&9G4Y#T!k?IC$UDp*5rv?yfrWJ@yTAA$5| zvB&by;&Jj&dLUJ@Z-~RtABcF{?GSIl(JeIPP!o_^#$OcFEjS%|Elao;4AvVh+Dpe? zdnbVF@&ftvd+^M`Gl2;rY(k2DCe~aa@e}93r`N)i0crYs8O{UczVV?0Cq?emMLdo*7^q8i+rAAJ0zBq%3xSHMj3^|MI7ph7 zS93iEOQzc*cqoKLfQd$FSOsTMFT6)|A(Q7o;(d6$-@xmiQJ43b&OK4 zKn(tkZ0r!Ddv2#N~dLa$`-I!F! zDZBDHq{pPfd&=&30_jN!WOj2B(o>1T*OR+frNUg_?o1Ntc@_94N*Mi*1=K?K^p@{w znAyF?M2Q>~2;TnkvHSGSDfbbl=Q^^KmxH7TkL3n3#7czbcccZYlQ4qA7{PArO0Ll7 zZ-c;XcsztZh#z>Z@8`E3-P#|>?F~#FDAPO-69Aux#Y_1iJ_UV{vE4G9r{&o&^*DZN zpmlRN%)h}dV>Y~fa)hj4q?f<$&$c%%@*6%QJT^m&7TG?~PH?>ImhjdfU|bxOB8|nz ziZ2PWmKPnQCA_HdE3lyWgwPA>H-THX+57xDOdt$-TktxTB}w`d$-E$!|4znVkSj09 zdq5nkaFYJ&;x8|5CV*75raab=$DYemUn-ej+-qj18ri9RWf~HB=-g(uH83nq9KWMT w6HnegCeS@Q+rqAu$x5`9lcg&lsEltegl6{T3tuW@2g-%NDCZB9ak0t&0^f!H0RR91 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/templates/async/alembic.ini.mako b/venv/lib/python3.12/site-packages/alembic/templates/async/alembic.ini.mako new file mode 100644 index 00000000..02ccb0f6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/async/alembic.ini.mako @@ -0,0 +1,149 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# this is typically a path given in POSIX (e.g. forward slashes) +# format, relative to the token %(here)s which refers to the location of this +# ini file +script_location = ${script_location} + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s +# Or organize into date-based subdirectories (requires recursive_version_locations = true) +# file_template = %%(year)d/%%(month).2d/%%(day).2d_%%(hour).2d%%(minute).2d_%%(second).2d_%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. for multiple paths, the path separator +# is defined by "path_separator" below. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the tzdata library which can be installed by adding +# `alembic[tz]` to the pip requirements. +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to /versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "path_separator" +# below. +# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions + +# path_separator; This indicates what character is used to split lists of file +# paths, including version_locations and prepend_sys_path within configparser +# files such as alembic.ini. +# The default rendered in new alembic.ini files is "os", which uses os.pathsep +# to provide os-dependent path splitting. +# +# Note that in order to support legacy alembic.ini files, this default does NOT +# take place if path_separator is not present in alembic.ini. If this +# option is omitted entirely, fallback logic is as follows: +# +# 1. Parsing of the version_locations option falls back to using the legacy +# "version_path_separator" key, which if absent then falls back to the legacy +# behavior of splitting on spaces and/or commas. +# 2. Parsing of the prepend_sys_path option falls back to the legacy +# behavior of splitting on spaces, commas, or colons. +# +# Valid values for path_separator are: +# +# path_separator = : +# path_separator = ; +# path_separator = space +# path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +path_separator = os + + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +# database URL. This is consumed by the user-maintained env.py script only. +# other means of configuring database URLs may be customized within the env.py +# file. +sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module +# hooks = ruff +# ruff.type = module +# ruff.module = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Alternatively, use the exec runner to execute a binary found on your PATH +# hooks = ruff +# ruff.type = exec +# ruff.executable = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration. This is also consumed by the user-maintained +# env.py script only. +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/venv/lib/python3.12/site-packages/alembic/templates/async/env.py b/venv/lib/python3.12/site-packages/alembic/templates/async/env.py new file mode 100644 index 00000000..9f2d5194 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/async/env.py @@ -0,0 +1,89 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = None + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = async_engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/venv/lib/python3.12/site-packages/alembic/templates/async/script.py.mako b/venv/lib/python3.12/site-packages/alembic/templates/async/script.py.mako new file mode 100644 index 00000000..11016301 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/async/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/venv/lib/python3.12/site-packages/alembic/templates/generic/README b/venv/lib/python3.12/site-packages/alembic/templates/generic/README new file mode 100644 index 00000000..98e4f9c4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/generic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/alembic/templates/generic/__pycache__/env.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/templates/generic/__pycache__/env.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cad6d494dbc24b434b003ad981f3a0847d9ab40e GIT binary patch literal 2554 zcmZuz&u z5+jg;)I+H%RXuU(fol~GJ@g+4*Ir_g$YEtvRTY(bi1m;2@gPhBuAa>KC~%ra>G#4ejQDJ@W^TGB(SRC62) zX5(OCWQEP?y_^WeLE(kl{Rdu85$n!X!g6*k2nrg0+9SlG;Y1}CO1<$RV$h!8F(QU& zNT%>Sx+Vn{;oxe&p`mXuh97$c9r8>b_TpOT38jwl-&!Q_fwILuKr_e{mLmeX2XVM% zMZAZWg?wE50y=c3ZsV$1rW!LH+r_4hbI!to1qICEs$-CXs$zKVE}AY5idm<`#cYvq zl~)okn`{wR>Mp|?{^ritzQ1N0*ml5Tn~X**l_F|!(@rr*e1k)UEG|#e)|??4xWc_ zOPXo%44p5zp;;E}-wC&0AS*<#GjcAE{q&@2GCy0{mx}`XZ?|3UzAT_0*b1wW`Y+7V)CCRwYJW@OGEAdo!b(-bG(%&Wm$FPo zsAiSsP1|t2q+x0n(OJo_hLcay^Oy~Gi^Qt-J<-&c}lk{G{N=0c>>43#HePw zn$G>aG_BjEUX$|z<)_W%_{97A^s+A1L z)FGtkgD1zak^Mh1x@7|xfjwNE*cvq*;EMqH9Mzm00QDj7blFIAT}L{xaeVB}<&%_iJLNa1m*8-9gEU@$fYOp_ zo1u;JGvb<4U9LH(PF@5+ZY<(I7=l=g9ncNH=wIvF{iQGR>6{D@*q2Vn5BM^WaN zj*i0I74G{3yZ~frG%(SY(=BZj_V;UV#fYNr$tay|r6!xH$*t76c5T2 z4r72(Qt^8)t1%EIwIvQU#i0#x@|BeSeyNq2Y-T1mr5sdu;M8hn_e54WyL)j!nAo|1 cq~X=8f%~R7@=6-ol1BcL&TL7;bP_WC2U~qq`Tzg` literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/templates/generic/alembic.ini.mako b/venv/lib/python3.12/site-packages/alembic/templates/generic/alembic.ini.mako new file mode 100644 index 00000000..0127b2af --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/generic/alembic.ini.mako @@ -0,0 +1,149 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# this is typically a path given in POSIX (e.g. forward slashes) +# format, relative to the token %(here)s which refers to the location of this +# ini file +script_location = ${script_location} + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s +# Or organize into date-based subdirectories (requires recursive_version_locations = true) +# file_template = %%(year)d/%%(month).2d/%%(day).2d_%%(hour).2d%%(minute).2d_%%(second).2d_%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. for multiple paths, the path separator +# is defined by "path_separator" below. +prepend_sys_path = . + + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the tzdata library which can be installed by adding +# `alembic[tz]` to the pip requirements. +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to /versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "path_separator" +# below. +# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions + +# path_separator; This indicates what character is used to split lists of file +# paths, including version_locations and prepend_sys_path within configparser +# files such as alembic.ini. +# The default rendered in new alembic.ini files is "os", which uses os.pathsep +# to provide os-dependent path splitting. +# +# Note that in order to support legacy alembic.ini files, this default does NOT +# take place if path_separator is not present in alembic.ini. If this +# option is omitted entirely, fallback logic is as follows: +# +# 1. Parsing of the version_locations option falls back to using the legacy +# "version_path_separator" key, which if absent then falls back to the legacy +# behavior of splitting on spaces and/or commas. +# 2. Parsing of the prepend_sys_path option falls back to the legacy +# behavior of splitting on spaces, commas, or colons. +# +# Valid values for path_separator are: +# +# path_separator = : +# path_separator = ; +# path_separator = space +# path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +# database URL. This is consumed by the user-maintained env.py script only. +# other means of configuring database URLs may be customized within the env.py +# file. +sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module +# hooks = ruff +# ruff.type = module +# ruff.module = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Alternatively, use the exec runner to execute a binary found on your PATH +# hooks = ruff +# ruff.type = exec +# ruff.executable = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration. This is also consumed by the user-maintained +# env.py script only. +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/venv/lib/python3.12/site-packages/alembic/templates/generic/env.py b/venv/lib/python3.12/site-packages/alembic/templates/generic/env.py new file mode 100644 index 00000000..36112a3c --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/generic/env.py @@ -0,0 +1,78 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = None + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/venv/lib/python3.12/site-packages/alembic/templates/generic/script.py.mako b/venv/lib/python3.12/site-packages/alembic/templates/generic/script.py.mako new file mode 100644 index 00000000..11016301 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/generic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/venv/lib/python3.12/site-packages/alembic/templates/multidb/README b/venv/lib/python3.12/site-packages/alembic/templates/multidb/README new file mode 100644 index 00000000..f046ec91 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/multidb/README @@ -0,0 +1,12 @@ +Rudimentary multi-database configuration. + +Multi-DB isn't vastly different from generic. The primary difference is that it +will run the migrations N times (depending on how many databases you have +configured), providing one engine name and associated context for each run. + +That engine name will then allow the migration to restrict what runs within it to +just the appropriate migrations for that engine. You can see this behavior within +the mako template. + +In the provided configuration, you'll need to have `databases` provided in +alembic's config, and an `sqlalchemy.url` provided for each engine name. diff --git a/venv/lib/python3.12/site-packages/alembic/templates/multidb/__pycache__/env.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/templates/multidb/__pycache__/env.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49b8b1f3e3801905c73266dd5a1d8826fa4fd925 GIT binary patch literal 4765 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/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "path_separator" +# below. +# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions + +# path_separator; This indicates what character is used to split lists of file +# paths, including version_locations and prepend_sys_path within configparser +# files such as alembic.ini. +# The default rendered in new alembic.ini files is "os", which uses os.pathsep +# to provide os-dependent path splitting. +# +# Note that in order to support legacy alembic.ini files, this default does NOT +# take place if path_separator is not present in alembic.ini. If this +# option is omitted entirely, fallback logic is as follows: +# +# 1. Parsing of the version_locations option falls back to using the legacy +# "version_path_separator" key, which if absent then falls back to the legacy +# behavior of splitting on spaces and/or commas. +# 2. Parsing of the prepend_sys_path option falls back to the legacy +# behavior of splitting on spaces, commas, or colons. +# +# Valid values for path_separator are: +# +# path_separator = : +# path_separator = ; +# path_separator = space +# path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +# for multiple database configuration, new named sections are added +# which each include a distinct ``sqlalchemy.url`` entry. A custom value +# ``databases`` is added which indicates a listing of the per-database sections. +# The ``databases`` entry as well as the URLs present in the ``[engine1]`` +# and ``[engine2]`` sections continue to be consumed by the user-maintained env.py +# script only. + +databases = engine1, engine2 + +[engine1] +sqlalchemy.url = driver://user:pass@localhost/dbname + +[engine2] +sqlalchemy.url = driver://user:pass@localhost/dbname2 + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module +# hooks = ruff +# ruff.type = module +# ruff.module = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Alternatively, use the exec runner to execute a binary found on your PATH +# hooks = ruff +# ruff.type = exec +# ruff.executable = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration. This is also consumed by the user-maintained +# env.py script only. +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/venv/lib/python3.12/site-packages/alembic/templates/multidb/env.py b/venv/lib/python3.12/site-packages/alembic/templates/multidb/env.py new file mode 100644 index 00000000..e937b64e --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/multidb/env.py @@ -0,0 +1,140 @@ +import logging +from logging.config import fileConfig +import re + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +USE_TWOPHASE = False + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) +logger = logging.getLogger("alembic.env") + +# gather section names referring to different +# databases. These are named "engine1", "engine2" +# in the sample .ini file. +db_names = config.get_main_option("databases", "") + +# add your model's MetaData objects here +# for 'autogenerate' support. These must be set +# up to hold just those tables targeting a +# particular database. table.tometadata() may be +# helpful here in case a "copy" of +# a MetaData is needed. +# from myapp import mymodel +# target_metadata = { +# 'engine1':mymodel.metadata1, +# 'engine2':mymodel.metadata2 +# } +target_metadata = {} + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + # for the --sql use case, run migrations for each URL into + # individual files. + + engines = {} + for name in re.split(r",\s*", db_names): + engines[name] = rec = {} + rec["url"] = context.config.get_section_option(name, "sqlalchemy.url") + + for name, rec in engines.items(): + logger.info("Migrating database %s" % name) + file_ = "%s.sql" % name + logger.info("Writing output to %s" % file_) + with open(file_, "w") as buffer: + context.configure( + url=rec["url"], + output_buffer=buffer, + target_metadata=target_metadata.get(name), + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + with context.begin_transaction(): + context.run_migrations(engine_name=name) + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # for the direct-to-DB use case, start a transaction on all + # engines, then run all migrations, then commit all transactions. + + engines = {} + for name in re.split(r",\s*", db_names): + engines[name] = rec = {} + rec["engine"] = engine_from_config( + context.config.get_section(name, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + for name, rec in engines.items(): + engine = rec["engine"] + rec["connection"] = conn = engine.connect() + + if USE_TWOPHASE: + rec["transaction"] = conn.begin_twophase() + else: + rec["transaction"] = conn.begin() + + try: + for name, rec in engines.items(): + logger.info("Migrating database %s" % name) + context.configure( + connection=rec["connection"], + upgrade_token="%s_upgrades" % name, + downgrade_token="%s_downgrades" % name, + target_metadata=target_metadata.get(name), + ) + context.run_migrations(engine_name=name) + + if USE_TWOPHASE: + for rec in engines.values(): + rec["transaction"].prepare() + + for rec in engines.values(): + rec["transaction"].commit() + except: + for rec in engines.values(): + rec["transaction"].rollback() + raise + finally: + for rec in engines.values(): + rec["connection"].close() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/venv/lib/python3.12/site-packages/alembic/templates/multidb/script.py.mako b/venv/lib/python3.12/site-packages/alembic/templates/multidb/script.py.mako new file mode 100644 index 00000000..8e667d84 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/multidb/script.py.mako @@ -0,0 +1,51 @@ +<%! +import re + +%>"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade(engine_name: str) -> None: + """Upgrade schema.""" + globals()["upgrade_%s" % engine_name]() + + +def downgrade(engine_name: str) -> None: + """Downgrade schema.""" + globals()["downgrade_%s" % engine_name]() + +<% + db_names = config.get_main_option("databases") +%> + +## generate an "upgrade_() / downgrade_()" function +## for each database name in the ini file. + +% for db_name in re.split(r',\s*', db_names): + +def upgrade_${db_name}() -> None: + """Upgrade ${db_name} schema.""" + ${context.get("%s_upgrades" % db_name, "pass")} + + +def downgrade_${db_name}() -> None: + """Downgrade ${db_name} schema.""" + ${context.get("%s_downgrades" % db_name, "pass")} + +% endfor diff --git a/venv/lib/python3.12/site-packages/alembic/templates/pyproject/README b/venv/lib/python3.12/site-packages/alembic/templates/pyproject/README new file mode 100644 index 00000000..fdacc05f --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/pyproject/README @@ -0,0 +1 @@ +pyproject configuration, based on the generic configuration. \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/alembic/templates/pyproject/__pycache__/env.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/templates/pyproject/__pycache__/env.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c14aae7be2cd0c3b046748769d5b3be56ad488fc GIT binary patch literal 2556 zcmZuz%}*Og6rc63?cMcPfS4pe>4XA~qhLasG-_3~3h9TURw#*)ELl6B2l4~zm0hz3bs;Ww*-jE=Q#Hnv~y*5zR($0Ht-n=*a z-u!;!KQb8=!SitPPxGpb(BE8%9FNP5^GLqs@!ZcPZiAbz~87_yQ0PAaP~CSgp$x z74Z&Q7K%yj3+T|ThK=iHm1@j%Y!90@E;tJd78J06>yAN6s*2&cy=ZzkEM|ie53@zW zRbENFY_dgMYj_N6_?uf_2mYFEVA}zUjXw|CaRHV%jp}00f+3=&>%?W+yhVali-Z!a zd3c#vRte+TCid={u4~%WAYm8@>y!k$4VEkuY&dp-VX{PQY!hO@EdR`*!bB7WS~>cl8Yz6eu(j%lDHMuBIe%d^aPrOg2mmPZ7b2XiK(>io~iuZGhOC2MWVQJbj=cnB!^w564 zG;?X%gDp?Nn!8#Rmcf4O^QJz{NZqwG1{SyJQl|!AF%64LZqpw;yoXBXkkON{iFf#J z%J5o6d#6#VudU*iir3H1yihN#Uf-5dZ7JW9@>}WL_UXdb;Lz6K*pn~U^B3MI@pxh{ zi`2fhGS*VYHkI*hIoFm)Tk`0JjNgh#ooFlLEoFS){*Zc( z+M7~`lH&Is9mhxZ|LEw34R8eh@N{Bp)O3I`LhN%~b9w;WN6gdZBhB+1-3;b9_-#0y zfR2#94)9q7>C^#87h`_HrDVZe@fCiN=oSP6quPrTpl`02rkvd=zeD{L2do#S@%uxZ zR!rNBY?PlVU!H4N)=dZ++)aE}0kA2*ZiAq-k4wC(kTKQC$Qh0D`_tn`6k|F7gP%W& zvY>Qy6y~mQ-yh%wAj_hGiETOCmWNvM&@UgfhcCB=FaI|GLjG_oH?rM7_;~8)slB8$ zkXZd{Cyms6TN!C7BOA)-lk)+<&a~t+Tc^(c{^=Vrrl@-|%I4dd$yR1^Gjnb`J2W|{w)i%M)OXjF(YGon>zQ-=kzwB(hwVJeU7Vc}pM9F2orpg_DTDlcB0gJE zo}W*0c_u#lQIeOR64s#hTruNkEvE_;S1pAVr5roH+Ur)>fuOd@4l{)n4VrSI(+B>a zp~8tdtT6{gP$xfUdJ#KS_zn0g+J!k37FhlmObv^CLJMHyJmC>|?TCUPyh0PNQSlWT zevO8AWMHMAMjwr?CP0+bwm8%hht|c(ms0lorFL$zm7Cm<3Q*mFQ>(e%6M5n6?!^IN fV&?*qhF7nK?i=FBOKE6R8u?2)vndVJNyzjc{>WAT literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/templates/pyproject/alembic.ini.mako b/venv/lib/python3.12/site-packages/alembic/templates/pyproject/alembic.ini.mako new file mode 100644 index 00000000..3d10f0e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/pyproject/alembic.ini.mako @@ -0,0 +1,44 @@ +# A generic, single database configuration. + +[alembic] + +# database URL. This is consumed by the user-maintained env.py script only. +# other means of configuring database URLs may be customized within the env.py +# file. +sqlalchemy.url = driver://user:pass@localhost/dbname + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/venv/lib/python3.12/site-packages/alembic/templates/pyproject/env.py b/venv/lib/python3.12/site-packages/alembic/templates/pyproject/env.py new file mode 100644 index 00000000..36112a3c --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/pyproject/env.py @@ -0,0 +1,78 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = None + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/venv/lib/python3.12/site-packages/alembic/templates/pyproject/pyproject.toml.mako b/venv/lib/python3.12/site-packages/alembic/templates/pyproject/pyproject.toml.mako new file mode 100644 index 00000000..7edd43b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/pyproject/pyproject.toml.mako @@ -0,0 +1,84 @@ +[tool.alembic] + +# path to migration scripts. +# this is typically a path given in POSIX (e.g. forward slashes) +# format, relative to the token %(here)s which refers to the location of this +# ini file +script_location = "${script_location}" + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = "%%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s" +# Or organize into date-based subdirectories (requires recursive_version_locations = true) +# file_template = "%%(year)d/%%(month).2d/%%(day).2d_%%(hour).2d%%(minute).2d_%%(second).2d_%%(rev)s_%%(slug)s" + +# additional paths to be prepended to sys.path. defaults to the current working directory. +prepend_sys_path = [ + "." +] + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the tzdata library which can be installed by adding +# `alembic[tz]` to the pip requirements. +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to /versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# version_locations = [ +# "%(here)s/alembic/versions", +# "%(here)s/foo/bar" +# ] + + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = "utf-8" + +# This section defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples +# [[tool.alembic.post_write_hooks]] +# format using "black" - use the console_scripts runner, +# against the "black" entrypoint +# name = "black" +# type = "console_scripts" +# entrypoint = "black" +# options = "-l 79 REVISION_SCRIPT_FILENAME" +# +# [[tool.alembic.post_write_hooks]] +# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module +# name = "ruff" +# type = "module" +# module = "ruff" +# options = "check --fix REVISION_SCRIPT_FILENAME" +# +# [[tool.alembic.post_write_hooks]] +# Alternatively, use the exec runner to execute a binary found on your PATH +# name = "ruff" +# type = "exec" +# executable = "ruff" +# options = "check --fix REVISION_SCRIPT_FILENAME" + diff --git a/venv/lib/python3.12/site-packages/alembic/templates/pyproject/script.py.mako b/venv/lib/python3.12/site-packages/alembic/templates/pyproject/script.py.mako new file mode 100644 index 00000000..11016301 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/pyproject/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/README b/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/README new file mode 100644 index 00000000..dfd718d3 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/README @@ -0,0 +1 @@ +pyproject configuration, with an async dbapi. \ No newline at end of file diff --git a/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/__pycache__/env.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/__pycache__/env.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0af333535c1881fcbb048d1303467afbdd4ea55 GIT binary patch literal 3350 zcmai0O>Eo96&{L|L{YY6$Fbws*<|c0cC?M{P2#RwY`cJ!c8hIVBM5d6tBXLBW0?v? zszWNa)1+|fqCda_MGxIv_K;%>r$7!p?!~_L62Ug)WgQenf6(3xC`E&u`rc5Kdo=ZD>*Ew5f5k!6ls3^)B|;t(lbDiCirB|&sTwQBB&20q zt|~=E(252uq# zCv0F}D)yWGrutK*m@!i_5$z3=VzfU6*piAECH zkYGI6JI-hRg|bU|DbQ9do85Gs6{`#*rRKUea)IM8!?#=qMx(m7;TR?6lr4vqR=8U& z8IeX3D4(tSL4q^C&K;w7M@bmS@U;E`ugApi%vF&4Ml+?z9S?~gk0vXLi0bwah_6N? zGiJ)BVo6Wq+wD+Gz1ku%BH6Hy9Pu@9d(S$3C#7%{NkVuM_}h+!GT_q0SI9NuNo$Zk zpMV|YtjnK}H7TFeKY*y-sXMf4mAQ^C^r+>~X?JDC2LDads%x@YO{360TD3eH`m)ZM zNBvcXLv$!zv;0+Bse3-v>Bo1zFXVN{q>c+5hki#y<0`1Q_3~<$L&2hM7_8>&%Qh2Q ztBfeV)Zk3)R;a8oYP!y}PuV@@P=_%ScJb}P;_VNq z@6u&3cTcx$T%r9XH+9>F{kzfjXW2S4>OQ-Yr($_lwS2K!*+Pq+&&BdC^2Cas!L6E4 z-MU|^`)#B5Gu{tv-8NQPbz`>9?T-SjrgOdO`5QJ1;*MTrW?l(omJ}kcTx*U*DZubNOAbrW?$gGa%wK7|$7`+Dvf6y*b-j zo~vy@44oUZ*RIcbu;m%hxvQ5!4fb1Iwv0KSRcp5H1E;o8<8B4NLX_kjn3%0?1S2PR zQfj9fN0_>Vdn&?fle`=y>A|K(8ybB+acxh#zIpRVNjH_@hBAEEmpvMtJ{%c49GQIb z-RHws|DB4*6E8DF8*HW~8>z{IRPIR4Hr0y__2Rxt|05Iaax;}{q;ju4{O+C9@Zz+* zGaX-iCmCqqy*7RrZHxmELa>+xAp8Lc0EEt5rCnMm6g4RG2))Tusat$KTBm?RRJ5YJ zyY@SP5m`S`-iYs{yTS(fpYkT{@{TR#WBeQ#5owW+i)`U%A_gBq>MXn<9Z)Px2R;mB zFlf4^lab=*fxm!a@4{=7bi(mu?2FQY@`Jyn2L8YI)1dlncrhdIWa5kGk_HSr4Rd)5 zp4N}S6}JGVdY$ffoL=eRZAX*z%%0+AtK%+H>Y{Pw6uf;J+s2SXdq^IT3Rcq_EehE@ z*^*58M_R_J}-U%SQyg)wv9z1jKOkjctn~9sIrK$`wuhLb_LZ+z$g z-T^>+26J?7xd6!_xFT>O{sOQ@&@NCB0bln#*RaGKqHDXZ!t`#QX4gzk%03$tyssJ~H&x zBQ?`h#~SL`=kGSh=Nsem&zASpZyaVX9Gw|?JoD?Bmq}$Pv3cvbj|_}Avy+YNWHX!F z&*pxs9cD(GnTbYbVt?|+US__j&OcY@0ksA$gT!%yq(_?CTMg~4J?&CcxztcD9VcP# z)uB57>KLV7ff)Q7+1zW8j?>rP3heABi$jHPNjt+*fhKlFWTelZFHFfh*QLT_V&{f} z^gT&$kKGUlMH-8b6<-o$EiXDqOL%4DS71T$385F%ZvwY&v-kORm_Qivw%~OvOOo^_l6gTc z|DBA#AXi?H_kcK7;UxXl#a~|BOaQ59O?j*#k3E;CzEm>5xYx{1HL_Ft$}}YM(7DZQ zYhYNKIDSWwCZ4=~OrU#qwuN0Qla**ICrejAP#ND`2+i!v7rs=+4wMUjQO+MI<6@Kl E1%_S%SpWb4 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/alembic.ini.mako b/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/alembic.ini.mako new file mode 100644 index 00000000..3d10f0e4 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/alembic.ini.mako @@ -0,0 +1,44 @@ +# A generic, single database configuration. + +[alembic] + +# database URL. This is consumed by the user-maintained env.py script only. +# other means of configuring database URLs may be customized within the env.py +# file. +sqlalchemy.url = driver://user:pass@localhost/dbname + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/env.py b/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/env.py new file mode 100644 index 00000000..9f2d5194 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/env.py @@ -0,0 +1,89 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = None + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = async_engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/pyproject.toml.mako b/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/pyproject.toml.mako new file mode 100644 index 00000000..7edd43b0 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/pyproject.toml.mako @@ -0,0 +1,84 @@ +[tool.alembic] + +# path to migration scripts. +# this is typically a path given in POSIX (e.g. forward slashes) +# format, relative to the token %(here)s which refers to the location of this +# ini file +script_location = "${script_location}" + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = "%%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s" +# Or organize into date-based subdirectories (requires recursive_version_locations = true) +# file_template = "%%(year)d/%%(month).2d/%%(day).2d_%%(hour).2d%%(minute).2d_%%(second).2d_%%(rev)s_%%(slug)s" + +# additional paths to be prepended to sys.path. defaults to the current working directory. +prepend_sys_path = [ + "." +] + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the tzdata library which can be installed by adding +# `alembic[tz]` to the pip requirements. +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to /versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# version_locations = [ +# "%(here)s/alembic/versions", +# "%(here)s/foo/bar" +# ] + + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = "utf-8" + +# This section defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples +# [[tool.alembic.post_write_hooks]] +# format using "black" - use the console_scripts runner, +# against the "black" entrypoint +# name = "black" +# type = "console_scripts" +# entrypoint = "black" +# options = "-l 79 REVISION_SCRIPT_FILENAME" +# +# [[tool.alembic.post_write_hooks]] +# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module +# name = "ruff" +# type = "module" +# module = "ruff" +# options = "check --fix REVISION_SCRIPT_FILENAME" +# +# [[tool.alembic.post_write_hooks]] +# Alternatively, use the exec runner to execute a binary found on your PATH +# name = "ruff" +# type = "exec" +# executable = "ruff" +# options = "check --fix REVISION_SCRIPT_FILENAME" + diff --git a/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/script.py.mako b/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/script.py.mako new file mode 100644 index 00000000..11016301 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/templates/pyproject_async/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/venv/lib/python3.12/site-packages/alembic/testing/__init__.py b/venv/lib/python3.12/site-packages/alembic/testing/__init__.py new file mode 100644 index 00000000..32915081 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/__init__.py @@ -0,0 +1,32 @@ +from sqlalchemy.testing import config +from sqlalchemy.testing import emits_warning +from sqlalchemy.testing import engines +from sqlalchemy.testing import exclusions +from sqlalchemy.testing import mock +from sqlalchemy.testing import provide_metadata +from sqlalchemy.testing import skip_if +from sqlalchemy.testing import uses_deprecated +from sqlalchemy.testing.config import combinations +from sqlalchemy.testing.config import fixture +from sqlalchemy.testing.config import requirements as requires +from sqlalchemy.testing.config import Variation +from sqlalchemy.testing.config import variation + +from .assertions import assert_raises +from .assertions import assert_raises_message +from .assertions import emits_python_deprecation_warning +from .assertions import eq_ +from .assertions import eq_ignore_whitespace +from .assertions import expect_deprecated +from .assertions import expect_raises +from .assertions import expect_raises_message +from .assertions import expect_sqlalchemy_deprecated +from .assertions import expect_sqlalchemy_deprecated_20 +from .assertions import expect_warnings +from .assertions import is_ +from .assertions import is_false +from .assertions import is_not_ +from .assertions import is_true +from .assertions import ne_ +from .fixtures import TestBase +from .util import resolve_lambda diff --git a/venv/lib/python3.12/site-packages/alembic/testing/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/testing/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fa91a97aced513580519a8a97753b84adb473e79 GIT binary patch literal 1418 zcmaKr&2AGh5XZeunxtuyG-=W{O`4=%pdy-uf&>S|fd}A(IQYQI-E~{5`;ppC+8%id zo`GlK4I(WZ5QpB7dgH{*WLv0^Sgrp4`R}nkUeA81RLTsVj}N}7`aEObv2**(WpH!p zF!qTBEO0^=IoxrO=YZ$<9B~df$8*Ga;5^S0&jZi%dEx?afftC2z(rmpE&-Q#iFhGg zh|0W7Tn1j`i^Pk-OT0q71YG4+;tFt$*MO@*EnJT3yiU9f+~5u3I`9f#A#MOSd6Rer zxW!wpL!0UXScn!GC+r;a@8+?Pf4cy@!;tk*~?-F-_H~A)U7r4iJ z#GAl<-Y4z>5BPw%A8tk4e4BU>?nJwM*I`#%``g!e4g1ct;3x61IsvN4NLk}u2py{! zjiQWCR4ff(Szh|#*r+7NUOr0vGiaAbI+>_IdXcmu5LVzMbEZa~ItHwbjWk{$M_T&A z$^fh-KZ%Z1ENr$?@mO8jv6eWb<@s1?8Ohk2vR7O|#SL*+7+&g>%8Pu|n>~`?LIb|^C5;VsmG?-@* zI8H3uh*po$&Bc-e-$`TN2nwufX_9avy--9)fq1*WH*NgMZU`s0Py;##-&*HU;4fyn zfKo&$p)8=_k3MbxJ9jv%E4_$8ODGkLEM>QBbQQfCdga;8VM*vZx($>SlqO0GWff%& zWgVrBvVqb;>7s0+^icXJ1CVJcD=?$Cu$v!S73yuk&+G=2{rccuB>Idl+PHoa$b$=I zPY;kXb0Xu38>%CBRzc5(PY+!K#c=?|aRw!3ToFpBYTwO1o9lThR@U=|qpNA@Rg?td zP`=iWVLL2da|H4;=QvKv_EPpZWe-xepR%2lxx~Yi9j5FcWv|I`e_$ANU#6@>@en+7 Zo~P_Z${vzGNLim=i0OT01N{iL`3=iZlPdrK literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/testing/__pycache__/assertions.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/testing/__pycache__/assertions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb046b9b6e4fddcefc2c54c35dd811ab6ff2f3e5 GIT binary patch literal 7477 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/testing/__pycache__/env.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/testing/__pycache__/env.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be80073bdb9870744407da9c5156b308832ffa33 GIT binary patch literal 16793 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/testing/__pycache__/fixtures.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/testing/__pycache__/fixtures.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2efbef0dcefdfc95032c4c5e893b95fd7955ee3f GIT binary patch literal 18987 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/testing/__pycache__/schemacompare.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/testing/__pycache__/schemacompare.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7d014d861cb7cd7a2af04ebab2a5dc803873d7c GIT binary patch literal 9104 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/testing/__pycache__/warnings.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/testing/__pycache__/warnings.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e3aa61e81e68a97be408f5100d28db14b57be447 GIT binary patch literal 1086 zcmb7D&1(}u6ragXl1-Y_HhxrEYqwa0fZ0Vo8d_TvsfQvIl;&XF>`s$yH`zElNxGt- zApQ&XUy$~nuvbrkmgp*YP!HaU2_il9O|sioDM%me`71<2XPleQ5#u19pPKA^TbB|{wU`-~m z<1hXU+id(lhRsASs%wceKvY~TYa+t@&?iPIFR1KO<`f>o{`e{a8 z$`8%K70c$79ojKhUxw@V&n?d0p*2PU&vL4d`c3$W#o18JoNyegd7>--;)9+%A!1;@ zQgax;+i#bhfhJw?nw0&=TGL@`ZcR6+s~L_-b4`nvb3zN84O(qzwpG-iO4+I2RIlIA zTo^wGIcs_e(sY|vik6{?CSYe;IF(xS{4`8muPyh-b=g@cIw-2Y0rnDgVhvy|2{QZ9JL>ISGP@UvN0XoB^IaJw zhTHN)OP=_c%4|GrkLO$C`QK6`k^Iw*qtwVRgrn(Ca%xWkx$lZc@%|Q>n?&1_iMgD# zeGLQ8Mw$3K{G|KT)@`FqD;^U+BO4G*%<&F&fFmaIeK}uo%(_jd*$9|ow>?}afpI5; Q@X!x5)rk$`B)b4-2kGb->c + # -> d -> e + # -> f + d = util.rev_id() + e = util.rev_id() + f = util.rev_id() + + script = ScriptDirectory.from_config(cfg) + script.generate_revision( + d, "revision d from b", head=b, splice=True, refresh=True + ) + write_script( + script, + d, + f"""\ +"Rev D" +revision = '{d}' +down_revision = '{b}' + +from alembic import op + + +def upgrade(): + op.execute("CREATE STEP 4") + + +def downgrade(): + op.execute("DROP STEP 4") + +""", + ) + + script.generate_revision( + e, "revision e from d", head=d, splice=True, refresh=True + ) + write_script( + script, + e, + f"""\ +"Rev E" +revision = '{e}' +down_revision = '{d}' + +from alembic import op + + +def upgrade(): + op.execute("CREATE STEP 5") + + +def downgrade(): + op.execute("DROP STEP 5") + +""", + ) + + script.generate_revision( + f, "revision f from b", head=b, splice=True, refresh=True + ) + write_script( + script, + f, + f"""\ +"Rev F" +revision = '{f}' +down_revision = '{b}' + +from alembic import op + + +def upgrade(): + op.execute("CREATE STEP 6") + + +def downgrade(): + op.execute("DROP STEP 6") + +""", + ) + + return d, e, f + + +def _multidb_testing_config(engines): + """alembic.ini fixture to work exactly with the 'multidb' template""" + + dir_ = _join_path(_get_staging_directory(), "scripts") + + sqlalchemy_future = "future" in config.db.__class__.__module__ + + databases = ", ".join(engines.keys()) + engines = "\n\n".join( + f"[{key}]\nsqlalchemy.url = {value.url}" + for key, value in engines.items() + ) + + return _write_config_file( + f""" +[alembic] +script_location = {dir_} +sourceless = false +sqlalchemy.future = {"true" if sqlalchemy_future else "false"} +databases = {databases} + +{engines} +[loggers] +keys = root + +[handlers] +keys = console + +[logger_root] +level = WARNING +handlers = console +qualname = + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatters] +keys = generic + +[formatter_generic] +format = %%(levelname)-5.5s [%%(name)s] %%(message)s +datefmt = %%H:%%M:%%S + """ + ) + + +def _join_path(base: str, *more: str): + return str(Path(base).joinpath(*more).as_posix()) diff --git a/venv/lib/python3.12/site-packages/alembic/testing/fixtures.py b/venv/lib/python3.12/site-packages/alembic/testing/fixtures.py new file mode 100644 index 00000000..73e42125 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/fixtures.py @@ -0,0 +1,404 @@ +from __future__ import annotations + +import configparser +from contextlib import contextmanager +import io +import os +import re +import shutil +from typing import Any +from typing import Dict +from typing import Generator +from typing import Literal +from typing import overload + +from sqlalchemy import Column +from sqlalchemy import create_mock_engine +from sqlalchemy import inspect +from sqlalchemy import MetaData +from sqlalchemy import String +from sqlalchemy import Table +from sqlalchemy import testing +from sqlalchemy import text +from sqlalchemy.testing import config +from sqlalchemy.testing import mock +from sqlalchemy.testing.assertions import eq_ +from sqlalchemy.testing.fixtures import FutureEngineMixin +from sqlalchemy.testing.fixtures import TablesTest as SQLAlchemyTablesTest +from sqlalchemy.testing.fixtures import TestBase as SQLAlchemyTestBase +from sqlalchemy.testing.util import drop_all_tables_from_metadata + +import alembic +from .assertions import _get_dialect +from .env import _get_staging_directory +from ..environment import EnvironmentContext +from ..migration import MigrationContext +from ..operations import Operations +from ..util import sqla_compat +from ..util.sqla_compat import sqla_2 + +testing_config = configparser.ConfigParser() +testing_config.read(["test.cfg"]) + + +class TestBase(SQLAlchemyTestBase): + is_sqlalchemy_future = sqla_2 + + @testing.fixture() + def clear_staging_dir(self): + yield + location = _get_staging_directory() + for filename in os.listdir(location): + file_path = os.path.join(location, filename) + if os.path.isfile(file_path) or os.path.islink(file_path): + os.unlink(file_path) + elif os.path.isdir(file_path): + shutil.rmtree(file_path) + + @contextmanager + def pushd(self, dirname) -> Generator[None, None, None]: + current_dir = os.getcwd() + try: + os.chdir(dirname) + yield + finally: + os.chdir(current_dir) + + @testing.fixture() + def pop_alembic_config_env(self): + yield + os.environ.pop("ALEMBIC_CONFIG", None) + + @testing.fixture() + def ops_context(self, migration_context): + with migration_context.begin_transaction(_per_migration=True): + yield Operations(migration_context) + + @testing.fixture + def migration_context(self, connection): + return MigrationContext.configure( + connection, opts=dict(transaction_per_migration=True) + ) + + @testing.fixture + def as_sql_migration_context(self, connection): + return MigrationContext.configure( + connection, opts=dict(transaction_per_migration=True, as_sql=True) + ) + + @testing.fixture + def connection(self): + global _connection_fixture_connection + + with config.db.connect() as conn: + _connection_fixture_connection = conn + yield conn + + _connection_fixture_connection = None + + @testing.fixture + def restore_operations(self): + """Restore runners for modified operations""" + + saved_impls = None + op_cls = None + + def _save_attrs(_op_cls): + nonlocal saved_impls, op_cls + saved_impls = _op_cls._to_impl._registry.copy() + op_cls = _op_cls + + yield _save_attrs + + if op_cls is not None and saved_impls is not None: + op_cls._to_impl._registry = saved_impls + + @config.fixture() + def metadata(self, request): + """Provide bound MetaData for a single test, dropping afterwards.""" + + from sqlalchemy.sql import schema + + metadata = schema.MetaData() + request.instance.metadata = metadata + yield metadata + del request.instance.metadata + + if ( + _connection_fixture_connection + and _connection_fixture_connection.in_transaction() + ): + trans = _connection_fixture_connection.get_transaction() + trans.rollback() + with _connection_fixture_connection.begin(): + drop_all_tables_from_metadata( + metadata, _connection_fixture_connection + ) + else: + drop_all_tables_from_metadata(metadata, config.db) + + +_connection_fixture_connection = None + + +class TablesTest(TestBase, SQLAlchemyTablesTest): + pass + + +FutureEngineMixin.is_sqlalchemy_future = True + + +def capture_db(dialect="postgresql://"): + buf = [] + + def dump(sql, *multiparams, **params): + buf.append(str(sql.compile(dialect=engine.dialect))) + + engine = create_mock_engine(dialect, dump) + return engine, buf + + +_engs: Dict[Any, Any] = {} + + +@overload +@contextmanager +def capture_context_buffer( + bytes_io: Literal[True], **kw: Any +) -> Generator[io.BytesIO, None, None]: ... + + +@overload +@contextmanager +def capture_context_buffer( + **kw: Any, +) -> Generator[io.StringIO, None, None]: ... + + +@contextmanager +def capture_context_buffer( + **kw: Any, +) -> Generator[io.StringIO | io.BytesIO, None, None]: + if kw.pop("bytes_io", False): + buf = io.BytesIO() + else: + buf = io.StringIO() + + kw.update({"dialect_name": "sqlite", "output_buffer": buf}) + conf = EnvironmentContext.configure + + def configure(*arg, **opt): + opt.update(**kw) + return conf(*arg, **opt) + + with mock.patch.object(EnvironmentContext, "configure", configure): + yield buf + + +@contextmanager +def capture_engine_context_buffer( + **kw: Any, +) -> Generator[io.StringIO, None, None]: + from .env import _sqlite_file_db + from sqlalchemy import event + + buf = io.StringIO() + + eng = _sqlite_file_db() + + conn = eng.connect() + + @event.listens_for(conn, "before_cursor_execute") + def bce(conn, cursor, statement, parameters, context, executemany): + buf.write(statement + "\n") + + kw.update({"connection": conn}) + conf = EnvironmentContext.configure + + def configure(*arg, **opt): + opt.update(**kw) + return conf(*arg, **opt) + + with mock.patch.object(EnvironmentContext, "configure", configure): + yield buf + + +def op_fixture( + dialect="default", + as_sql=False, + naming_convention=None, + literal_binds=False, + native_boolean=None, +): + opts = {} + if naming_convention: + opts["target_metadata"] = MetaData(naming_convention=naming_convention) + + class buffer_: + def __init__(self): + self.lines = [] + + def write(self, msg): + msg = msg.strip() + msg = re.sub(r"[\n\t]", "", msg) + if as_sql: + # the impl produces soft tabs, + # so search for blocks of 4 spaces + msg = re.sub(r" ", "", msg) + msg = re.sub(r"\;\n*$", "", msg) + + self.lines.append(msg) + + def flush(self): + pass + + buf = buffer_() + + class ctx(MigrationContext): + def get_buf(self): + return buf + + def clear_assertions(self): + buf.lines[:] = [] + + def assert_(self, *sql): + # TODO: make this more flexible about + # whitespace and such + eq_(buf.lines, [re.sub(r"[\n\t]", "", s) for s in sql]) + + def assert_contains(self, sql): + for stmt in buf.lines: + if re.sub(r"[\n\t]", "", sql) in stmt: + return + else: + assert False, "Could not locate fragment %r in %r" % ( + sql, + buf.lines, + ) + + if as_sql: + opts["as_sql"] = as_sql + if literal_binds: + opts["literal_binds"] = literal_binds + + ctx_dialect = _get_dialect(dialect) + if native_boolean is not None: + ctx_dialect.supports_native_boolean = native_boolean + # this is new as of SQLAlchemy 1.2.7 and is used by SQL Server, + # which breaks assumptions in the alembic test suite + ctx_dialect.non_native_boolean_check_constraint = True + if not as_sql: + + def execute(stmt, *multiparam, **param): + if isinstance(stmt, str): + stmt = text(stmt) + assert stmt.supports_execution + sql = str(stmt.compile(dialect=ctx_dialect)) + + buf.write(sql) + + connection = mock.Mock(dialect=ctx_dialect, execute=execute) + else: + opts["output_buffer"] = buf + connection = None + context = ctx(ctx_dialect, connection, opts) + + alembic.op._proxy = Operations(context) + return context + + +class AlterColRoundTripFixture: + # since these tests are about syntax, use more recent SQLAlchemy as some of + # the type / server default compare logic might not work on older + # SQLAlchemy versions as seems to be the case for SQLAlchemy 1.1 on Oracle + + __requires__ = ("alter_column",) + + def setUp(self): + self.conn = config.db.connect() + self.ctx = MigrationContext.configure(self.conn) + self.op = Operations(self.ctx) + self.metadata = MetaData() + + def _compare_type(self, t1, t2): + c1 = Column("q", t1) + c2 = Column("q", t2) + assert not self.ctx.impl.compare_type( + c1, c2 + ), "Type objects %r and %r didn't compare as equivalent" % (t1, t2) + + def _compare_server_default(self, t1, s1, t2, s2): + c1 = Column("q", t1, server_default=s1) + c2 = Column("q", t2, server_default=s2) + assert not self.ctx.impl.compare_server_default( + c1, c2, s2, s1 + ), "server defaults %r and %r didn't compare as equivalent" % (s1, s2) + + def tearDown(self): + sqla_compat._safe_rollback_connection_transaction(self.conn) + with self.conn.begin(): + self.metadata.drop_all(self.conn) + self.conn.close() + + def _run_alter_col(self, from_, to_, compare=None): + column = Column( + from_.get("name", "colname"), + from_.get("type", String(10)), + nullable=from_.get("nullable", True), + server_default=from_.get("server_default", None), + # comment=from_.get("comment", None) + ) + t = Table("x", self.metadata, column) + + with sqla_compat._ensure_scope_for_ddl(self.conn): + t.create(self.conn) + insp = inspect(self.conn) + old_col = insp.get_columns("x")[0] + + # TODO: conditional comment support + self.op.alter_column( + "x", + column.name, + existing_type=column.type, + existing_server_default=( + column.server_default + if column.server_default is not None + else False + ), + existing_nullable=True if column.nullable else False, + # existing_comment=column.comment, + nullable=to_.get("nullable", None), + # modify_comment=False, + server_default=to_.get("server_default", False), + new_column_name=to_.get("name", None), + type_=to_.get("type", None), + ) + + insp = inspect(self.conn) + new_col = insp.get_columns("x")[0] + + if compare is None: + compare = to_ + + eq_( + new_col["name"], + compare["name"] if "name" in compare else column.name, + ) + self._compare_type( + new_col["type"], compare.get("type", old_col["type"]) + ) + eq_(new_col["nullable"], compare.get("nullable", column.nullable)) + self._compare_server_default( + new_col["type"], + new_col.get("default", None), + compare.get("type", old_col["type"]), + ( + compare["server_default"].text + if "server_default" in compare + else ( + column.server_default.arg.text + if column.server_default is not None + else None + ) + ), + ) diff --git a/venv/lib/python3.12/site-packages/alembic/testing/plugin/__init__.py b/venv/lib/python3.12/site-packages/alembic/testing/plugin/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.12/site-packages/alembic/testing/plugin/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/testing/plugin/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..160bb2e74dfe4dc10e4da4566a830a20672eaf65 GIT binary patch literal 198 zcmX@j%ge<81dVFW=2MHC?XaUYs|`w{2WR2e=HDnGhP)uIp(mILt|^xC>!?G#~8nR JeLRo@L%*@}Of3Ka literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/testing/plugin/bootstrap.py b/venv/lib/python3.12/site-packages/alembic/testing/plugin/bootstrap.py new file mode 100644 index 00000000..d4a2c552 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/plugin/bootstrap.py @@ -0,0 +1,4 @@ +""" +Bootstrapper for test framework plugins. + +""" diff --git a/venv/lib/python3.12/site-packages/alembic/testing/requirements.py b/venv/lib/python3.12/site-packages/alembic/testing/requirements.py new file mode 100644 index 00000000..1b217c93 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/requirements.py @@ -0,0 +1,189 @@ +from sqlalchemy.testing.requirements import Requirements + +from alembic import util +from ..testing import exclusions + + +class SuiteRequirements(Requirements): + @property + def schemas(self): + """Target database must support external schemas, and have one + named 'test_schema'.""" + + return exclusions.open() + + @property + def autocommit_isolation(self): + """target database should support 'AUTOCOMMIT' isolation level""" + + return exclusions.closed() + + @property + def materialized_views(self): + """needed for sqlalchemy compat""" + return exclusions.closed() + + @property + def unique_constraint_reflection(self): + def doesnt_have_check_uq_constraints(config): + from sqlalchemy import inspect + + insp = inspect(config.db) + try: + insp.get_unique_constraints("x") + except NotImplementedError: + return True + except TypeError: + return True + except Exception: + pass + return False + + return exclusions.skip_if(doesnt_have_check_uq_constraints) + + @property + def sequences(self): + """Target database must support SEQUENCEs.""" + + return exclusions.only_if( + [lambda config: config.db.dialect.supports_sequences], + "no sequence support", + ) + + @property + def foreign_key_match(self): + return exclusions.open() + + @property + def foreign_key_constraint_reflection(self): + return exclusions.open() + + @property + def check_constraints_w_enforcement(self): + """Target database must support check constraints + and also enforce them.""" + + return exclusions.open() + + @property + def reflects_pk_names(self): + return exclusions.closed() + + @property + def reflects_fk_options(self): + return exclusions.closed() + + @property + def sqlalchemy_1x(self): + return exclusions.skip_if( + lambda config: util.sqla_2, + "SQLAlchemy 1.x test", + ) + + @property + def sqlalchemy_2(self): + return exclusions.skip_if( + lambda config: not util.sqla_2, + "SQLAlchemy 2.x test", + ) + + @property + def asyncio(self): + def go(config): + try: + import greenlet # noqa: F401 + except ImportError: + return False + else: + return True + + return exclusions.only_if(go) + + @property + def comments(self): + return exclusions.only_if( + lambda config: config.db.dialect.supports_comments + ) + + @property + def alter_column(self): + return exclusions.open() + + @property + def computed_columns(self): + return exclusions.closed() + + @property + def computed_columns_warn_no_persisted(self): + def go(config): + return hasattr( + config.db.dialect, "supports_virtual_generated_columns" + ) + + return exclusions.only_if("postgresql<18") + exclusions.only_if(go) + + @property + def autoincrement_on_composite_pk(self): + return exclusions.closed() + + @property + def fk_ondelete_is_reflected(self): + return exclusions.closed() + + @property + def fk_onupdate_is_reflected(self): + return exclusions.closed() + + @property + def fk_onupdate(self): + return exclusions.open() + + @property + def fk_ondelete_restrict(self): + return exclusions.open() + + @property + def fk_onupdate_restrict(self): + return exclusions.open() + + @property + def fk_ondelete_noaction(self): + return exclusions.open() + + @property + def fk_initially(self): + return exclusions.closed() + + @property + def fk_deferrable(self): + return exclusions.closed() + + @property + def fk_deferrable_is_reflected(self): + return exclusions.closed() + + @property + def fk_names(self): + return self.foreign_key_name_reflection + + @property + def foreign_key_name_reflection(self): + return exclusions.open() + + @property + def integer_subtype_comparisons(self): + return exclusions.open() + + @property + def no_name_normalize(self): + return exclusions.skip_if( + lambda config: config.db.dialect.requires_name_normalize + ) + + @property + def identity_columns(self): + return exclusions.closed() + + @property + def identity_columns_alter(self): + return exclusions.closed() diff --git a/venv/lib/python3.12/site-packages/alembic/testing/schemacompare.py b/venv/lib/python3.12/site-packages/alembic/testing/schemacompare.py new file mode 100644 index 00000000..204cc4dd --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/schemacompare.py @@ -0,0 +1,169 @@ +from itertools import zip_longest + +from sqlalchemy import schema +from sqlalchemy.sql.elements import ClauseList + + +class CompareTable: + def __init__(self, table): + self.table = table + + def __eq__(self, other): + if self.table.name != other.name or self.table.schema != other.schema: + return False + + for c1, c2 in zip_longest(self.table.c, other.c): + if (c1 is None and c2 is not None) or ( + c2 is None and c1 is not None + ): + return False + if CompareColumn(c1) != c2: + return False + + return True + + # TODO: compare constraints, indexes + + def __ne__(self, other): + return not self.__eq__(other) + + +class CompareColumn: + def __init__(self, column): + self.column = column + + def __eq__(self, other): + return ( + self.column.name == other.name + and self.column.nullable == other.nullable + ) + # TODO: datatypes etc + + def __ne__(self, other): + return not self.__eq__(other) + + +class CompareIndex: + def __init__(self, index, name_only=False): + self.index = index + self.name_only = name_only + + def __eq__(self, other): + if self.name_only: + return self.index.name == other.name + else: + return ( + str(schema.CreateIndex(self.index)) + == str(schema.CreateIndex(other)) + and self.index.dialect_kwargs == other.dialect_kwargs + ) + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + expr = ClauseList(*self.index.expressions) + try: + expr_str = expr.compile().string + except Exception: + expr_str = str(expr) + return f"" + + +class CompareCheckConstraint: + def __init__(self, constraint): + self.constraint = constraint + + def __eq__(self, other): + return ( + isinstance(other, schema.CheckConstraint) + and self.constraint.name == other.name + and (str(self.constraint.sqltext) == str(other.sqltext)) + and (other.table.name == self.constraint.table.name) + and other.table.schema == self.constraint.table.schema + ) + + def __ne__(self, other): + return not self.__eq__(other) + + +class CompareForeignKey: + def __init__(self, constraint): + self.constraint = constraint + + def __eq__(self, other): + r1 = ( + isinstance(other, schema.ForeignKeyConstraint) + and self.constraint.name == other.name + and (other.table.name == self.constraint.table.name) + and other.table.schema == self.constraint.table.schema + ) + if not r1: + return False + for c1, c2 in zip_longest(self.constraint.columns, other.columns): + if (c1 is None and c2 is not None) or ( + c2 is None and c1 is not None + ): + return False + if CompareColumn(c1) != c2: + return False + return True + + def __ne__(self, other): + return not self.__eq__(other) + + +class ComparePrimaryKey: + def __init__(self, constraint): + self.constraint = constraint + + def __eq__(self, other): + r1 = ( + isinstance(other, schema.PrimaryKeyConstraint) + and self.constraint.name == other.name + and (other.table.name == self.constraint.table.name) + and other.table.schema == self.constraint.table.schema + ) + if not r1: + return False + + for c1, c2 in zip_longest(self.constraint.columns, other.columns): + if (c1 is None and c2 is not None) or ( + c2 is None and c1 is not None + ): + return False + if CompareColumn(c1) != c2: + return False + + return True + + def __ne__(self, other): + return not self.__eq__(other) + + +class CompareUniqueConstraint: + def __init__(self, constraint): + self.constraint = constraint + + def __eq__(self, other): + r1 = ( + isinstance(other, schema.UniqueConstraint) + and self.constraint.name == other.name + and (other.table.name == self.constraint.table.name) + and other.table.schema == self.constraint.table.schema + ) + if not r1: + return False + + for c1, c2 in zip_longest(self.constraint.columns, other.columns): + if (c1 is None and c2 is not None) or ( + c2 is None and c1 is not None + ): + return False + if CompareColumn(c1) != c2: + return False + + return True + + def __ne__(self, other): + return not self.__eq__(other) diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/__init__.py b/venv/lib/python3.12/site-packages/alembic/testing/suite/__init__.py new file mode 100644 index 00000000..3da498d2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/suite/__init__.py @@ -0,0 +1,7 @@ +from .test_autogen_comments import * # noqa +from .test_autogen_computed import * # noqa +from .test_autogen_diffs import * # noqa +from .test_autogen_fks import * # noqa +from .test_autogen_identity import * # noqa +from .test_environment import * # noqa +from .test_op import * # noqa diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..18b42f8eb9dd039514e6a24f22d5be3d7bb95178 GIT binary patch literal 429 zcmZXQ!Ait15QdXhT@hDASPx!SL4*q3pkBoX@Zc+iZj*M0wn=HyU3&B>d5PK zli$-;Puo51IBLAW|FD}-fDOKU2nJ@PxzaSMwM@C7u`CKMwE8cXwdTwmZFDn85@ilI zv?Tj$24*LMZn{P;=1@uD)|f$SmE|*9yVrg(vXE7#$|&ZF#FFuF0eTiX>cqLVlN_d` zZ1hZuo5|HRQJ{HfJz11mTa@!+3Ndk`L8L_0woE7m0h-cD*_hqiLb5vNch!Mi>V89= Z*z42B7%$Ozd(M_<+`{+`9asCd{0+->g(Uz0 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/_autogen_fixtures.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/_autogen_fixtures.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1cc7a2dbb1a7ef558d0653dcc900c163253c0df6 GIT binary patch literal 17402 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_diffs.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_diffs.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51f4296563b4a7ab663cf06f4299b7514592dbe1 GIT binary patch literal 12370 zcmd5?TWl2P6`sq^?(FXDt}hF=NdksA4hsbb3%{hyid|IRu8KYtAbd=y+CHvcX8mqv>EJ0{G_Z75Iv49ay%q(nMJrRgY5 zqt1ZNL>Z#9ptDhy=q}J*Q5VrU(77l_ba%>~_C!6Pdqh6Pr@c`x=w8v6@}>PzKhgau ze_DtNL>E90L<2+*fF6tni5>(!6b;c7Lmj2W&=pFolDtyrrc0k~w2I_cLw-$Be)Vu| zB>V^~MQF`;Fxh`Bqe}gf3@P`aY-%W-0nJm$QYBf35x&@-2n~Oo3FEzx z2aWFz@$qB<#C1wV7?I0-{+AesJXXavJ_blp64wBNcu}nNfC>HfvpSseZF;)HNWOdf``OOk~p3FcsmtH9D@*7bj`W zKPV^Dad|j)RvMmUG*2R%PD>e8u7k!AkBoOFdr;ttknyIq>R4P+Bw3Bc@ma?b*_4c6 z*MjM+nCu&d4AR9Ur@0g<)u%CoXEi3h3;tU*uhmAuz8dlRcc$e+Rz9l?#uJj#lE{kE zjtfck%nsCy%=1#_d`l|X+cG$;p2=o*@7&eeq9j#m2edgG?}ujbl$7pGCR&UI-=Yjb zF-gVZIz7Z-a(&9q!C|cdE5YnjG3jEG@PiWTOQuqiI1*WepPePE*246aUJ#e51veF3 z|Lf=&^B}Mef4<@aV~K06pPs*w_+rm(CSSKN&%ge_o;h{s&RhAqt~`J2VYq%Kye${r zHqkmEPqp3)w~u)r@cyx5GeT2NXc|x4=eIq9QK%va4L0#8r39ESUn*MaDaFdCm3N~MtabyTu)GnO`>K$uR_BqPE( zp;g`hH6kwg1+2Xh#U>PuAQXf&0#M!piq=%}RmZ^CWU?yGVx+BNfY@5$kjncYmch!@ zJ9hysC-VGBK+CKUniYZ%gqmx+Kl4nybi4NMmb`Fk!Q=A!<^{?dnBh0(_>DK@@z!y9 z>c}*|@jl-nH^Cr9NU_N7pHT80LJH@MvT`^v(NqE5S%#S4$!)`v#%5EZTn`UJV>8kP zXNn`B(Mhe!qzIGNG1FMtWtgTp zCp1rR_xYVm@nbPfBXNWr4C7N9%V%U7M38%BOyh(gn1!Rstki)kGOTvy(&$%#yMkaT z&x4f|rM~!3O1G=>c6gl;R(=J33N#k+mgFy)hniZQ1;ye%H8w8yYc5r~s5*jkds$*D z?rUV(Bit;s@hNAop}59 zOLyOb(m-&*;|98`qSiGyTFFD#8fL0nbJeXg)$O_J_L=H^x$1rQ`PT`KyAzg=26IB4 zn9MK%G7ydB_d$tqje=(<(zh53DBLJFVD}x3dLoO~=aPHW?O5s}rJhmO<@$oRH|iO% z{6up+iW+4FssN$P`ep083bmm}1jeFk!14tP1!}<5ZyL%?UkTz!X9u4 zeX;~mR3?@kR37~YE*Wn>;?a0;kw|<63_7UjQ9LWf;TV=~2>-L!#cZ8BA7!IXMZ2xA+Qt*D!WaCAsmr(V{=EbO8;-YqA z>q{UmQ?tREkJDGvHx7<76Y5y{Ua)P9ovqt&{lX^~Zpfc}Fec1~8)w2zxp33?o4N4I zW8R1U+G+oD4*^n?X#i!nNV{s)I1Z~p9}5(WB(tL z`B2w1-!&cTg0gA8p5QV`i9kYBHNqWPGcdAWP?llCp$uyQ1-i*-+#cp-|q6Vh4&g6zB|1Qe+nT z)2qQ;6d0N+{{V4?nyp!X`NTu6;qtpPTtkj)xbgD%YxlVC&2s)3u0F@r&v2V_+~#i> zp4dPN>22BTEeFZw_V zq90^H3J##-x#${bT$BMxQ78ns(dyvCHz0U;Mb4>Z3D*x9 znFB*_%;*v;dPZ5t{TCitw@iscTzsC^`43`Zugyv9lwXFfdgOMHS^(om>kSxm=xD0< zLUoxi7jyQ)q!Duz*o~M+7u2!H%ZfRSI2!i8NVIB=24P1=8fjmG0W&Nu+b#w}DGG?o zlo6OZ#ivv6IK`(u`MQogzweC%)|eyniD8`M{T%&ofhR=9>4+GwCBtt@9`2;j2FeSJo$w_BF5@#$EgoaQ^X**1ye{tAQxJS~5a#J)Xu*5!qxB=TMG*}X+5^-a0%9LWpE z&A2#JyTrERcFLiTk6t}`?ZDOJA9r8vzOgGOyf7m)=7h%aO*vuL)T{UTy-N{map%r8 zK&%r37HzQ7$F-g&&QnE1)+ujb=za+1k zyFevvI=GsqX&%|Xa!R!|{|(~#cO~&`>}t1tRyQH$h27>3_fTGVb-~NQju7l_7j}fM z71#4ky_6STBlYHk)LK~k_x=wkH{bVaBYbJUWzlW(Ye|&l5Hab#spA==9)CG_x`ilf z(u}g+19aHHtkW}rGlt-kykmS-aAC`OuWieD5IR&X2SE2^JD(Qv#r;eo_Hh9?bGbA2 z?+J|+Apf55Vj0}eK4VyRehpx653Otz?6%Q6`}Fi)viAbPJN9073H)yezW98~Ab6Q9 zicXpMD@gJ3k5N?UH}9?t^+k@y$dRW7eNi`mLd^fZBFx_b_U6>cG z3wz37|7rbfjPQ|Eo~#A<3vrqa`gYHTCBBX6i5tJG=G&C${W}1DNAE@lzaA+P_eNg^ z6@q`)a^T1P(JX#3V!1enRu26oo#ZM9zRl4wwlgYlb&RbIthu_*jul|OzXq(oJn#!O zR@>pRJN<>e%YuH*ogVq802BFVC>-`XI#vSRVqatA6Jwl~4UUCeZ4`UyrLQH#C z5T=rl({bxsFL4S}{Ul5+_F@EM%Wtt4qq&b|GTFli;agiFJ*=FAukJGa$&6H~W3zW9 z3#7;|89DK6>!YZ%f8p}@k}Qv}^w0bXrzwT)Y7(s4To=zo&z*2y=$!Xa8@7B~>x1Za z{nF^x)&Tdj$v)1rkiNB#(;caie*klq-#~Ey#X%5w%xs(*E1==oxcnB19u%ihL{a=4 z#rr6FQHUr4C=P?rUWnTd#$wqFIqN4@Ha2)xK7$oIQ8c00f}+b5KLJHS_trRWo~_+| z`Q&3bL*Dl|T*Y-fZsNJUkHbL_jWv+mS_1_j_M*^}^IIvmU>-=f=eYK7m^yARp7rh_ znm#HH2oFK?0q)|d#2G0)ER!(Og)_l#LJESX~Eq^(+$(>w@z$bpio^n&75cGz0>vECc3_*P$jqi EU*)v?9{>OV literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_fks.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/testing/suite/__pycache__/test_autogen_fks.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6cb5958dc9e2e8b89eb5a7eacb9eecc2c34b82c5 GIT binary patch literal 34741 zcmeHQdvF`adA|d2cpgB21mAi<6h)bOTGY$3WZAMrNwy`Xk(e}L8Y2+yC_w@N`T$ub zN;OLAPUYHZs5Dd26U~H<-I|Qpgq^liHJ!GaPNz+qv;j?}Mz8indGz5X(}6@f)42Vk z-?#S$4lh!&l!;HH#opb%Zui^Ye*4>Rm*4Vw-4tA(>H1#mD;Fs0hj^o0cG)xk7I-dG z9L3RbYKR`7Y4lsbZyB%w*fp4S_m`^obLcpfx74~#T+H@$e#a`kVx!5S6R|`j9fR~SiD)u07&|#di&p-bFg&mgC8KBH*NL$nkEHl0mQuMa zvSK*TLDrb@Hy$K_v<8_jHrySA3k~ zdFrZsGr)3Ai0v&AdzlhP`{i7^*may6V*5D{xPcM2+bg!q^}-90G_O^43O}_UzIoN4 zRQ=&I{74uoMs9$xzz@dxC{|e%YgoCw;K6F0pMYw)Or_O#$_JdMO`ciO7>8UOJxZq; z_0E!ZsDA5JIW^8U&0LjZD8DqNOT%tR!;yBXY3O3=a&hW%ajChur#(vQy0EM+tX3lL zw9h1LomtqcvcRN0YN_f@(wzS!t-QG^f3xA6=uwNV6pO7FeH!;^ypm0NjpRxj-xYa`l{@!`h`e3%Cu_L9K2_sP4u-(Va>T@!>Q=g=kA7 zDj*K`L+AGl3$dYyFoKdV+RmnULA1m;(Uyn|@$d9Npo0*|nK&DdlgO6H7Lk4q9{C{T zv%C=I_`%58cv@sI)t>k2w2U_469L6QbP4=W@>xDSct)&~;sHjHzQ{pRe&}#q3_DpQ z9>#p9oC4O4fC4RG4-imP1ne&Y;;!fn4@v+h)`cS}NHGmbhq>6`;FL|YrTF-uXc^i9 z|9eCRpQch6Ed*e>`t~5__H`uNEu*?m12i|YH(llZDQrpaah>Q0-uN<7{`O=ef?Ao{3 z(8St_*6hZ;*$t1*v5zg(IU(gBN%`*Pyx?`X-1&Mc(0JK<$vb{xqGQg#<|1>`$-e1a zHtSrLV*_KWUO1d%oADdH!HZ8|U3eQV9?mV_l&_;)%ipD-tn*NLlsRPQ?i6Gz`U=Ha zr)?&%$fR9Z3cNVG0=G(+R`dr0_&E%KZb?_l!KurkGN7B3hARc=CSlc*tH7ljiV9)7 zQpp!nH+oRNPwS?d!S0U0y+ek328L6$Jpk$(`UF(11k{lF1sofMW^`K6X-8){IxE0A zD6B;9Ds(!~S&hybbh^+Xz-^Ck9AH^@5g-#RCa8?aDr`V!BRZSFQPEb|3U{K1fS{C8 zW^F|TlmhO@baq#QKNEPHW4jhy48WD|4gev6qlJ0{@ZIe^O~E%GtOSW;rzY%^&6BQ~ zJ=eRgx4dh$xZJ{as6+z_vhV;0)cNZlprDG$B~egCF$Mdo>EFMgsJN)Qss;aa{^P$%hoiDWo>Dv~(KSE28zVH1#KYVExOyhC$r zrwlxk;p~P-=GeVvO5feWzEas2!`_dao{PO>J)iRxgPTtQx5EH&@5iiMH&eBn&4CRw zcpN9;Dx>f@E6(6ux?J3fO38VqSrhfss|%~m;C<6|CSm<%VU@0?`^rGsaN11Y&jn6e zU{>02Qk}zImDvfy_ZO;-6<82mNfuk*dL{A~q`sF3vOx)CMO%yuZy}1Lq*#&%J(5z1 z>ShJaQgkQNr+_cORCInLV}LIW6aqZxsl*0I%4aD=ZYRp8in2iN4Gkx8IuB}XW@klZ z)I8!id~fPZC9KxZ&GqwdGFM-sin`~urb)}IZFB4fOj&;y$_18>k7oTFa)FL)=k~0B z$2?>8`W6B#=)}tmn(X~o!2Gg8HMb1jt1RsQ-6>_Z0{$q-X~3|bQx=vBOxG*lqp^i$ zl-I@8jUI3BQzllY)Uy&p4GgMD-Bg345Jpv3akQ5%;Aw+|r)@OwwAcc2tr8a4JZ2M# z*$gqUbPB_(iwTNwi>wH%bA5U4PiFj8fU`F37i~vJ4n8$S6Eue!nGv# z4^XgFhCuU=?kR10eeec*)J%ugqpn2RO8mmoDcjD8rtG@i+3r2pcido~BzYGr(A7|> zfo4LbE+CZ(DY(1^xAi}zIz1CL_p z$I#h_&g0;SEUc_aIy9`z35W1GQp4qkft4!dtJk-~xYR~g_%gdIf7z!Tr6Q1>_o6!)SvO0WX01ib6d*SG2+=fqd)uo-F%-g!5w`&9cjKftA`i+Xqk& zVu;uMO~RN1(+7hAcSndX4T$d&)VF9k0Mxh2%KjaPdf_P$WF5j2mA@OGmD2WG2+Ygo z4SKN7Y^#XxCb9Nvz+R>9e%}D}q3s zRmR+e1$UQY!LQ0zeFl!G8lKw&s|BoL7E63R;Mb=Z!dIEaEV+WquZ-$1GJFcZ~um^OQEkCJbuOa6((j71_tx(l1WZ*wN+6Raz&qYBIE=Eob3#2~k0VY#h7gWuc zYE(xN0aY{nig;P44}BN$(mx$g5KdPnbG)qAh1KfFFCirtG^?X(u(FK7L}QI8sL=qz zF9F^OA4camI{oN;1f7qf^ULUb44wVxm=MEF5{IBzMkqj)>8Xfp`XxAmhEoV`Tki$9 zfrUhg)itjhP_N})pk8+Mw(O2W*^a~WRtwDTy)J;frb5UwuA=pI*MkW)A!5Q|(H#yC zCAqV49{g-L{LI-%9F4Hy&Jzxw0Gnq%!AY+K{+Y8eflr0Q!ZvK?htS!L&H;3AJx*lh zKtn+Mr^I&sz6z46wxdFL0wW#4NC9YL@-~qah9dD;h8Ozq<;T(a1UOR^VH$`%q0oR& z(ecCo)H!gTr*id87oN&B?7DE|wrwNhzWp@a4em$j2bkvD{q%!ObDpCqJNqWnGRw5Q z$!wctw%xMqX58~u%Dz31zS|jk8{^K$^}*K7GV5+xwoAd*=h1gNL2rTkL3%yYER_jM z5{3WjC0lw>^WT6+#am@86wh#0&ZcOVy!|vXU84VqL^OVu;}0j3XVkqiPS8uRvB)N(W&J9AWQ{ z6(Cb*&@IwwVy`XIBcgTi%ofu3(AOiJg0!OY{F5Y$NQCe-e;6EnFC3-PYHwBYdyW?T zP?Io+!uo(a7u;Fl7dBKvEm`m>OC6`CtbJmW9@n+~a>5Yg1Cu6q4Ay^B=}02VlMOpl zjOdV%A}lQ|GwG3GK8*W<$o@K>6WvH3MFiNSluY3#;AWkWWIyhUNhlPSlE^$>h)RZ? zGf*gYVRgv8Z>-;3P-LB)Q^HI#?Re&V9#9ek=vBDu`AcIJiBVw zY|F0OR=B&_vg$Sizj-^=x_p9p*_(GUE%kYd3Dgtx$DL5}mkq|^XFUB9O;6Vp*RNq*{@iYHUk-Pj99y!eWd0^ zCYIp%=lX?bAck-ToGGj5NkvcbLlFYmqMK|1ll<-S3moucgE5|y_J@&|2L&9GrmW;+ zglEAove-I_v;_Sr{64jTSfz4tSjarKl;*({SZtifAcG53uAzB+`xhR`H7}cRywSWi zw{rDc!RB!$yK?7^;6vcewRGNGzd5&VV{Z4}TMk=m+XCZgY|8toWvgzsuT)xyCAx*u zJ}CH-IRoJ~5#gtNg&WRL1jRsPco>x*oq%JH66vV4V}e*##4))I=#c#nXv85}<9wpq zC2U5&>l6&0VU%~_K@7D8ozwWnlHMYW;2mzW6P^brg@u7?l`cv8Ta6s6PPjAcs;}vP zMVxZWfPdKT=p&3u$))Drl{bS6FPma^y zb+!?SH0;R07AK0*PeCsA5Ur?UHE$&@t0);LLdLM)Yzf1!K^f4Sd{t5g^a|gdQrm2x zYGJJwTspPQTop4>!a-58E?I3`TxrbJwpvPKstcu;K3)gz#(M;XnSoTyPIO3tXA z^Ag(C2ZNDb$#GRsah|{$K-4KYWbl$emf$Pu71GL70(|B=&Xe#)-+@Z# zI01u>0|!BjT3OU~3bY(y7_$`6NrO|MlMvj_JYtT&NW~7pSq&VV)lfzUIfBC+F5-a> z;%^(@d86~8IsY!uK>{7QU~6{y_SxWe&_Mznpo5GZ1uZ1d0l?YVI^KKfC}<%;A7~+7 zX&Z!)!E%y;Z72Fr{Dt2Hr`tgWq2iIJM;m!ksGk^yoS;fU>pmm1rmC-9hhKH56gg7} z;gbEEoA4%O0UUPol^+7`gJJ5n)9&)Y(4y^Ko_TuAJLf-8ynh)s>scqF6CauEojEYG z<2sYK!p`Ld3dTklolQmzY+B{Qo%>|O_!=O+Uf|Ucb)AyYo#%ajGBO~`G zMEXR5&L`3tLTYGgUm1SuF({6HC@i7-!SP-?erpjL-%fKKNZRdNM%Q~8wIZDP7w}CM z1K$L9H(QnwmdRm5mdPfwOp<GV# zP-!LM*WjVV5ec6KuYnFCOh#sV%{7H2&8t{Xk3ot^{j`i9dZS@=u6^ZAi3O^e`VoEu zQZn1V@($`#d$HqAStLVdiMW*fIK_bHYcVqVy8~wIkZ=*-qaq``fKCTGUqpw@ST3XQ zchNC&L&9(2Gi=IkR?^L1#0M%h1a$9~i0~3T17FVxVGQ3ISt60d(YhJfvv?7zYGXsi%2E`@JpUWkO0Ogh-I?2(Q~ z^4JNF1V?t1<&h3e9h%&AZTk&&Uv)fEL3rSi%*w4yLU4jn?wf$^BAnQgzA6`JxE#C` zyxerDX}tc+!Iy(yZh9GwS0=W;zWtT$ukU+Tqej+t_9OgDV zuN}YE`%RqPcvr#f#_NL#tT$i?oAbBi{0+H4a4b6J&iR`aUt=4vXc)^0Utdpo{a>_> z_5O}~eBXE36$(-J7F+i(8@9~(d!~9Po*7#;w(nBsI6c1VW!slsFT36dbWHZn`g=tG zk#}(eEEK}G>SsUj_oQ%Q@$L2PN9&z^&OVLEi-$oHAR*!uC2Ii@?uc$9@`X@&VInrX+C z==JEV%iHk&_JCzG(|+p&tVh?EiWjFDpBC?y4ezD$U@5S~>LqI_)cj4LwhI@f;haPp zU-e=pp7p5AHQ|KE>f_mT@m%DTNOy_YCYmfRbE~|XDAoMx zO2i8lM1otxNtA&S@j6Z7!I_n6ehnq!!4h2Ig^ZpWudzftv+sk2QZ-&viFl#H+~E7X zx_Hes#PjLmwUmfwGJ`4+(o8~N0ay7V<#(X9MEq?gU*^~1546`14^Dqo^IKLT-owTD zl?Z88GMI-bIVisaD@w#)T263ct&)TCJFv1uP9`NS5z?egT+k$^P>J|U%c;>Mr&V_) zrzVq}I!feZRwvC`{DIXa;vFuQB(6mlZ_P++cbnL1u&aCOL&N0U-+nlbvfnU=z)=pz zhWTTl2I{9!O6#B3ElsP>^yW5InSbbbnZ=y_R|OZP%b~V&5m6l=mohunR*J}$d+$kl z&=Q0B@@w>O>rUI#u~J~%qszSnSEdWGm>Q@phr3e)7Gn(nSK(FaPWuHD8`XT_IMwZw zO-^wHlJ}(Il2Yjt2}LWI%nR5#VDSRmMt2M>uF6Yi!IF8`1`NbV%k0i%oC_z1!RFAJ z;Lj1CHmNFDy%GH|RVP7|b7Tt+>e6?YO@(2udJP8J8)5b z%|!F$qp-VwmOXs!IdJaY$%Ga1gu_|=!Ox$6;r#fqiTb&^&aAVucvV|nqxcFm5t_W5 zhWaYJVqgfvSz;<%be@KAik_+q3`~`ktT)=w_yu?~IME$k(6pxA+Obk4<9bM3))3bM zMqn!E27Bs!eWDW%A?D+Jnim~#-b6IQ@jB)J**xL*&_TU}EK~}pivdmWReZ1x9kS|% z>u<0<1fRmoNqoLh@}o5fG4)7+0Kw#4nz9GfwKgUH%=(hhEg3rD`l1r}T}W#l*_aE| zO@HuB|C(9j?I`~lv64V|xp zV`yb*MOEJNsI*;XQ-y{u2=9ZwQPkF%nO)hgN3IFgxAls}v~_E_wnkVcIs~dFrFy&6 z*5F$8Jzg6{M`^?9j<#;*y+-UCA?S^KD8(n>(5~3CvGj=CPSFUo69>I@eDMn@Fr*-;SVv> zqO)(Ss`2{^kw`uLQFWCq{3V9d(CDx5*1V1j&`7T4s?kUae+X;(PfTn2F5!XjrR6Gq*zLPQ{|9LPtlAfA~mV(}q@Rwww=~DG59pq93g79YwqJOdlA#Fsn>n=#RU#?HtEI{$ojt5Z zEh$v(S$55&d*-R^+Q;stQ2nDSRR6RHp;AAyhHfVdl@j&WQK+(}Lgj&{@RpiK$*)b1 zD>?hJk=Yl_+lZy8lh{4D(>BO}T&Gp`MR_8hrGo<*T*M**%T#c|oKUerHKYtyW_8+} zaKzp1m%1@ZrhwhJ3f-kon%Y^VzE5JV!p zfi4*y6p}+4QtYXKrDbEk1htgA~@R6=q0tk-?Xj-BZ z{>U0H2s~GXK)~4aJ(Pih5sQ|dnu~{YMmo}pYM%Ia-AI<1?G>cMJlc;@IEs16-PhdN zb^A?C;>~!;rnW_JRZXFCT$MWRyau&Y4liQ#qIpR`iyCplz-z;(qUWYzcMNIKo7k*K z9ZQyJCX}@3JkWcv_t>%C1O18$gE2+7)R_1O{0ihk_yMM8E)^>XXviF-iC{cbK`=_j zw1M`Q`V3^OW6&mkb>_)z*Q3|#tCx*XuDN{?SkZ)rMe?7CD^^QXp!j_#Un%jBXt^o} z5;Fyu0@cBz4Fx5YXO;{?UW4)sOC|-w2rOQ>Vnahb7XxmtUjcysz?Lm5E<{Fz?_)Ss zwJjkF8;C3zySkcXcsPDGB?AV^Sms~_$*3}&6(}bciJa)E7uGj^=2~#puW$o-uu&NZtBJ7Ry!4XE-L1cyP(&e*&eAF_eJ*(iDh^VKLdDIS zR@~`Xb{AG0{@%HYBdhT+N0NW55L2@BYmMa-!> z!hZ!`m+FQ0&%)aj7|>v!?ymx;yXEe0)cw0@2^7q=JngtituiwktLXSz@N?M1i*@}y z*Miw~d*}R*R%e4CVQ!5!2rxQVqs*0S>icKKPxv1cSo3C**0bahQLFk)rS+_$7-MFQ zH9FIu?SgUgeQvD2?k-dFdtx<%re(zPUTi^QE!j9H97_RTDjymo%wU*5M(5ALkps%y zh9Tf}4D;6*#zPX5!U%tZPlEd7m31xQo7yWmLG{(Qv{wbI55jlgmGA~SIdnvH-Uf&K z6$Nz;CHxSd|0h28Lgrd?3O~UY|BcQqbmr0dDLOv`M{F|$Hde*|VkizBL_BFf!mH>* z=i8v;rPhJ&A@4`HZR=##|l z5;45*oMk$1Ss+#1)UYOxK12E&XPJ$+EOzPrraby!zrA*>-%{U8`OE1;{^sc~mnsSJ z2VZ_~QU0ahV`nS)6$fH8i(jWy&L{0mTp^79Y(?>BO!oJ13H7~V6FvyS#<7^4(Z%0;GaK1Ys%Eq z;N-0|O}|Yof16tWHnrkyY9;Y6`!{OK+tg~}Z+n|+hvy6S0}i?wc9hMWTAQT+CQc(m*R|MO8l{A zMs}1^fD8CRLfx7|1BgbA4YM#dHmpTmpupxa4@F=0MT#g`o|SjAs0*y#Y|DM=Q_s2c zOKK!L=nr~rojLda&b{Y+=iD>=uW&d>LHf~=|4o0{L{Y!RjFo&QvvmuYMM|P1Izz>2 zcrqC(%fuNP%UIyqI7@gAcrMNno(GLzGD0>G4*(yKf|+186b}(T zlnG_UxJY;r_;5T-_%QI1c!cl~;OpXbG{sP7DXH!XB}L_cTz7{vRvM4a(6RbW%nZk9 z-S<{LGo8%==l@PllgDKRSm8ZcOAaNqB=G!5ax^0Y6H=!#$wVrjy^z#4Y3L4R4TIBK zeq7F-Nq?kGEAoh}YMZQ4<<}sS&W%GWUn-v)OC!h1QwezZ(`rIfrg2mO`@ES{S zN@0`P`Tlv?;T41lD(mtT| zGyYhs-eR7jtRyx0JCdB!(%KB33!G0hZRX+RQBc&8l(~guTs$aT^p*x2v`A@ooi*Jo z?NP>P=8ICGE(KcS%{7A3XqG}z6M|LhJgsA}Hj1XIMq3o! zQ1edP87dYY*12g_R&+kEoredLmNrqIHt`5=GdizoNk!8G>0C;Yvmo0ew*kv8DCulc znMq8_GYYD<9!yG-DdL-0pr9J%JGL77xgWOYVO>fDI@k)|M=>Vl;$x{w$%Fi=62 z(79YPE9-)#JW2!$gK0ILgXQE>vVt0{><1F#bxxHtV>*-Vhra=xm(pWns!pd>oVx?l zAEx>)<&{bGLNX<*eW|=8_gqSA=X;Pd3opvKi+!2&Xy1hy?R-9WytjX#Pfcrb5A>N# zjzhO(M$V3=Q+*~b_o>rRO>&8(f#L~kxxE)=^cHNFz+JIQ&F%TPe!!gFKA(Hlp~%}ZUsIC}f&YU@BnI1a_@V#B%^*$^9wCvUv@>DlXN zSH(k+TaPwwM4LCF4U3_LP`UHgR3+NK-q!Inz=tDG1HnM>Q43YScQL#WE{&GER-=dK z_=iGh?(Jgd{5!?>=g*beD#DSCs*st^RpaEwbZF?KG z7R~J59M%8=IeoZ^sB3zYm25->?AT+~YkMuOz)gVDR+}{2x_IrCwPF{7xmV#a-b737 z47lUgIIyy3_4hF8Zd3%M3l1x&ND9ub97KYrtl%zF38OM=L<8`C%fp>9i@ZxtIPlwTdn56VoM^T*Ody!l zBw}~WTY@D=))P>2_r76CkbG&nz}}>PN>@oOS75wX<0Z8qv9?$3EU(I%@tW;BdtE5K zEQ%DE1aMTk9Y0w^|Bj>rZ|R!k_U+r9ddcwW-BfhziRNlsqd}43kr<;#@=7w5krVk` zA~&7M=%STZB)HQvy5Fkc^H@@AN&qqpwAXz`M+FU}VNoL4JouzD2}vGHPG_{+wqJ~> zq3^dr{(MqRNNMmhQ(9v3Qc@XL9naa*?4U2CGt#JIdUQ4;=ZHDluGJhF@l{+F+MaES zZCIY_1(-^%e*6r}qlPvVrXU0;QY|n2Vb})t?W5I zH{>|AjW;j-iuupbFGF8;R$d-jZGF2UoY{y(uT9QRu0&oe)5}0cfSY4CY)2US++|l= zPgI0gYR%L}sQ%jP^RHh!J%76NPhW)&Z-{$7d8@?#?Ar%H)`e_3cQKin zhAT}r`H{i#sTq*BP2z(fN!Njc5Mv#eL3syDc~I#T0+f{yGzefx!~rhQN|OUx6E-Ya z%`rrf23HeRevHlmEX}e7=DGSXu0EV|(YHeryhoYO1#wJ)cY%s4JTT4_*ngw{!}n9l zRnKYmlD&ckmNHvTH-MdEbeV$BHB$9V+@C(v4#5b%UGgekbL93(`bd<)*f1$lTmAlPQe`)SNN9yNwMHqmWJxup?L3$GVjzX7rcZjbVT zBe#yT1!j?h1|&`3AT`NatF@;jRAd`cr?Dla;Ld>=`5vrf77Z|(dy*Ve={8&i*S=kC z=cpN1NUi|7bJ0Fa8tP)qF#JCg^Sj*s4j7&ITRIQ%fvjr8GyLNa5!N&%#_7!DCG@T| zou1U`iz>b>x>tsC#i!28smYJu*xiS!@w|H9$mCPWjC#74^!_=FsGb9InYv$B57+%0 z!SeAX<`<#cq1BcXmFO$$Z3jMm=lVPU`tHI|aj-bG(b#;$UuMhi|Ge&2{aR!9W0q<^ z`qW3&x0Kk@>~hOXYtbG8ezBnI-cudeG0amZzPs&LDsMtI79!52?i-cI@`|kU$uSM0 zOhK?k39~4GJSncd9kKb0q_a8sk}`ttglC6aWhP_C8i_kvvm+n(e4`D_;^;4cS9O_s z+(8Yz_Q~kt;37VMTs19 z!rM*mAn|axU)V7uCkhU+Z*+`F^4e?b#2t8dt(5bO673ZG_KFDE@jC+FVRuA` z*fca^oBt0IbDJ?7G4#P|Egf;$(8wv@1+rU&hyY?2wyZ`)m%ShnBAg}0*IdJm2oVn< zWLff#5>-W)HMo(JPfXA9)xCTDJ1jwkuc3f}+hWXBAhjxXdf?M`FuU3HdG}}CcL%@Z zDqU}{if24J{Ut%I2I358#Ar5#pIW-e zqn!(dP{e9db^O@_g?!JcS>Fg5Eq!#M-8Y`+Zw&3WMa*3Y!y2ygUj*$u&eYmn2ki`Z zyAjUb6{r3_W`BUh%kd^~gh-?yCdI^O1gt%rGzX4$03p8-S>6Ui9iGAJF(se1u;}&O z9kJSowW}*$6V%5*o&`wH0W{mu$s5J~ih$J6T%s3_&G`*LI(hTW&(D2!ZZ-Pyn$T}J z+YRtdG=ZN%6A3+-NI(o}8p2ZvT}&jVrjr@726Bo#HJw&u73xBX#3+2Plyedk_n=4& zAUTO-2no8-x(Gi)8BOPsS~{OobxumBG=+qcL~|(o%&Y26c3{f<=|Uoz0pDHu5C{4f zBq=21YKS3t;*Trn8xVq@n$)j>T%p$Y9J~D8^@gU)=bmu;`L-vgTKU6IUJLTgPg)xI z{f~}OzShhCc#q$=!tYxTgy*$V=hfL|q5Xa!TpYaGUg8}7I#=$$OIKP3mWAV3{r8o? z{_7*R8Y}I+%R=A(iP7Rzx&M~>#i5G$t^0QFa7Fy~Bc2Te9|x&$eNidDf48wBzKRVj zo>Xt`3HyAH+9;oRk8fGwTki3FD}3K$<{00)#ZtbPx7xA(0A!zx&Z$-q4noMS+0Y;h)X_?mjaXbA%3>kMk z{n7W`U9G-W3K*Pt?%ZSi?%A{FoIPjvecrEgR{vtLmJ0AKoddyC!Qn|C1p zp7i_%%RCx^eK~lQ?~ewaJbSRiUc5Ufc$Vcip7|X@}pQv4hO`Fy@5#de19Y`AdN(#TKQz_zacYA1c*Kj zNAzhqO&`f=`*a-n4$(((I-q)v0&3v&KxxhZ)X335O`H*^nKJ=pI5SWS#{jkB)cb6l z6)4NufZ910s3TA`SeHHoDL+`BTgKVnA@0e=eI>hS=SXW;$`!#~U7QoBn=1xdHm3EI z2rlKR_wxP2k^TXE0r-r1dj^tE1@Enc|M;S|<}~<(qF5%T!Sq_mvLp-2=XglY;D%;` z@XKsK5I40Gnn7b)H95;^IPE>TXMQAVPSzM?lDX7zLOWSqCCEQ z!6-p*ln?L@JjaR%Q$ZUH3-l{dJ~(`}`;uS@k48sEqyEdISFQwjK^Ghv33*6C7YT%} zh&SU)p>+f~psK3sQKW-z&-%fW%>D6lOIqB$}Om&G$bv<>p%;i15Av}t5ap#4{Z zH=;0w@#=hh-;}I^+!6U}Ah(G1^6C%T-*1nXH^#~vSIV1~>845DZR2$3hS_%K)YPfz zuDgL%bIp2rL%jS%to%g6Rq+AyJ~MxOo77fVCp&Myv`rKCqFM8d`8~@fskK1aotLIw zT4ieIogY>#RKz`}W1iEio~}(R!8-3uOie7)<=Y0LqEYU!ymF3HI@{EzDs904i2=Z* zk`Of_Qhy&Zvjp&naw(PJ%hk&V6=bIBCNZHQ#^9ev)8o;K?@*uyLSdA+d?i$l;25g- zDxiWb8tCV_@bzK;PypT;H(rYQG0$w0 z1XIi(gbEpRMtLL7`MfBdU~cJ81TJC%nF3Pfr`mh+wVRr#GFxbYOrDhn4OsZ4L>gj$<}*et=z4k=w1PXDQVIT%0Pi14YG}cpcBwz) z=eW>Wwi!*dnz> zrbz=p+GwA4&bnvZal0pG_pI0(R_VqBTN-CQG1l{`bD8z5uxBT!b=o$qn>EfD<7{1w zty^L1SLucXZMkEevaZsub(+oUW{qwvz~lc1gA#GQBOV9tRJa@(%85hmnBq=FUjuid zCdf)^8d?Ph+ChAKSt(f$xKrWXz1(Gt;E%v97G&lE^$#0hM%`nk?8t3HBct3DsBorJ7LO@J5kjH;- zk08kDu@^=uyS;k_!+%&D>rByf=WO>(cih<+b2hFxn^u|T1Y^I`Gu5-oRID@3>E7Ab zW?qXIdt=4kmEz`Arsbgl)YG;p*SZF?_@P-+U0e7v7n$cveKvB*M)_Qsx=XQ$eg~)h zvb(<*IPu33Gr1=GiCw})w$ys|3oKH9mk}yI1+sU&Q3xs!D7xMA*ht}@hXH07b{NzU z-Gze?D%!bOw?DxJ^iGr$sw^bUi%o8R!)QQh6X-uL{}z zP)J4n`gR9Y)lU8l;rWk16vew-ef%SCx$4;!N5`ZgHz*0~CYjJw^BQ-lbe*}5Cwh;& zR=S3^P93>;rp9-Q`mDME@=K?vPK|Nti~;h$)KQ(5JWBVegzvtXrg`8?!Y&0l+W7?G zyDu|%YT!#r@O_~mex=B0w&jb^Q`L^&78%`fM}MV?%bPg zthKg0EeyMEL8n?kpBQP&N`tsYkd=5BIM|BfOA78F&S#h*Z8a8O#9ov-cF1<=uC(<0=1XF~2ZSHohGKX`Z z3Y!~dZ8NqxdX@D^?ky~hSe!}&QNeD4SIa7WZ`WxId_3%)RUmQ6E^ZSPypO;*8JABpDVuKs< zOVyOG-ndk2z`U38wPk8(1^wHF(Vw+jC=Y_Z8~kg2Yw@oG37`k`8@^Ng_rfMN6al;k z$q$j>emTjCeg$*55W>HTz zKqpGeKdp%|=VY&U6BZs*$orm9oek{AMfERgd`;wHnZtLGT5NPfe(50PYcMVyF<`!l z@}0~?HEJDEr?=?}hP5+v`fo*0`-!;CAnlX|Q+Tp+%CKb!wSCuobrf+WCg=mV3TGwt zP)57F)(QQWVTa9|f)hSQ0aef+ z8n_l18uMQn#f|7ZiY?xW#DoMbU@#pen-Vtv_+iUF93nh!YvOThPOknb7WGf|{l=RL ztDEfl8_;*;U9gC55gXEm*X8PtPa$aITXA$gS!ZvfxI)?huvXl>X(C(|@zRzrN?X=l z<##W}E1F^zO&{q#F@9`ZbDh5Ls+=>#tB%I1j(*hnN%zOytFE)_uBtib-?{(B9j|VW zRkyEHKfCJcxbG}YxGLkW`k1Re?m825ok>)9zb3WrGB8=)hqsMXamm9{qNHWJhOj#m z&hogkHs-92JKJN<_PFy*%y}l^^2{Fx)mmHvO^U0biQNH9Hg=cFTF*&wt>?7*TF*HQ zJ3R+GAhY)iv`wJlZIbW&zHm5 zKUX!tm0zEvx(M^HI}A|rIZ1Uf#?NU3lz(ocy7Fy{_}8a9vaC`2%x7mJ2E>yU9$Hq) zQcB7e!BjPMv6d3!pp!KSmGxq?FQCqsk}-OOm5Z9XV1|O3Eihxxs-bRuGf= z+|+sw-NXtbJxQ7aLjUe&2j|yU0qB{v=cGs zi52I`Rpt~3SJoM~)qP>BTW6IT^STd>3&u6}=zUnmHb^3KzVpNGh3-|hZJl-9b$;M} z-yL_i#@wwd?qjR$@%xNT{LbNEj6E1EasvAxDHhX!Ymu1V5M-g$im*M=$3uWZ5%BTy6 zGA)06oucLcKk&c`ZfHS|Uh&s&YJk}ly9WA@Q-+PJ8B~eraY`RzyiTPL_`X7k>0vA! z?xfa_64MiF$kDIlIPkcGu@sK^sok}3%ulUFTFfuoG6cbi@szA2#`P%h>h<8=OR+t< zmTJ82)jW1tSDx}`W!RhHJ0((b;c*d6^5{ipz5!1~k3OF>zd)t!UVaU$i?X2)b2%=) z^fh6&$&?OwX`Nf~^}BWn|FoEVW>2*!2{vF*iqpU0ZA~2ZB+3qaZO~b)TcjSpM%rWv zYq7FxWye9hbju5DpxH|^m*%=won8p;m9&7>V|S(EeHOG&;6CZ`zGAR!HeH0#5~nL; zbY+5Z!mULLnT-55MJA)^p{;Y~e-S_asmNRR{foUvf&{)c%W-?)FTSZitiKwn6 zm#mbp)Vx$=@RdRyyrw()zI!^Ou9kAcwo%d8I2!|iKL7&=P z_o;jgl^kWt(11RFw2RRxp}tg46KRt0_Dvm;<~M04NHsGk`Giy67JOz-_N3zO9+UV5 zFpps{freisc)LF|8sO0Z;VY2fs+(X6z(=2c@snb~Eas)~i$ICt6fv~op~QWv0wu1; z33N2vT%|)UgRhe09jrVW1mg{Pv7Woi_XcA5XV3~Xc2VhU$4p1O z$Qvv2#*5lwMQtlZrzYw9Ek{2we{BA_Wtl!Rsk;Lod&X&ZjCQZmN9GTG>2yzaCTvC1 z*Wdj~f_2U^GfbSVjN3tAw=X^0<^@yOmH!A#_DfxLqscHMdr zey-tG=L4OUs(4VPr4BrxjgsjCaKYAz9fwQMR>j7%Q6rR~#+Z!iNtCWP&B&~sx)`i5)k0nGAKP^qgUk+^wVh@bgo=LLpdhO1x z+dATqN|Cs7D-u0af(V|df~)-vvzP2$3kK(zx{UJ zyqS65d-G<0N~Mwn+O?tIoL6In{DCix7O#ViuK-*poLGcYuDP_RSsF#G1J*5FVFR#X z84AY$$E=vbalmmau5iLl6iv$nZ1N7bqnNak3MT<~TAh?=MCcdxH#YF^Q^a}ak`}%5 zu0^s`S6L~IoFP25NO-qMiq0h?Qdr&9J(<1p!_&eK()O(Fc<>D7L^@w_TrQ-ac0Bc7 zDvumX7fZb2ijg(6&CoD@s^nIR9-x@GEW7|f;!Q6QvqGZwTp%597PZrM-W33hKwJ&h z^vHVu>uw+>#iUbA`Ht$^|4J zXWYnxxK2JI7gYCF!Rf`Lgvc~#aqPAhKtQ9$wV)A9(x7pPMmkEW@XP2^;b4$=*%c?4 zV|FDdF}qwA9v7Sy6dj?Sw}Xrk8lGJgVJgzrbrM&XxLigHV*%0%`1$z+h59(P(v^$i@WpTqlB96pn(%sikvmkyr-TLB_ZN zGR|Wl6Fd&mJH0)-w;xQX@5rYIhhA-S1AE!*qCvbERvdAC-!J;0>`AqAL zj_e%zBuwF>SRgBASH$M0CvW!Mda9NlUp6OXALzFDmw*oC-&p`ErCa#NX@Fx?Yn*b2 zLT^(tH&S;83vd#vWwAB}))`$6z(Xaa#N?NQAe!CZuY7{D@61#g+A#-?q94flY&#NK z7#zTvp8?TK#d}}8|M>^Cf$W{3?}zU8e0QuidV1NMY{SZ1FhLtuHj4wWp*69g@4|*L zC7*^F!){;5%a8w0M=HFzVqdFr!j ztt<05rgtQFvi5&!NoaZ%*To_}Df?S_9i>No`;D;-t_-i_wNH=MHpp*_QgYzd`Q1v% zrhCW1boWEUH7p%m-xg*#8pB{WIo;a5!_7ejm)I5;>(tDj_z$7o%@}VaAInqod9ZHv zV!1f>Ew7E9S~lNML7mDZLz6L8qFI<^3>buO6~@9Y#x7NCx31}AEFWDljLAbVvwRi> zCXsv|#Rv*y>o!pnt1Cd_rs-f`>D*HFu@ov6<}9M|B`FFg*j2IMjr(Ru7Kf+iZI>*P?9zWjR2HbgTlSYI^5|a zGtuXgFY(J%cA~D+b^JhhKAR~?b+1Po&*1>PW}^Gdd^sZL6?p>s@NV(XgIL!orN5Bz zhh%V-46Krat95YbA$f6?99@qSZQnxg1KM+s_I#479mw9N$N$tcIZFC*cU&%gt Qn$`w$cdkAr2-ULx0C7~d+W-In literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/_autogen_fixtures.py b/venv/lib/python3.12/site-packages/alembic/testing/suite/_autogen_fixtures.py new file mode 100644 index 00000000..8329a1ac --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/suite/_autogen_fixtures.py @@ -0,0 +1,479 @@ +from __future__ import annotations + +from typing import Any +from typing import Dict +from typing import Literal +from typing import overload +from typing import Set + +from sqlalchemy import CHAR +from sqlalchemy import CheckConstraint +from sqlalchemy import Column +from sqlalchemy import event +from sqlalchemy import ForeignKey +from sqlalchemy import Index +from sqlalchemy import inspect +from sqlalchemy import Integer +from sqlalchemy import MetaData +from sqlalchemy import Numeric +from sqlalchemy import PrimaryKeyConstraint +from sqlalchemy import String +from sqlalchemy import Table +from sqlalchemy import Text +from sqlalchemy import text +from sqlalchemy import UniqueConstraint + +from ... import autogenerate +from ... import util +from ...autogenerate import api +from ...ddl.base import _fk_spec +from ...migration import MigrationContext +from ...operations import ops +from ...testing import config +from ...testing import eq_ +from ...testing.env import clear_staging_env +from ...testing.env import staging_env + +names_in_this_test: Set[Any] = set() + + +@event.listens_for(Table, "after_parent_attach") +def new_table(table, parent): + names_in_this_test.add(table.name) + + +def _default_include_object(obj, name, type_, reflected, compare_to): + if type_ == "table": + return name in names_in_this_test + else: + return True + + +_default_object_filters: Any = _default_include_object + +_default_name_filters: Any = None + + +class ModelOne: + __requires__ = ("unique_constraint_reflection",) + + schema: Any = None + + @classmethod + def _get_db_schema(cls): + schema = cls.schema + + m = MetaData(schema=schema) + + Table( + "user", + m, + Column("id", Integer, primary_key=True), + Column("name", String(50)), + Column("a1", Text), + Column("pw", String(50)), + Index("pw_idx", "pw"), + ) + + Table( + "address", + m, + Column("id", Integer, primary_key=True), + Column("email_address", String(100), nullable=False), + ) + + Table( + "order", + m, + Column("order_id", Integer, primary_key=True), + Column( + "amount", + Numeric(8, 2), + nullable=False, + server_default=text("0"), + ), + CheckConstraint("amount >= 0", name="ck_order_amount"), + ) + + Table( + "extra", + m, + Column("x", CHAR), + Column("uid", Integer, ForeignKey("user.id")), + ) + + return m + + @classmethod + def _get_model_schema(cls): + schema = cls.schema + + m = MetaData(schema=schema) + + Table( + "user", + m, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", Text, server_default="x"), + ) + + Table( + "address", + m, + Column("id", Integer, primary_key=True), + Column("email_address", String(100), nullable=False), + Column("street", String(50)), + UniqueConstraint("email_address", name="uq_email"), + ) + + Table( + "order", + m, + Column("order_id", Integer, primary_key=True), + Column( + "amount", + Numeric(10, 2), + nullable=True, + server_default=text("0"), + ), + Column("user_id", Integer, ForeignKey("user.id")), + CheckConstraint("amount > -1", name="ck_order_amount"), + ) + + Table( + "item", + m, + Column("id", Integer, primary_key=True), + Column("description", String(100)), + Column("order_id", Integer, ForeignKey("order.order_id")), + CheckConstraint("len(description) > 5"), + ) + return m + + +class NamingConvModel: + __requires__ = ("unique_constraint_reflection",) + configure_opts = {"conv_all_constraint_names": True} + naming_convention = { + "ix": "ix_%(column_0_label)s", + "uq": "uq_%(table_name)s_%(constraint_name)s", + "ck": "ck_%(table_name)s_%(constraint_name)s", + "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", + "pk": "pk_%(table_name)s", + } + + @classmethod + def _get_db_schema(cls): + # database side - assume all constraints have a name that + # we would assume here is a "db generated" name. need to make + # sure these all render with op.f(). + m = MetaData() + Table( + "x1", + m, + Column("q", Integer), + Index("db_x1_index_q", "q"), + PrimaryKeyConstraint("q", name="db_x1_primary_q"), + ) + Table( + "x2", + m, + Column("q", Integer), + Column("p", ForeignKey("x1.q", name="db_x2_foreign_q")), + CheckConstraint("q > 5", name="db_x2_check_q"), + ) + Table( + "x3", + m, + Column("q", Integer), + Column("r", Integer), + Column("s", Integer), + UniqueConstraint("q", name="db_x3_unique_q"), + ) + Table( + "x4", + m, + Column("q", Integer), + PrimaryKeyConstraint("q", name="db_x4_primary_q"), + ) + Table( + "x5", + m, + Column("q", Integer), + Column("p", ForeignKey("x4.q", name="db_x5_foreign_q")), + Column("r", Integer), + Column("s", Integer), + PrimaryKeyConstraint("q", name="db_x5_primary_q"), + UniqueConstraint("r", name="db_x5_unique_r"), + CheckConstraint("s > 5", name="db_x5_check_s"), + ) + # SQLite and it's "no names needed" thing. bleh. + # we can't have a name for these so you'll see "None" for the name. + Table( + "unnamed_sqlite", + m, + Column("q", Integer), + Column("r", Integer), + PrimaryKeyConstraint("q"), + UniqueConstraint("r"), + ) + return m + + @classmethod + def _get_model_schema(cls): + from sqlalchemy.sql.naming import conv + + m = MetaData(naming_convention=cls.naming_convention) + Table( + "x1", m, Column("q", Integer, primary_key=True), Index(None, "q") + ) + Table( + "x2", + m, + Column("q", Integer), + Column("p", ForeignKey("x1.q")), + CheckConstraint("q > 5", name="token_x2check1"), + ) + Table( + "x3", + m, + Column("q", Integer), + Column("r", Integer), + Column("s", Integer), + UniqueConstraint("r", name="token_x3r"), + UniqueConstraint("s", name=conv("userdef_x3_unique_s")), + ) + Table( + "x4", + m, + Column("q", Integer, primary_key=True), + Index("userdef_x4_idx_q", "q"), + ) + Table( + "x6", + m, + Column("q", Integer, primary_key=True), + Column("p", ForeignKey("x4.q")), + Column("r", Integer), + Column("s", Integer), + UniqueConstraint("r", name="token_x6r"), + CheckConstraint("s > 5", "token_x6check1"), + CheckConstraint("s < 20", conv("userdef_x6_check_s")), + ) + return m + + +class _ComparesFKs: + def _assert_fk_diff( + self, + diff, + type_, + source_table, + source_columns, + target_table, + target_columns, + name=None, + conditional_name=None, + source_schema=None, + onupdate=None, + ondelete=None, + initially=None, + deferrable=None, + ): + # the public API for ForeignKeyConstraint was not very rich + # in 0.7, 0.8, so here we use the well-known but slightly + # private API to get at its elements + ( + fk_source_schema, + fk_source_table, + fk_source_columns, + fk_target_schema, + fk_target_table, + fk_target_columns, + fk_onupdate, + fk_ondelete, + fk_deferrable, + fk_initially, + ) = _fk_spec(diff[1]) + + eq_(diff[0], type_) + eq_(fk_source_table, source_table) + eq_(fk_source_columns, source_columns) + eq_(fk_target_table, target_table) + eq_(fk_source_schema, source_schema) + eq_(fk_onupdate, onupdate) + eq_(fk_ondelete, ondelete) + eq_(fk_initially, initially) + eq_(fk_deferrable, deferrable) + + eq_([elem.column.name for elem in diff[1].elements], target_columns) + if conditional_name is not None: + if conditional_name == "servergenerated": + fks = inspect(self.bind).get_foreign_keys(source_table) + server_fk_name = fks[0]["name"] + eq_(diff[1].name, server_fk_name) + else: + eq_(diff[1].name, conditional_name) + else: + eq_(diff[1].name, name) + + +class AutogenTest(_ComparesFKs): + def _flatten_diffs(self, diffs): + for d in diffs: + if isinstance(d, list): + yield from self._flatten_diffs(d) + else: + yield d + + @classmethod + def _get_bind(cls): + return config.db + + configure_opts: Dict[Any, Any] = {} + + @classmethod + def setup_class(cls): + staging_env() + cls.bind = cls._get_bind() + cls.m1 = cls._get_db_schema() + cls.m1.create_all(cls.bind) + cls.m2 = cls._get_model_schema() + + @classmethod + def teardown_class(cls): + cls.m1.drop_all(cls.bind) + clear_staging_env() + + def setUp(self): + self.conn = conn = self.bind.connect() + ctx_opts = { + "compare_type": True, + "compare_server_default": True, + "target_metadata": self.m2, + "upgrade_token": "upgrades", + "downgrade_token": "downgrades", + "alembic_module_prefix": "op.", + "sqlalchemy_module_prefix": "sa.", + "include_object": _default_object_filters, + "include_name": _default_name_filters, + } + if self.configure_opts: + ctx_opts.update(self.configure_opts) + self.context = context = MigrationContext.configure( + connection=conn, opts=ctx_opts + ) + + self.autogen_context = api.AutogenContext(context, self.m2) + + def tearDown(self): + self.conn.close() + + def _update_context( + self, object_filters=None, name_filters=None, include_schemas=None + ): + if include_schemas is not None: + self.autogen_context.opts["include_schemas"] = include_schemas + if object_filters is not None: + self.autogen_context._object_filters = [object_filters] + if name_filters is not None: + self.autogen_context._name_filters = [name_filters] + return self.autogen_context + + +class AutogenFixtureTest(_ComparesFKs): + + @overload + def _fixture( + self, + m1: MetaData, + m2: MetaData, + include_schemas=..., + opts=..., + object_filters=..., + name_filters=..., + *, + return_ops: Literal[True], + max_identifier_length=..., + ) -> ops.UpgradeOps: ... + + @overload + def _fixture( + self, + m1: MetaData, + m2: MetaData, + include_schemas=..., + opts=..., + object_filters=..., + name_filters=..., + *, + return_ops: Literal[False] = ..., + max_identifier_length=..., + ) -> list[Any]: ... + + def _fixture( + self, + m1: MetaData, + m2: MetaData, + include_schemas=False, + opts=None, + object_filters=_default_object_filters, + name_filters=_default_name_filters, + return_ops: bool = False, + max_identifier_length=None, + ) -> ops.UpgradeOps | list[Any]: + if max_identifier_length: + dialect = self.bind.dialect + existing_length = dialect.max_identifier_length + dialect.max_identifier_length = ( + dialect._user_defined_max_identifier_length + ) = max_identifier_length + try: + self._alembic_metadata, model_metadata = m1, m2 + for m in util.to_list(self._alembic_metadata): + m.create_all(self.bind) + + with self.bind.connect() as conn: + ctx_opts = { + "compare_type": True, + "compare_server_default": True, + "target_metadata": model_metadata, + "upgrade_token": "upgrades", + "downgrade_token": "downgrades", + "alembic_module_prefix": "op.", + "sqlalchemy_module_prefix": "sa.", + "include_object": object_filters, + "include_name": name_filters, + "include_schemas": include_schemas, + } + if opts: + ctx_opts.update(opts) + self.context = context = MigrationContext.configure( + connection=conn, opts=ctx_opts + ) + + autogen_context = api.AutogenContext(context, model_metadata) + uo = ops.UpgradeOps(ops=[]) + autogenerate._produce_net_changes(autogen_context, uo) + + if return_ops: + return uo + else: + return uo.as_diffs() + finally: + if max_identifier_length: + dialect = self.bind.dialect + dialect.max_identifier_length = ( + dialect._user_defined_max_identifier_length + ) = existing_length + + def setUp(self): + staging_env() + self.bind = config.db + + def tearDown(self): + if hasattr(self, "_alembic_metadata"): + for m in util.to_list(self._alembic_metadata): + m.drop_all(self.bind) + clear_staging_env() diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_comments.py b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_comments.py new file mode 100644 index 00000000..7ef074f5 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_comments.py @@ -0,0 +1,242 @@ +from sqlalchemy import Column +from sqlalchemy import Float +from sqlalchemy import MetaData +from sqlalchemy import String +from sqlalchemy import Table + +from ._autogen_fixtures import AutogenFixtureTest +from ...testing import eq_ +from ...testing import mock +from ...testing import TestBase + + +class AutogenerateCommentsTest(AutogenFixtureTest, TestBase): + __backend__ = True + + __requires__ = ("comments",) + + def test_existing_table_comment_no_change(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("test", String(10), primary_key=True), + comment="this is some table", + ) + + Table( + "some_table", + m2, + Column("test", String(10), primary_key=True), + comment="this is some table", + ) + + diffs = self._fixture(m1, m2) + + eq_(diffs, []) + + def test_add_table_comment(self): + m1 = MetaData() + m2 = MetaData() + + Table("some_table", m1, Column("test", String(10), primary_key=True)) + + Table( + "some_table", + m2, + Column("test", String(10), primary_key=True), + comment="this is some table", + ) + + diffs = self._fixture(m1, m2) + + eq_(diffs[0][0], "add_table_comment") + eq_(diffs[0][1].comment, "this is some table") + eq_(diffs[0][2], None) + + def test_remove_table_comment(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("test", String(10), primary_key=True), + comment="this is some table", + ) + + Table("some_table", m2, Column("test", String(10), primary_key=True)) + + diffs = self._fixture(m1, m2) + + eq_(diffs[0][0], "remove_table_comment") + eq_(diffs[0][1].comment, None) + + def test_alter_table_comment(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("test", String(10), primary_key=True), + comment="this is some table", + ) + + Table( + "some_table", + m2, + Column("test", String(10), primary_key=True), + comment="this is also some table", + ) + + diffs = self._fixture(m1, m2) + + eq_(diffs[0][0], "add_table_comment") + eq_(diffs[0][1].comment, "this is also some table") + eq_(diffs[0][2], "this is some table") + + def test_existing_column_comment_no_change(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("test", String(10), primary_key=True), + Column("amount", Float, comment="the amount"), + ) + + Table( + "some_table", + m2, + Column("test", String(10), primary_key=True), + Column("amount", Float, comment="the amount"), + ) + + diffs = self._fixture(m1, m2) + + eq_(diffs, []) + + def test_add_column_comment(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("test", String(10), primary_key=True), + Column("amount", Float), + ) + + Table( + "some_table", + m2, + Column("test", String(10), primary_key=True), + Column("amount", Float, comment="the amount"), + ) + + diffs = self._fixture(m1, m2) + eq_( + diffs, + [ + [ + ( + "modify_comment", + None, + "some_table", + "amount", + { + "existing_nullable": True, + "existing_type": mock.ANY, + "existing_server_default": False, + }, + None, + "the amount", + ) + ] + ], + ) + + def test_remove_column_comment(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("test", String(10), primary_key=True), + Column("amount", Float, comment="the amount"), + ) + + Table( + "some_table", + m2, + Column("test", String(10), primary_key=True), + Column("amount", Float), + ) + + diffs = self._fixture(m1, m2) + eq_( + diffs, + [ + [ + ( + "modify_comment", + None, + "some_table", + "amount", + { + "existing_nullable": True, + "existing_type": mock.ANY, + "existing_server_default": False, + }, + "the amount", + None, + ) + ] + ], + ) + + def test_alter_column_comment(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("test", String(10), primary_key=True), + Column("amount", Float, comment="the amount"), + ) + + Table( + "some_table", + m2, + Column("test", String(10), primary_key=True), + Column("amount", Float, comment="the adjusted amount"), + ) + + diffs = self._fixture(m1, m2) + + eq_( + diffs, + [ + [ + ( + "modify_comment", + None, + "some_table", + "amount", + { + "existing_nullable": True, + "existing_type": mock.ANY, + "existing_server_default": False, + }, + "the amount", + "the adjusted amount", + ) + ] + ], + ) diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_computed.py b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_computed.py new file mode 100644 index 00000000..586691b2 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_computed.py @@ -0,0 +1,157 @@ +from contextlib import nullcontext + +import sqlalchemy as sa +from sqlalchemy import Column +from sqlalchemy import Integer +from sqlalchemy import MetaData +from sqlalchemy import Table + +from ._autogen_fixtures import AutogenFixtureTest +from ... import testing +from ...testing import config +from ...testing import eq_ +from ...testing import expect_warnings +from ...testing import is_ +from ...testing import is_true +from ...testing import mock +from ...testing import TestBase + + +class AutogenerateComputedTest(AutogenFixtureTest, TestBase): + __requires__ = ("computed_columns",) + __backend__ = True + + def _fixture_ctx(self): + if config.requirements.computed_columns_warn_no_persisted.enabled: + ctx = expect_warnings() + else: + ctx = nullcontext() + return ctx + + def test_add_computed_column(self): + m1 = MetaData() + m2 = MetaData() + + Table("user", m1, Column("id", Integer, primary_key=True)) + + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("foo", Integer, sa.Computed("5")), + ) + + with self._fixture_ctx(): + diffs = self._fixture(m1, m2) + + eq_(diffs[0][0], "add_column") + eq_(diffs[0][2], "user") + eq_(diffs[0][3].name, "foo") + c = diffs[0][3].computed + + is_true(isinstance(c, sa.Computed)) + is_(c.persisted, None) + eq_(str(c.sqltext), "5") + + def test_remove_computed_column(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("foo", Integer, sa.Computed("5")), + ) + + Table("user", m2, Column("id", Integer, primary_key=True)) + + with self._fixture_ctx(): + diffs = self._fixture(m1, m2) + + eq_(diffs[0][0], "remove_column") + eq_(diffs[0][2], "user") + c = diffs[0][3] + eq_(c.name, "foo") + + is_true(isinstance(c.computed, sa.Computed)) + is_true(isinstance(c.server_default, sa.Computed)) + + @testing.combinations( + lambda: (None, sa.Computed("bar*5")), + (lambda: (sa.Computed("bar*5"), None)), + lambda: ( + sa.Computed("bar*5"), + sa.Computed("bar * 42", persisted=True), + ), + lambda: (sa.Computed("bar*5"), sa.Computed("bar * 42")), + ) + def test_cant_change_computed_warning(self, test_case): + arg_before, arg_after = testing.resolve_lambda(test_case, **locals()) + m1 = MetaData() + m2 = MetaData() + + arg_before = [] if arg_before is None else [arg_before] + arg_after = [] if arg_after is None else [arg_after] + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("bar", Integer), + Column("foo", Integer, *arg_before), + ) + + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("bar", Integer), + Column("foo", Integer, *arg_after), + ) + + with mock.patch("alembic.util.warn") as mock_warn, self._fixture_ctx(): + diffs = self._fixture(m1, m2) + + eq_( + mock_warn.mock_calls, + [mock.call("Computed default on user.foo cannot be modified")], + ) + + eq_(list(diffs), []) + + @testing.combinations( + lambda: (None, None), + lambda: (sa.Computed("5"), sa.Computed("5")), + lambda: (sa.Computed("bar*5"), sa.Computed("bar*5")), + lambda: (sa.Computed("bar*5"), sa.Computed("bar * \r\n\t5")), + ) + def test_computed_unchanged(self, test_case): + arg_before, arg_after = testing.resolve_lambda(test_case, **locals()) + m1 = MetaData() + m2 = MetaData() + + arg_before = [] if arg_before is None else [arg_before] + arg_after = [] if arg_after is None else [arg_after] + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("bar", Integer), + Column("foo", Integer, *arg_before), + ) + + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("bar", Integer), + Column("foo", Integer, *arg_after), + ) + + with mock.patch("alembic.util.warn") as mock_warn, self._fixture_ctx(): + diffs = self._fixture(m1, m2) + eq_(mock_warn.mock_calls, []) + + eq_(list(diffs), []) diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_diffs.py b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_diffs.py new file mode 100644 index 00000000..75bcd37a --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_diffs.py @@ -0,0 +1,273 @@ +from sqlalchemy import BigInteger +from sqlalchemy import Column +from sqlalchemy import Integer +from sqlalchemy import MetaData +from sqlalchemy import Table +from sqlalchemy.testing import in_ + +from ._autogen_fixtures import AutogenFixtureTest +from ... import testing +from ...testing import config +from ...testing import eq_ +from ...testing import is_ +from ...testing import TestBase + + +class AlterColumnTest(AutogenFixtureTest, TestBase): + __backend__ = True + + @testing.combinations((True,), (False,)) + @config.requirements.comments + def test_all_existings_filled(self, pk): + m1 = MetaData() + m2 = MetaData() + + Table("a", m1, Column("x", Integer, primary_key=pk)) + Table("a", m2, Column("x", Integer, comment="x", primary_key=pk)) + + alter_col = self._assert_alter_col(m1, m2, pk) + eq_(alter_col.modify_comment, "x") + + @testing.combinations((True,), (False,)) + @config.requirements.comments + def test_all_existings_filled_in_notnull(self, pk): + m1 = MetaData() + m2 = MetaData() + + Table("a", m1, Column("x", Integer, nullable=False, primary_key=pk)) + Table( + "a", + m2, + Column("x", Integer, nullable=False, comment="x", primary_key=pk), + ) + + self._assert_alter_col(m1, m2, pk, nullable=False) + + @testing.combinations((True,), (False,)) + @config.requirements.comments + def test_all_existings_filled_in_comment(self, pk): + m1 = MetaData() + m2 = MetaData() + + Table("a", m1, Column("x", Integer, comment="old", primary_key=pk)) + Table("a", m2, Column("x", Integer, comment="new", primary_key=pk)) + + alter_col = self._assert_alter_col(m1, m2, pk) + eq_(alter_col.existing_comment, "old") + + @testing.combinations((True,), (False,)) + @config.requirements.comments + def test_all_existings_filled_in_server_default(self, pk): + m1 = MetaData() + m2 = MetaData() + + Table( + "a", m1, Column("x", Integer, server_default="5", primary_key=pk) + ) + Table( + "a", + m2, + Column( + "x", Integer, server_default="5", comment="new", primary_key=pk + ), + ) + + alter_col = self._assert_alter_col(m1, m2, pk) + in_("5", alter_col.existing_server_default.arg.text) + + def _assert_alter_col(self, m1, m2, pk, nullable=None): + ops = self._fixture(m1, m2, return_ops=True) + modify_table = ops.ops[-1] + alter_col = modify_table.ops[0] + + if nullable is None: + eq_(alter_col.existing_nullable, not pk) + else: + eq_(alter_col.existing_nullable, nullable) + assert alter_col.existing_type._compare_type_affinity(Integer()) + return alter_col + + +class AutoincrementTest(AutogenFixtureTest, TestBase): + __backend__ = True + __requires__ = ("integer_subtype_comparisons",) + + def test_alter_column_autoincrement_none(self): + m1 = MetaData() + m2 = MetaData() + + Table("a", m1, Column("x", Integer, nullable=False)) + Table("a", m2, Column("x", Integer, nullable=True)) + + ops = self._fixture(m1, m2, return_ops=True) + assert "autoincrement" not in ops.ops[0].ops[0].kw + + def test_alter_column_autoincrement_pk_false(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "a", + m1, + Column("x", Integer, primary_key=True, autoincrement=False), + ) + Table( + "a", + m2, + Column("x", BigInteger, primary_key=True, autoincrement=False), + ) + + ops = self._fixture(m1, m2, return_ops=True) + is_(ops.ops[0].ops[0].kw["autoincrement"], False) + + def test_alter_column_autoincrement_pk_implicit_true(self): + m1 = MetaData() + m2 = MetaData() + + Table("a", m1, Column("x", Integer, primary_key=True)) + Table("a", m2, Column("x", BigInteger, primary_key=True)) + + ops = self._fixture(m1, m2, return_ops=True) + is_(ops.ops[0].ops[0].kw["autoincrement"], True) + + def test_alter_column_autoincrement_pk_explicit_true(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "a", m1, Column("x", Integer, primary_key=True, autoincrement=True) + ) + Table( + "a", + m2, + Column("x", BigInteger, primary_key=True, autoincrement=True), + ) + + ops = self._fixture(m1, m2, return_ops=True) + is_(ops.ops[0].ops[0].kw["autoincrement"], True) + + def test_alter_column_autoincrement_nonpk_false(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "a", + m1, + Column("id", Integer, primary_key=True), + Column("x", Integer, autoincrement=False), + ) + Table( + "a", + m2, + Column("id", Integer, primary_key=True), + Column("x", BigInteger, autoincrement=False), + ) + + ops = self._fixture(m1, m2, return_ops=True) + is_(ops.ops[0].ops[0].kw["autoincrement"], False) + + def test_alter_column_autoincrement_nonpk_implicit_false(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "a", + m1, + Column("id", Integer, primary_key=True), + Column("x", Integer), + ) + Table( + "a", + m2, + Column("id", Integer, primary_key=True), + Column("x", BigInteger), + ) + + ops = self._fixture(m1, m2, return_ops=True) + assert "autoincrement" not in ops.ops[0].ops[0].kw + + def test_alter_column_autoincrement_nonpk_explicit_true(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "a", + m1, + Column("id", Integer, primary_key=True, autoincrement=False), + Column("x", Integer, autoincrement=True), + ) + Table( + "a", + m2, + Column("id", Integer, primary_key=True, autoincrement=False), + Column("x", BigInteger, autoincrement=True), + ) + + ops = self._fixture(m1, m2, return_ops=True) + is_(ops.ops[0].ops[0].kw["autoincrement"], True) + + def test_alter_column_autoincrement_compositepk_false(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "a", + m1, + Column("id", Integer, primary_key=True), + Column("x", Integer, primary_key=True, autoincrement=False), + ) + Table( + "a", + m2, + Column("id", Integer, primary_key=True), + Column("x", BigInteger, primary_key=True, autoincrement=False), + ) + + ops = self._fixture(m1, m2, return_ops=True) + is_(ops.ops[0].ops[0].kw["autoincrement"], False) + + def test_alter_column_autoincrement_compositepk_implicit_false(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "a", + m1, + Column("id", Integer, primary_key=True), + Column("x", Integer, primary_key=True), + ) + Table( + "a", + m2, + Column("id", Integer, primary_key=True), + Column("x", BigInteger, primary_key=True), + ) + + ops = self._fixture(m1, m2, return_ops=True) + assert "autoincrement" not in ops.ops[0].ops[0].kw + + @config.requirements.autoincrement_on_composite_pk + def test_alter_column_autoincrement_compositepk_explicit_true(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "a", + m1, + Column("id", Integer, primary_key=True, autoincrement=False), + Column("x", Integer, primary_key=True, autoincrement=True), + # on SQLA 1.0 and earlier, this being present + # trips the "add KEY for the primary key" so that the + # AUTO_INCREMENT keyword is accepted by MySQL. SQLA 1.1 and + # greater the columns are just reorganized. + mysql_engine="InnoDB", + ) + Table( + "a", + m2, + Column("id", Integer, primary_key=True, autoincrement=False), + Column("x", BigInteger, primary_key=True, autoincrement=True), + ) + + ops = self._fixture(m1, m2, return_ops=True) + is_(ops.ops[0].ops[0].kw["autoincrement"], True) diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_fks.py b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_fks.py new file mode 100644 index 00000000..d69736e6 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_fks.py @@ -0,0 +1,1191 @@ +from sqlalchemy import Column +from sqlalchemy import ForeignKeyConstraint +from sqlalchemy import Integer +from sqlalchemy import MetaData +from sqlalchemy import String +from sqlalchemy import Table + +from ._autogen_fixtures import AutogenFixtureTest +from ...testing import combinations +from ...testing import config +from ...testing import eq_ +from ...testing import mock +from ...testing import TestBase + + +class AutogenerateForeignKeysTest(AutogenFixtureTest, TestBase): + __backend__ = True + __requires__ = ("foreign_key_constraint_reflection",) + + def test_remove_fk(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("test", String(10), primary_key=True), + ) + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("test2", String(10)), + ForeignKeyConstraint(["test2"], ["some_table.test"]), + ) + + Table( + "some_table", + m2, + Column("test", String(10), primary_key=True), + ) + + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("test2", String(10)), + ) + + diffs = self._fixture(m1, m2) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["test2"], + "some_table", + ["test"], + conditional_name="servergenerated", + ) + + def test_add_fk(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("id", Integer, primary_key=True), + Column("test", String(10)), + ) + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("test2", String(10)), + ) + + Table( + "some_table", + m2, + Column("id", Integer, primary_key=True), + Column("test", String(10)), + ) + + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("test2", String(10)), + ForeignKeyConstraint(["test2"], ["some_table.test"]), + ) + + diffs = self._fixture(m1, m2) + + self._assert_fk_diff( + diffs[0], "add_fk", "user", ["test2"], "some_table", ["test"] + ) + + def test_no_change(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("id", Integer, primary_key=True), + Column("test", String(10)), + ) + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("test2", Integer), + ForeignKeyConstraint(["test2"], ["some_table.id"]), + ) + + Table( + "some_table", + m2, + Column("id", Integer, primary_key=True), + Column("test", String(10)), + ) + + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("test2", Integer), + ForeignKeyConstraint(["test2"], ["some_table.id"]), + ) + + diffs = self._fixture(m1, m2) + + eq_(diffs, []) + + def test_no_change_composite_fk(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("id_1", String(10), primary_key=True), + Column("id_2", String(10), primary_key=True), + ) + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("other_id_1", String(10)), + Column("other_id_2", String(10)), + ForeignKeyConstraint( + ["other_id_1", "other_id_2"], + ["some_table.id_1", "some_table.id_2"], + ), + ) + + Table( + "some_table", + m2, + Column("id_1", String(10), primary_key=True), + Column("id_2", String(10), primary_key=True), + ) + + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("other_id_1", String(10)), + Column("other_id_2", String(10)), + ForeignKeyConstraint( + ["other_id_1", "other_id_2"], + ["some_table.id_1", "some_table.id_2"], + ), + ) + + diffs = self._fixture(m1, m2) + + eq_(diffs, []) + + @config.requirements.foreign_key_name_reflection + def test_casing_convention_changed_so_put_drops_first(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("test", String(10), primary_key=True), + ) + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("test2", String(10)), + ForeignKeyConstraint(["test2"], ["some_table.test"], name="MyFK"), + ) + + Table( + "some_table", + m2, + Column("test", String(10), primary_key=True), + ) + + # foreign key autogen currently does not take "name" into account, + # so change the def just for the purposes of testing the + # add/drop order for now. + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("test2", String(10)), + ForeignKeyConstraint(["a1"], ["some_table.test"], name="myfk"), + ) + + diffs = self._fixture(m1, m2) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["test2"], + "some_table", + ["test"], + name="MyFK", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["a1"], + "some_table", + ["test"], + name="myfk", + ) + + def test_add_composite_fk_with_name(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("id_1", String(10), primary_key=True), + Column("id_2", String(10), primary_key=True), + ) + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("other_id_1", String(10)), + Column("other_id_2", String(10)), + ) + + Table( + "some_table", + m2, + Column("id_1", String(10), primary_key=True), + Column("id_2", String(10), primary_key=True), + ) + + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("other_id_1", String(10)), + Column("other_id_2", String(10)), + ForeignKeyConstraint( + ["other_id_1", "other_id_2"], + ["some_table.id_1", "some_table.id_2"], + name="fk_test_name", + ), + ) + + diffs = self._fixture(m1, m2) + self._assert_fk_diff( + diffs[0], + "add_fk", + "user", + ["other_id_1", "other_id_2"], + "some_table", + ["id_1", "id_2"], + name="fk_test_name", + ) + + @config.requirements.no_name_normalize + def test_remove_composite_fk(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("id_1", String(10), primary_key=True), + Column("id_2", String(10), primary_key=True), + ) + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("other_id_1", String(10)), + Column("other_id_2", String(10)), + ForeignKeyConstraint( + ["other_id_1", "other_id_2"], + ["some_table.id_1", "some_table.id_2"], + name="fk_test_name", + ), + ) + + Table( + "some_table", + m2, + Column("id_1", String(10), primary_key=True), + Column("id_2", String(10), primary_key=True), + ) + + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("a1", String(10), server_default="x"), + Column("other_id_1", String(10)), + Column("other_id_2", String(10)), + ) + + diffs = self._fixture(m1, m2) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["other_id_1", "other_id_2"], + "some_table", + ["id_1", "id_2"], + conditional_name="fk_test_name", + ) + + def test_add_fk_colkeys(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("id_1", String(10), primary_key=True), + Column("id_2", String(10), primary_key=True), + ) + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("other_id_1", String(10)), + Column("other_id_2", String(10)), + ) + + Table( + "some_table", + m2, + Column("id_1", String(10), key="tid1", primary_key=True), + Column("id_2", String(10), key="tid2", primary_key=True), + ) + + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("other_id_1", String(10), key="oid1"), + Column("other_id_2", String(10), key="oid2"), + ForeignKeyConstraint( + ["oid1", "oid2"], + ["some_table.tid1", "some_table.tid2"], + name="fk_test_name", + ), + ) + + diffs = self._fixture(m1, m2) + + self._assert_fk_diff( + diffs[0], + "add_fk", + "user", + ["other_id_1", "other_id_2"], + "some_table", + ["id_1", "id_2"], + name="fk_test_name", + ) + + def test_no_change_colkeys(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("id_1", String(10), primary_key=True), + Column("id_2", String(10), primary_key=True), + ) + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("other_id_1", String(10)), + Column("other_id_2", String(10)), + ForeignKeyConstraint( + ["other_id_1", "other_id_2"], + ["some_table.id_1", "some_table.id_2"], + ), + ) + + Table( + "some_table", + m2, + Column("id_1", String(10), key="tid1", primary_key=True), + Column("id_2", String(10), key="tid2", primary_key=True), + ) + + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("other_id_1", String(10), key="oid1"), + Column("other_id_2", String(10), key="oid2"), + ForeignKeyConstraint( + ["oid1", "oid2"], ["some_table.tid1", "some_table.tid2"] + ), + ) + + diffs = self._fixture(m1, m2) + + eq_(diffs, []) + + +class IncludeHooksTest(AutogenFixtureTest, TestBase): + __backend__ = True + __requires__ = ("fk_names",) + + @combinations(("object",), ("name",)) + @config.requirements.no_name_normalize + def test_remove_connection_fk(self, hook_type): + m1 = MetaData() + m2 = MetaData() + + ref = Table( + "ref", + m1, + Column("id", Integer, primary_key=True), + ) + t1 = Table( + "t", + m1, + Column("x", Integer), + Column("y", Integer), + ) + t1.append_constraint( + ForeignKeyConstraint([t1.c.x], [ref.c.id], name="fk1") + ) + t1.append_constraint( + ForeignKeyConstraint([t1.c.y], [ref.c.id], name="fk2") + ) + + ref = Table( + "ref", + m2, + Column("id", Integer, primary_key=True), + ) + Table( + "t", + m2, + Column("x", Integer), + Column("y", Integer), + ) + + if hook_type == "object": + + def include_object(object_, name, type_, reflected, compare_to): + return not ( + isinstance(object_, ForeignKeyConstraint) + and type_ == "foreign_key_constraint" + and reflected + and name == "fk1" + ) + + diffs = self._fixture(m1, m2, object_filters=include_object) + elif hook_type == "name": + + def include_name(name, type_, parent_names): + if name == "fk1": + if type_ == "index": # MariaDB thing + return True + eq_(type_, "foreign_key_constraint") + eq_( + parent_names, + { + "schema_name": None, + "table_name": "t", + "schema_qualified_table_name": "t", + }, + ) + return False + else: + return True + + diffs = self._fixture(m1, m2, name_filters=include_name) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "t", + ["y"], + "ref", + ["id"], + conditional_name="fk2", + ) + eq_(len(diffs), 1) + + def test_add_metadata_fk(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "ref", + m1, + Column("id", Integer, primary_key=True), + ) + Table( + "t", + m1, + Column("x", Integer), + Column("y", Integer), + ) + + ref = Table( + "ref", + m2, + Column("id", Integer, primary_key=True), + ) + t2 = Table( + "t", + m2, + Column("x", Integer), + Column("y", Integer), + ) + t2.append_constraint( + ForeignKeyConstraint([t2.c.x], [ref.c.id], name="fk1") + ) + t2.append_constraint( + ForeignKeyConstraint([t2.c.y], [ref.c.id], name="fk2") + ) + + def include_object(object_, name, type_, reflected, compare_to): + return not ( + isinstance(object_, ForeignKeyConstraint) + and type_ == "foreign_key_constraint" + and not reflected + and name == "fk1" + ) + + diffs = self._fixture(m1, m2, object_filters=include_object) + + self._assert_fk_diff( + diffs[0], "add_fk", "t", ["y"], "ref", ["id"], name="fk2" + ) + eq_(len(diffs), 1) + + @combinations(("object",), ("name",)) + @config.requirements.no_name_normalize + def test_change_fk(self, hook_type): + m1 = MetaData() + m2 = MetaData() + + r1a = Table( + "ref_a", + m1, + Column("a", Integer, primary_key=True), + ) + Table( + "ref_b", + m1, + Column("a", Integer, primary_key=True), + Column("b", Integer, primary_key=True), + ) + t1 = Table( + "t", + m1, + Column("x", Integer), + Column("y", Integer), + Column("z", Integer), + ) + t1.append_constraint( + ForeignKeyConstraint([t1.c.x], [r1a.c.a], name="fk1") + ) + t1.append_constraint( + ForeignKeyConstraint([t1.c.y], [r1a.c.a], name="fk2") + ) + + Table( + "ref_a", + m2, + Column("a", Integer, primary_key=True), + ) + r2b = Table( + "ref_b", + m2, + Column("a", Integer, primary_key=True), + Column("b", Integer, primary_key=True), + ) + t2 = Table( + "t", + m2, + Column("x", Integer), + Column("y", Integer), + Column("z", Integer), + ) + t2.append_constraint( + ForeignKeyConstraint( + [t2.c.x, t2.c.z], [r2b.c.a, r2b.c.b], name="fk1" + ) + ) + t2.append_constraint( + ForeignKeyConstraint( + [t2.c.y, t2.c.z], [r2b.c.a, r2b.c.b], name="fk2" + ) + ) + + if hook_type == "object": + + def include_object(object_, name, type_, reflected, compare_to): + return not ( + isinstance(object_, ForeignKeyConstraint) + and type_ == "foreign_key_constraint" + and name == "fk1" + ) + + diffs = self._fixture(m1, m2, object_filters=include_object) + elif hook_type == "name": + + def include_name(name, type_, parent_names): + if type_ == "index": + return True # MariaDB thing + + if name == "fk1": + eq_(type_, "foreign_key_constraint") + eq_( + parent_names, + { + "schema_name": None, + "table_name": "t", + "schema_qualified_table_name": "t", + }, + ) + return False + else: + return True + + diffs = self._fixture(m1, m2, name_filters=include_name) + + if hook_type == "object": + self._assert_fk_diff( + diffs[0], "remove_fk", "t", ["y"], "ref_a", ["a"], name="fk2" + ) + self._assert_fk_diff( + diffs[1], + "add_fk", + "t", + ["y", "z"], + "ref_b", + ["a", "b"], + name="fk2", + ) + eq_(len(diffs), 2) + elif hook_type == "name": + eq_( + {(d[0], d[1].name) for d in diffs}, + {("add_fk", "fk2"), ("add_fk", "fk1"), ("remove_fk", "fk2")}, + ) + + +class AutogenerateFKOptionsTest(AutogenFixtureTest, TestBase): + __backend__ = True + + def _fk_opts_fixture(self, old_opts, new_opts): + m1 = MetaData() + m2 = MetaData() + + Table( + "some_table", + m1, + Column("id", Integer, primary_key=True), + Column("test", String(10)), + ) + + Table( + "user", + m1, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("tid", Integer), + ForeignKeyConstraint(["tid"], ["some_table.id"], **old_opts), + ) + + Table( + "some_table", + m2, + Column("id", Integer, primary_key=True), + Column("test", String(10)), + ) + + Table( + "user", + m2, + Column("id", Integer, primary_key=True), + Column("name", String(50), nullable=False), + Column("tid", Integer), + ForeignKeyConstraint(["tid"], ["some_table.id"], **new_opts), + ) + + return self._fixture(m1, m2) + + @config.requirements.fk_ondelete_is_reflected + def test_add_ondelete(self): + diffs = self._fk_opts_fixture({}, {"ondelete": "cascade"}) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + ondelete=None, + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + ondelete="cascade", + ) + + @config.requirements.fk_ondelete_is_reflected + def test_remove_ondelete(self): + diffs = self._fk_opts_fixture({"ondelete": "CASCADE"}, {}) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + ondelete="CASCADE", + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + ondelete=None, + ) + + def test_nochange_ondelete(self): + """test case sensitivity""" + diffs = self._fk_opts_fixture( + {"ondelete": "caSCAde"}, {"ondelete": "CasCade"} + ) + eq_(diffs, []) + + @config.requirements.fk_onupdate_is_reflected + def test_add_onupdate(self): + diffs = self._fk_opts_fixture({}, {"onupdate": "cascade"}) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + onupdate=None, + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + onupdate="cascade", + ) + + @config.requirements.fk_onupdate_is_reflected + def test_remove_onupdate(self): + diffs = self._fk_opts_fixture({"onupdate": "CASCADE"}, {}) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + onupdate="CASCADE", + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + onupdate=None, + ) + + @config.requirements.fk_onupdate + def test_nochange_onupdate(self): + """test case sensitivity""" + diffs = self._fk_opts_fixture( + {"onupdate": "caSCAde"}, {"onupdate": "CasCade"} + ) + eq_(diffs, []) + + @config.requirements.fk_ondelete_restrict + def test_nochange_ondelete_restrict(self): + """test the RESTRICT option which MySQL doesn't report on""" + + diffs = self._fk_opts_fixture( + {"ondelete": "restrict"}, {"ondelete": "restrict"} + ) + eq_(diffs, []) + + @config.requirements.fk_onupdate_restrict + def test_nochange_onupdate_restrict(self): + """test the RESTRICT option which MySQL doesn't report on""" + + diffs = self._fk_opts_fixture( + {"onupdate": "restrict"}, {"onupdate": "restrict"} + ) + eq_(diffs, []) + + @config.requirements.fk_ondelete_noaction + def test_nochange_ondelete_noaction(self): + """test the NO ACTION option which generally comes back as None""" + + diffs = self._fk_opts_fixture( + {"ondelete": "no action"}, {"ondelete": "no action"} + ) + eq_(diffs, []) + + @config.requirements.fk_onupdate + def test_nochange_onupdate_noaction(self): + """test the NO ACTION option which generally comes back as None""" + + diffs = self._fk_opts_fixture( + {"onupdate": "no action"}, {"onupdate": "no action"} + ) + eq_(diffs, []) + + @config.requirements.fk_ondelete_restrict + def test_change_ondelete_from_restrict(self): + """test the RESTRICT option which MySQL doesn't report on""" + + # note that this is impossible to detect if we change + # from RESTRICT to NO ACTION on MySQL. + diffs = self._fk_opts_fixture( + {"ondelete": "restrict"}, {"ondelete": "cascade"} + ) + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + onupdate=None, + ondelete=mock.ANY, # MySQL reports None, PG reports RESTRICT + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + onupdate=None, + ondelete="cascade", + ) + + @config.requirements.fk_ondelete_restrict + def test_change_onupdate_from_restrict(self): + """test the RESTRICT option which MySQL doesn't report on""" + + # note that this is impossible to detect if we change + # from RESTRICT to NO ACTION on MySQL. + diffs = self._fk_opts_fixture( + {"onupdate": "restrict"}, {"onupdate": "cascade"} + ) + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + onupdate=mock.ANY, # MySQL reports None, PG reports RESTRICT + ondelete=None, + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + onupdate="cascade", + ondelete=None, + ) + + @config.requirements.fk_ondelete_is_reflected + @config.requirements.fk_onupdate_is_reflected + def test_ondelete_onupdate_combo(self): + diffs = self._fk_opts_fixture( + {"onupdate": "CASCADE", "ondelete": "SET NULL"}, + {"onupdate": "RESTRICT", "ondelete": "RESTRICT"}, + ) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + onupdate="CASCADE", + ondelete="SET NULL", + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + onupdate="RESTRICT", + ondelete="RESTRICT", + ) + + @config.requirements.fk_initially + def test_add_initially_deferred(self): + diffs = self._fk_opts_fixture({}, {"initially": "deferred"}) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + initially=None, + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + initially="deferred", + ) + + @config.requirements.fk_initially + def test_remove_initially_deferred(self): + diffs = self._fk_opts_fixture({"initially": "deferred"}, {}) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + initially="DEFERRED", + deferrable=True, + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + initially=None, + ) + + @config.requirements.fk_deferrable + @config.requirements.fk_initially + def test_add_initially_immediate_plus_deferrable(self): + diffs = self._fk_opts_fixture( + {}, {"initially": "immediate", "deferrable": True} + ) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + initially=None, + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + initially="immediate", + deferrable=True, + ) + + @config.requirements.fk_deferrable + @config.requirements.fk_initially + def test_remove_initially_immediate_plus_deferrable(self): + diffs = self._fk_opts_fixture( + {"initially": "immediate", "deferrable": True}, {} + ) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + initially=None, # immediate is the default + deferrable=True, + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + initially=None, + deferrable=None, + ) + + @config.requirements.fk_initially + @config.requirements.fk_deferrable + def test_add_initially_deferrable_nochange_one(self): + diffs = self._fk_opts_fixture( + {"deferrable": True, "initially": "immediate"}, + {"deferrable": True, "initially": "immediate"}, + ) + + eq_(diffs, []) + + @config.requirements.fk_initially + @config.requirements.fk_deferrable + def test_add_initially_deferrable_nochange_two(self): + diffs = self._fk_opts_fixture( + {"deferrable": True, "initially": "deferred"}, + {"deferrable": True, "initially": "deferred"}, + ) + + eq_(diffs, []) + + @config.requirements.fk_initially + @config.requirements.fk_deferrable + def test_add_initially_deferrable_nochange_three(self): + diffs = self._fk_opts_fixture( + {"deferrable": None, "initially": "deferred"}, + {"deferrable": None, "initially": "deferred"}, + ) + + eq_(diffs, []) + + @config.requirements.fk_deferrable + def test_add_deferrable(self): + diffs = self._fk_opts_fixture({}, {"deferrable": True}) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + deferrable=None, + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + deferrable=True, + ) + + @config.requirements.fk_deferrable_is_reflected + def test_remove_deferrable(self): + diffs = self._fk_opts_fixture({"deferrable": True}, {}) + + self._assert_fk_diff( + diffs[0], + "remove_fk", + "user", + ["tid"], + "some_table", + ["id"], + deferrable=True, + conditional_name="servergenerated", + ) + + self._assert_fk_diff( + diffs[1], + "add_fk", + "user", + ["tid"], + "some_table", + ["id"], + deferrable=None, + ) diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_identity.py b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_identity.py new file mode 100644 index 00000000..3dee9fc9 --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_autogen_identity.py @@ -0,0 +1,226 @@ +import sqlalchemy as sa +from sqlalchemy import Column +from sqlalchemy import Integer +from sqlalchemy import MetaData +from sqlalchemy import Table + +from alembic.util import sqla_compat +from ._autogen_fixtures import AutogenFixtureTest +from ... import testing +from ...testing import config +from ...testing import eq_ +from ...testing import is_true +from ...testing import TestBase + + +class AutogenerateIdentityTest(AutogenFixtureTest, TestBase): + __requires__ = ("identity_columns",) + __backend__ = True + + def test_add_identity_column(self): + m1 = MetaData() + m2 = MetaData() + + Table("user", m1, Column("other", sa.Text)) + + Table( + "user", + m2, + Column("other", sa.Text), + Column( + "id", + Integer, + sa.Identity(start=5, increment=7), + primary_key=True, + ), + ) + + diffs = self._fixture(m1, m2) + + eq_(diffs[0][0], "add_column") + eq_(diffs[0][2], "user") + eq_(diffs[0][3].name, "id") + i = diffs[0][3].identity + + is_true(isinstance(i, sa.Identity)) + eq_(i.start, 5) + eq_(i.increment, 7) + + def test_remove_identity_column(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "user", + m1, + Column( + "id", + Integer, + sa.Identity(start=2, increment=3), + primary_key=True, + ), + ) + + Table("user", m2) + + diffs = self._fixture(m1, m2) + + eq_(diffs[0][0], "remove_column") + eq_(diffs[0][2], "user") + c = diffs[0][3] + eq_(c.name, "id") + + is_true(isinstance(c.identity, sa.Identity)) + eq_(c.identity.start, 2) + eq_(c.identity.increment, 3) + + def test_no_change_identity_column(self): + m1 = MetaData() + m2 = MetaData() + + for m in (m1, m2): + id_ = sa.Identity(start=2) + Table("user", m, Column("id", Integer, id_)) + + diffs = self._fixture(m1, m2) + + eq_(diffs, []) + + def test_dialect_kwargs_changes(self): + m1 = MetaData() + m2 = MetaData() + + if sqla_compat.identity_has_dialect_kwargs: + args = {"oracle_on_null": True, "oracle_order": True} + else: + args = {"on_null": True, "order": True} + + Table("user", m1, Column("id", Integer, sa.Identity(start=2))) + id_ = sa.Identity(start=2, **args) + Table("user", m2, Column("id", Integer, id_)) + + diffs = self._fixture(m1, m2) + if config.db.name == "oracle": + is_true(len(diffs), 1) + eq_(diffs[0][0][0], "modify_default") + else: + eq_(diffs, []) + + @testing.combinations( + (None, dict(start=2)), + (dict(start=2), None), + (dict(start=2), dict(start=2, increment=7)), + (dict(always=False), dict(always=True)), + ( + dict(start=1, minvalue=0, maxvalue=100, cycle=True), + dict(start=1, minvalue=0, maxvalue=100, cycle=False), + ), + ( + dict(start=10, increment=3, maxvalue=9999), + dict(start=10, increment=1, maxvalue=3333), + ), + ) + @config.requirements.identity_columns_alter + def test_change_identity(self, before, after): + arg_before = (sa.Identity(**before),) if before else () + arg_after = (sa.Identity(**after),) if after else () + + m1 = MetaData() + m2 = MetaData() + + Table( + "user", + m1, + Column("id", Integer, *arg_before), + Column("other", sa.Text), + ) + + Table( + "user", + m2, + Column("id", Integer, *arg_after), + Column("other", sa.Text), + ) + + diffs = self._fixture(m1, m2) + + eq_(len(diffs[0]), 1) + diffs = diffs[0][0] + eq_(diffs[0], "modify_default") + eq_(diffs[2], "user") + eq_(diffs[3], "id") + old = diffs[5] + new = diffs[6] + + def check(kw, idt): + if kw: + is_true(isinstance(idt, sa.Identity)) + for k, v in kw.items(): + eq_(getattr(idt, k), v) + else: + is_true(idt in (None, False)) + + check(before, old) + check(after, new) + + def test_add_identity_to_column(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "user", + m1, + Column("id", Integer), + Column("other", sa.Text), + ) + + Table( + "user", + m2, + Column("id", Integer, sa.Identity(start=2, maxvalue=1000)), + Column("other", sa.Text), + ) + + diffs = self._fixture(m1, m2) + + eq_(len(diffs[0]), 1) + diffs = diffs[0][0] + eq_(diffs[0], "modify_default") + eq_(diffs[2], "user") + eq_(diffs[3], "id") + eq_(diffs[5], None) + added = diffs[6] + + is_true(isinstance(added, sa.Identity)) + eq_(added.start, 2) + eq_(added.maxvalue, 1000) + + def test_remove_identity_from_column(self): + m1 = MetaData() + m2 = MetaData() + + Table( + "user", + m1, + Column("id", Integer, sa.Identity(start=2, maxvalue=1000)), + Column("other", sa.Text), + ) + + Table( + "user", + m2, + Column("id", Integer), + Column("other", sa.Text), + ) + + diffs = self._fixture(m1, m2) + + eq_(len(diffs[0]), 1) + diffs = diffs[0][0] + eq_(diffs[0], "modify_default") + eq_(diffs[2], "user") + eq_(diffs[3], "id") + eq_(diffs[6], None) + removed = diffs[5] + + is_true(isinstance(removed, sa.Identity)) diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/test_environment.py b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_environment.py new file mode 100644 index 00000000..df2d9afb --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_environment.py @@ -0,0 +1,364 @@ +import io + +from ...migration import MigrationContext +from ...testing import assert_raises +from ...testing import config +from ...testing import eq_ +from ...testing import is_ +from ...testing import is_false +from ...testing import is_not_ +from ...testing import is_true +from ...testing import ne_ +from ...testing.fixtures import TestBase + + +class MigrationTransactionTest(TestBase): + __backend__ = True + + conn = None + + def _fixture(self, opts): + self.conn = conn = config.db.connect() + + if opts.get("as_sql", False): + self.context = MigrationContext.configure( + dialect=conn.dialect, opts=opts + ) + self.context.output_buffer = self.context.impl.output_buffer = ( + io.StringIO() + ) + else: + self.context = MigrationContext.configure( + connection=conn, opts=opts + ) + return self.context + + def teardown_method(self): + if self.conn: + self.conn.close() + + def test_proxy_transaction_rollback(self): + context = self._fixture( + {"transaction_per_migration": True, "transactional_ddl": True} + ) + + is_false(self.conn.in_transaction()) + proxy = context.begin_transaction(_per_migration=True) + is_true(self.conn.in_transaction()) + proxy.rollback() + is_false(self.conn.in_transaction()) + + def test_proxy_transaction_commit(self): + context = self._fixture( + {"transaction_per_migration": True, "transactional_ddl": True} + ) + proxy = context.begin_transaction(_per_migration=True) + is_true(self.conn.in_transaction()) + proxy.commit() + is_false(self.conn.in_transaction()) + + def test_proxy_transaction_contextmanager_commit(self): + context = self._fixture( + {"transaction_per_migration": True, "transactional_ddl": True} + ) + proxy = context.begin_transaction(_per_migration=True) + is_true(self.conn.in_transaction()) + with proxy: + pass + is_false(self.conn.in_transaction()) + + def test_proxy_transaction_contextmanager_rollback(self): + context = self._fixture( + {"transaction_per_migration": True, "transactional_ddl": True} + ) + proxy = context.begin_transaction(_per_migration=True) + is_true(self.conn.in_transaction()) + + def go(): + with proxy: + raise Exception("hi") + + assert_raises(Exception, go) + is_false(self.conn.in_transaction()) + + def test_proxy_transaction_contextmanager_explicit_rollback(self): + context = self._fixture( + {"transaction_per_migration": True, "transactional_ddl": True} + ) + proxy = context.begin_transaction(_per_migration=True) + is_true(self.conn.in_transaction()) + + with proxy: + is_true(self.conn.in_transaction()) + proxy.rollback() + is_false(self.conn.in_transaction()) + + is_false(self.conn.in_transaction()) + + def test_proxy_transaction_contextmanager_explicit_commit(self): + context = self._fixture( + {"transaction_per_migration": True, "transactional_ddl": True} + ) + proxy = context.begin_transaction(_per_migration=True) + is_true(self.conn.in_transaction()) + + with proxy: + is_true(self.conn.in_transaction()) + proxy.commit() + is_false(self.conn.in_transaction()) + + is_false(self.conn.in_transaction()) + + def test_transaction_per_migration_transactional_ddl(self): + context = self._fixture( + {"transaction_per_migration": True, "transactional_ddl": True} + ) + + is_false(self.conn.in_transaction()) + + with context.begin_transaction(): + is_false(self.conn.in_transaction()) + with context.begin_transaction(_per_migration=True): + is_true(self.conn.in_transaction()) + + is_false(self.conn.in_transaction()) + is_false(self.conn.in_transaction()) + + def test_transaction_per_migration_non_transactional_ddl(self): + context = self._fixture( + {"transaction_per_migration": True, "transactional_ddl": False} + ) + + is_false(self.conn.in_transaction()) + + with context.begin_transaction(): + is_false(self.conn.in_transaction()) + with context.begin_transaction(_per_migration=True): + is_true(self.conn.in_transaction()) + + is_false(self.conn.in_transaction()) + is_false(self.conn.in_transaction()) + + def test_transaction_per_all_transactional_ddl(self): + context = self._fixture({"transactional_ddl": True}) + + is_false(self.conn.in_transaction()) + + with context.begin_transaction(): + is_true(self.conn.in_transaction()) + with context.begin_transaction(_per_migration=True): + is_true(self.conn.in_transaction()) + + is_true(self.conn.in_transaction()) + is_false(self.conn.in_transaction()) + + def test_transaction_per_all_non_transactional_ddl(self): + context = self._fixture({"transactional_ddl": False}) + + is_false(self.conn.in_transaction()) + + with context.begin_transaction(): + is_false(self.conn.in_transaction()) + with context.begin_transaction(_per_migration=True): + is_true(self.conn.in_transaction()) + + is_false(self.conn.in_transaction()) + is_false(self.conn.in_transaction()) + + def test_transaction_per_all_sqlmode(self): + context = self._fixture({"as_sql": True}) + + context.execute("step 1") + with context.begin_transaction(): + context.execute("step 2") + with context.begin_transaction(_per_migration=True): + context.execute("step 3") + + context.execute("step 4") + context.execute("step 5") + + if context.impl.transactional_ddl: + self._assert_impl_steps( + "step 1", + "BEGIN", + "step 2", + "step 3", + "step 4", + "COMMIT", + "step 5", + ) + else: + self._assert_impl_steps( + "step 1", "step 2", "step 3", "step 4", "step 5" + ) + + def test_transaction_per_migration_sqlmode(self): + context = self._fixture( + {"as_sql": True, "transaction_per_migration": True} + ) + + context.execute("step 1") + with context.begin_transaction(): + context.execute("step 2") + with context.begin_transaction(_per_migration=True): + context.execute("step 3") + + context.execute("step 4") + context.execute("step 5") + + if context.impl.transactional_ddl: + self._assert_impl_steps( + "step 1", + "step 2", + "BEGIN", + "step 3", + "COMMIT", + "step 4", + "step 5", + ) + else: + self._assert_impl_steps( + "step 1", "step 2", "step 3", "step 4", "step 5" + ) + + @config.requirements.autocommit_isolation + def test_autocommit_block(self): + context = self._fixture({"transaction_per_migration": True}) + + is_false(self.conn.in_transaction()) + + with context.begin_transaction(): + is_false(self.conn.in_transaction()) + with context.begin_transaction(_per_migration=True): + is_true(self.conn.in_transaction()) + + with context.autocommit_block(): + # in 1.x, self.conn is separate due to the + # execution_options call. however for future they are the + # same connection and there is a "transaction" block + # despite autocommit + if self.is_sqlalchemy_future: + is_(context.connection, self.conn) + else: + is_not_(context.connection, self.conn) + is_false(self.conn.in_transaction()) + + eq_( + context.connection._execution_options[ + "isolation_level" + ], + "AUTOCOMMIT", + ) + + ne_( + context.connection._execution_options.get( + "isolation_level", None + ), + "AUTOCOMMIT", + ) + is_true(self.conn.in_transaction()) + + is_false(self.conn.in_transaction()) + is_false(self.conn.in_transaction()) + + @config.requirements.autocommit_isolation + def test_autocommit_block_no_transaction(self): + context = self._fixture({"transaction_per_migration": True}) + + is_false(self.conn.in_transaction()) + + with context.autocommit_block(): + is_true(context.connection.in_transaction()) + + # in 1.x, self.conn is separate due to the execution_options + # call. however for future they are the same connection and there + # is a "transaction" block despite autocommit + if self.is_sqlalchemy_future: + is_(context.connection, self.conn) + else: + is_not_(context.connection, self.conn) + is_false(self.conn.in_transaction()) + + eq_( + context.connection._execution_options["isolation_level"], + "AUTOCOMMIT", + ) + + ne_( + context.connection._execution_options.get("isolation_level", None), + "AUTOCOMMIT", + ) + + is_false(self.conn.in_transaction()) + + def test_autocommit_block_transactional_ddl_sqlmode(self): + context = self._fixture( + { + "transaction_per_migration": True, + "transactional_ddl": True, + "as_sql": True, + } + ) + + with context.begin_transaction(): + context.execute("step 1") + with context.begin_transaction(_per_migration=True): + context.execute("step 2") + + with context.autocommit_block(): + context.execute("step 3") + + context.execute("step 4") + + context.execute("step 5") + + self._assert_impl_steps( + "step 1", + "BEGIN", + "step 2", + "COMMIT", + "step 3", + "BEGIN", + "step 4", + "COMMIT", + "step 5", + ) + + def test_autocommit_block_nontransactional_ddl_sqlmode(self): + context = self._fixture( + { + "transaction_per_migration": True, + "transactional_ddl": False, + "as_sql": True, + } + ) + + with context.begin_transaction(): + context.execute("step 1") + with context.begin_transaction(_per_migration=True): + context.execute("step 2") + + with context.autocommit_block(): + context.execute("step 3") + + context.execute("step 4") + + context.execute("step 5") + + self._assert_impl_steps( + "step 1", "step 2", "step 3", "step 4", "step 5" + ) + + def _assert_impl_steps(self, *steps): + to_check = self.context.output_buffer.getvalue() + + self.context.impl.output_buffer = buf = io.StringIO() + for step in steps: + if step == "BEGIN": + self.context.impl.emit_begin() + elif step == "COMMIT": + self.context.impl.emit_commit() + else: + self.context.impl._exec(step) + + eq_(to_check, buf.getvalue()) diff --git a/venv/lib/python3.12/site-packages/alembic/testing/suite/test_op.py b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_op.py new file mode 100644 index 00000000..a63b3f2f --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/suite/test_op.py @@ -0,0 +1,42 @@ +"""Test against the builders in the op.* module.""" + +from sqlalchemy import Column +from sqlalchemy import event +from sqlalchemy import Integer +from sqlalchemy import String +from sqlalchemy import Table +from sqlalchemy.sql import text + +from ...testing.fixtures import AlterColRoundTripFixture +from ...testing.fixtures import TestBase + + +@event.listens_for(Table, "after_parent_attach") +def _add_cols(table, metadata): + if table.name == "tbl_with_auto_appended_column": + table.append_column(Column("bat", Integer)) + + +class BackendAlterColumnTest(AlterColRoundTripFixture, TestBase): + __backend__ = True + + def test_rename_column(self): + self._run_alter_col({}, {"name": "newname"}) + + def test_modify_type_int_str(self): + self._run_alter_col({"type": Integer()}, {"type": String(50)}) + + def test_add_server_default_int(self): + self._run_alter_col({"type": Integer}, {"server_default": text("5")}) + + def test_modify_server_default_int(self): + self._run_alter_col( + {"type": Integer, "server_default": text("2")}, + {"server_default": text("5")}, + ) + + def test_modify_nullable_to_non(self): + self._run_alter_col({}, {"nullable": False}) + + def test_modify_non_nullable_to_nullable(self): + self._run_alter_col({"nullable": False}, {"nullable": True}) diff --git a/venv/lib/python3.12/site-packages/alembic/testing/util.py b/venv/lib/python3.12/site-packages/alembic/testing/util.py new file mode 100644 index 00000000..4517a69f --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/util.py @@ -0,0 +1,126 @@ +# testing/util.py +# Copyright (C) 2005-2019 the SQLAlchemy authors and contributors +# +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +from __future__ import annotations + +import types +from typing import Union + +from sqlalchemy.util import inspect_getfullargspec + +from ..util import sqla_2 + + +def flag_combinations(*combinations): + """A facade around @testing.combinations() oriented towards boolean + keyword-based arguments. + + Basically generates a nice looking identifier based on the keywords + and also sets up the argument names. + + E.g.:: + + @testing.flag_combinations( + dict(lazy=False, passive=False), + dict(lazy=True, passive=False), + dict(lazy=False, passive=True), + dict(lazy=False, passive=True, raiseload=True), + ) + + + would result in:: + + @testing.combinations( + ('', False, False, False), + ('lazy', True, False, False), + ('lazy_passive', True, True, False), + ('lazy_passive', True, True, True), + id_='iaaa', + argnames='lazy,passive,raiseload' + ) + + """ + from sqlalchemy.testing import config + + keys = set() + + for d in combinations: + keys.update(d) + + keys = sorted(keys) + + return config.combinations( + *[ + ("_".join(k for k in keys if d.get(k, False)),) + + tuple(d.get(k, False) for k in keys) + for d in combinations + ], + id_="i" + ("a" * len(keys)), + argnames=",".join(keys), + ) + + +def resolve_lambda(__fn, **kw): + """Given a no-arg lambda and a namespace, return a new lambda that + has all the values filled in. + + This is used so that we can have module-level fixtures that + refer to instance-level variables using lambdas. + + """ + + pos_args = inspect_getfullargspec(__fn)[0] + pass_pos_args = {arg: kw.pop(arg) for arg in pos_args} + glb = dict(__fn.__globals__) + glb.update(kw) + new_fn = types.FunctionType(__fn.__code__, glb) + return new_fn(**pass_pos_args) + + +def metadata_fixture(ddl="function"): + """Provide MetaData for a pytest fixture.""" + + from sqlalchemy.testing import config + from . import fixture_functions + + def decorate(fn): + def run_ddl(self): + from sqlalchemy import schema + + metadata = self.metadata = schema.MetaData() + try: + result = fn(self, metadata) + metadata.create_all(config.db) + # TODO: + # somehow get a per-function dml erase fixture here + yield result + finally: + metadata.drop_all(config.db) + + return fixture_functions.fixture(scope=ddl)(run_ddl) + + return decorate + + +def _safe_int(value: str) -> Union[int, str]: + try: + return int(value) + except: + return value + + +def testing_engine(url=None, options=None, future=False): + from sqlalchemy.testing import config + from sqlalchemy.testing.engines import testing_engine + + if not future: + future = getattr(config._current.options, "future_engine", False) + + if not sqla_2: + kw = {"future": future} if future else {} + else: + kw = {} + return testing_engine(url, options, **kw) diff --git a/venv/lib/python3.12/site-packages/alembic/testing/warnings.py b/venv/lib/python3.12/site-packages/alembic/testing/warnings.py new file mode 100644 index 00000000..86d45a0d --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/testing/warnings.py @@ -0,0 +1,31 @@ +# testing/warnings.py +# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors +# +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + + +import warnings + +from sqlalchemy import exc as sa_exc + + +def setup_filters(): + """Set global warning behavior for the test suite.""" + + warnings.resetwarnings() + + warnings.filterwarnings("error", category=sa_exc.SADeprecationWarning) + warnings.filterwarnings("error", category=sa_exc.SAWarning) + + # some selected deprecations... + warnings.filterwarnings("error", category=DeprecationWarning) + try: + import pytest + except ImportError: + pass + else: + warnings.filterwarnings( + "once", category=pytest.PytestDeprecationWarning + ) diff --git a/venv/lib/python3.12/site-packages/alembic/util/__init__.py b/venv/lib/python3.12/site-packages/alembic/util/__init__.py new file mode 100644 index 00000000..8f3f685b --- /dev/null +++ b/venv/lib/python3.12/site-packages/alembic/util/__init__.py @@ -0,0 +1,33 @@ +from .editor import open_in_editor as open_in_editor +from .exc import AutogenerateDiffsDetected as AutogenerateDiffsDetected +from .exc import CommandError as CommandError +from .exc import DatabaseNotAtHead as DatabaseNotAtHead +from .langhelpers import _with_legacy_names as _with_legacy_names +from .langhelpers import asbool as asbool +from .langhelpers import dedupe_tuple as dedupe_tuple +from .langhelpers import Dispatcher as Dispatcher +from .langhelpers import DispatchPriority as DispatchPriority +from .langhelpers import EMPTY_DICT as EMPTY_DICT +from .langhelpers import immutabledict as immutabledict +from .langhelpers import memoized_property as memoized_property +from .langhelpers import ModuleClsProxy as ModuleClsProxy +from .langhelpers import not_none as not_none +from .langhelpers import PriorityDispatcher as PriorityDispatcher +from .langhelpers import PriorityDispatchResult as PriorityDispatchResult +from .langhelpers import rev_id as rev_id +from .langhelpers import to_list as to_list +from .langhelpers import to_tuple as to_tuple +from .langhelpers import unique_list as unique_list +from .messaging import err as err +from .messaging import format_as_comma as format_as_comma +from .messaging import msg as msg +from .messaging import obfuscate_url_pw as obfuscate_url_pw +from .messaging import status as status +from .messaging import warn as warn +from .messaging import warn_deprecated as warn_deprecated +from .messaging import write_outstream as write_outstream +from .pyfiles import coerce_resource_to_filename as coerce_resource_to_filename +from .pyfiles import load_python_file as load_python_file +from .pyfiles import pyc_file_from_path as pyc_file_from_path +from .pyfiles import template_to_file as template_to_file +from .sqla_compat import sqla_2 as sqla_2 diff --git a/venv/lib/python3.12/site-packages/alembic/util/__pycache__/__init__.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/util/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2cd78f8712a126ef8b51e9eef792aeb9699b4361 GIT binary patch literal 1465 zcmZ9M%WfMt6ox5FmSpQL-|v>4I1mCwNYbJRdV^sFZ5Bz5BAc=cf}WwIK{&$+$+4pB z`Uc(g8Tu@J0|C=6y6Ub)c3t(H6Km61{QUUy3=et8nco_Xbq3E*7k@puM)57a$MmR;xcfRSBWdYYkZBk z3cSwOiPwN@yhgkZT<3M-8gPR*i0i;j-Xv}Sw|I-V3Ebvw;udg+cZl1-UEU?`0B`UO z;x6ze-z44u-r`%to50(An|Mp_#JhZ#cpG?+?-B0+@AG})UEl+LK)eTh$PbD4fsgnR z@d5BLKPEl|KH(?CNBT7G@gDIp@EJcNJ^?=G=ftPL_xL^Hp1vPn@QVVQpY_lGf>UAr z!lG$Xm53-2DvZ3b;Ot#y-b_@Ytn_LWjmK`Jyb8PuVX%H>;#el(b89KMGm>83NT*&I zf9bzf5`(&87Wt{rY9fQVNMx)WxMk^XjL|qrsKP8&!e^;g&|e=#E|or*D$L(r_O5MY zY~<${@%-iW>mS7E#no%*HKI7qAgzWq2Of-0tYQU3d3+Y6Fl>@67y9qRHb2H9d05ukw)gqmNS9V_d42xBoNt;01!d8STwF*Nqe`^Ls zQNm>2d8=d$)9FD2d|sX17b0@CKgcsDMoq~ zryAFEHv@QO_f|{sK{qWtla*dpAWMNz#AYzfG&qQLjh7BwxC$y8}L17|B}S5ekb z@K<4LD0P$uN)x4p(njf^bWt`?Hc_@vwm}waaF3jvM9IYNpt*~(hq8}yfO3d(gmMhB zsHAhOgR>`Souc$m&OjD5+87)QY~G%O^DFxR%KrUvI5YMQT**MWVPHZv!23Kvx-)N8 zay!(~&G2JUpFVp0Wa!{j2Jp@sIf2)tR`E>~3^O0;p@0u8@;Q7% zSLmi7e-{gdLe4(P**7^Gpyg86mMo>2gdzMz1I)P0(>9u0wh Yp0fuzJEtxLe@6f_Jo}yX?L$cRAK?X^-2eap literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/util/__pycache__/compat.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/util/__pycache__/compat.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b5eb78bc859571ea0f7bff4cccd87971fbf74a2b GIT binary patch literal 5221 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 literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/util/__pycache__/editor.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/util/__pycache__/editor.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b553cb8663bebb98bca04c46fcf63c27343679c0 GIT binary patch literal 3223 zcmY*bO>7&-6`tWPS4&b7m-@G4HzTQbOxu!TyG3l-382Uh;#!r1$Vm{o2DRE9Nh>XP ziPh?xk;Lmz1&+ zXWqQ`=DnFW-+MEEZ)r&)_@d?G$+`AApZ8{`sR6IU>@qXZ zcdo>;-QVzyWpR+w9mn-`-*O#q62=N$wmjbhN?vd+2YS^j+Llk3eV~)ZJT(^chHl$j z!#pl|xAUMyP99FXdrA~&hq-(c|b6d0D5FS?$!48rDdw@}a>^Cn|1Gh%!t z(WgKUA;*W?Ks-hjysr6?2%&}Jni1E*6}%QpAU|GLtHhphm+-Agep5&xRFNuZt|7r_ z1rH)$txJE1Sm&DTr|M%fR*`>;P3cpqZmS}H7&Gyj{BwB;FQH}W1GI#*vi=uvl_c=p zDN@HH{yZgfR+&0v*0L$F9HJ8*IYLd#ciAwxH7j(eQ?{7v6sY5qvd%1h#-_r|^z_Xe zx9;9~XL_1g9`UGu0rY`_e_vk&yShh;jLuR<9fJ-hL*L+*EmsvpHb$rKOo@{cYhpS$s!ts) z>P#<)%{qF4j*`0&0KRoTBECyp2+J^e-?D8oLrGEhU@jqL%4Ps@1+D>W7n|Tg45K8L z4(J#b{K!QVu~9PSfYA_I#AI%92!hOL(bf$r!a3_QGCl2j!_kcC>0$CNcx^Z-=oSwP z*Ym0816)H#ldC;qmxL};qs003^_&fGHmd|N*9)2=t3h*q)^f~zxMId(79?&i8&oWf zrD2?n2WmYOteq=JFd6boKmi8JhQ~q6H%?xBFtX&bMXw0k@J0;Rq(e)VKR?8&hzY1F zkJ#4CNO8rVcb&__7cY%?ke5R+XHlPnX}V1dGnO#|i?>H2#1&V9R(=S1%c(E651eM; z=e+~XI;s(Lf^4N94m>*d;M|k<*T-uyIX;2+GpDwUz0AOdvahynzPa_*PW$Q4)ieLy zPj!5YFen_f_Wo&nr*CYp^^LzuJL>KK)g;b-;dOx7U-yil&k|!T(jT=kRr<3U2hxbr z62u7cor^?AzM9RpxQm~TpQR=XC9S5E^pm? zo<3XEdcV~AceVcQ{_T68YZq%RsQuERhPuc`Vn6-TcH+sad+Ey?ay6CSP4!jVyC2-H zHG%Xy6{Xs0F`W4Ep(Lf08j|A5AuI`f@49NBH~pDU&;2}bJ^Lvak`x5w_j6`-ujT7k)UN(Is!^@F&0DTMfG5{FES zjPFd~Dl0R7vH~yc9G?^~>voBH9CREBAq)n&4sTwApJza`j{cKG%^jbp+b8yV&OaI3 z?HT^Vndd!Ms%mEQy8Lg?PS!3tH5BJshc%n=ECGrCxjvpDr`(Yuy9#`E1sX6e4wBR1 zX9hKid@~^ZD5lc%=C}g9u0UrU|ZW4wFaC^1Uz58GBj|fVSIoFzD9`y)O&z>4$#R1)PI1w#XoTR{>?e307X}>*Q8E7 zfvc^Vh&cJ9koYQ|IE-`A;VT)e)Xt``R!z6nB;KDI7X4T8w!=7#zdNbm>*5su4@mA8 Al>h($ literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/util/__pycache__/exc.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/util/__pycache__/exc.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..877bcc0fcf26937d5a0f26b61ae4ecd4257f126a GIT binary patch literal 1795 zcmb7E&1)M+6ra(4XeCRIT^#4bq-;{sqEfHyHqgeD5Z5tDXb~k1g>DGz_3p@8yxLV~ zMz&>qNWoC&5GaM>9D5D@ugdTcRYL$kb`rdwoxNQy{p*O#IGjHd;-@NyJ zsMo6m##e9t96V77d5Vi-Xc?e~-vZbtKJjTty40o=aVacy6*><3TUgW`a(OFYR5oZdr4ax)Enc! zp;?0_V{R11(v?9R30NwN(Euj(lR!wAwVVB3$NEr9ItDrb`gKY!? ztd`=Q<8>Jz)_37_2sX1KEkA_pJGMn<9{G7HrntH868S)?E#} zW?u$j6I`--1CDQ>iW&fc@4ZVBwaF(7{r_TQ@`@|nRp6l&%SCyOx&HP~*CRjhT*-tf zJIwU+17;vhcrnwJrq}13MbhlRag*ooA*^cEgmJ?S1oO?+L2A9w?nmCjij}i$<)$ky zXy5Fx2v4TA3q6;+UC!G7wy?Yo^VS`_QmmNiIVKrLuljIbez|3t8;oOmxW3Q)g#~lo znt$J#&s=NqF(e6-BctmzL{o)8y(oj&CBK)-nvtEs?IV=tsZS3tz|PnINV5t#`ty}N z(x!fCw`Qw*WQ*F`XQZJfrxyD%USkpC!1I-$-4<6M8c#BRyPFf36652Rxyd-ZLw?|u z%pyN4DUAi$S=9f<$wg}}+w@cjq8Jz%{ORFk0N>GlB8wezSKT4^AlY{4!=lK@PHBfY zrQ*nr0;`-GLIzOdEeKQ-{T}03KtU8pD4K~PUmPzh;6zJ{u`Hoq3ho^6M3bwb;y^>6;xb_V=C(eR+eus=|q%wZD{G)O5{)tD% z*~jMGp8Ci*^Z5AbJ@sBSee{OG&)~(1kUaClfMx2;_Hk@cx6=M}3!2tDre#^TZznou zvd^PLZN(95=p3t)*HMh4IEDhfP3n0t9t%UqX;A(;Xa$mbIg4o;kD|cX#Rnjs?~-RF zO*4k$hBoudiC~yfSwT$SpwsyT8%oku67@Mgx1CJ zof2IRdO@_tF$Fj_60W1b3P`5%)?>LZB`QZ2lFD+?8?#=N)=%p6(ZWiM53^QziN6JQ z7@@cXVyI9`56HP+$@Bp^aX?NU5c7#LaiCrp>6FfWb8$pq8W{#P?$<^HrqT2So%z1@ J2Z1S#>2Ep3#AW~h literal 0 HcmV?d00001 diff --git a/venv/lib/python3.12/site-packages/alembic/util/__pycache__/langhelpers.cpython-312.pyc b/venv/lib/python3.12/site-packages/alembic/util/__pycache__/langhelpers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6b22dea4976f23214980d7fab907de0bcaa13c9f GIT binary patch literal 16881 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