tina4-python 3.13.54__tar.gz → 3.13.56__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tina4_python-3.13.54 → tina4_python-3.13.56}/PKG-INFO +1 -1
- {tina4_python-3.13.54 → tina4_python-3.13.56}/pyproject.toml +1 -1
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/ai/__init__.py +5 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/migration/runner.py +59 -41
- {tina4_python-3.13.54 → tina4_python-3.13.56}/.gitignore +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/README.md +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/CLAUDE.md +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/Testing.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/api/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/auth/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/cache/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/cli/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/container/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/core/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/core/cache.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/core/constants.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/core/events.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/core/middleware.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/core/rate_limiter.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/core/request.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/core/response.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/core/router.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/core/server.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/crud/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/database/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/database/adapter.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/database/connection.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/database/firebird.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/database/mongodb.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/database/mssql.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/database/mysql.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/database/odbc.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/database/postgres.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/database/sqlite.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/debug/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/debug/error_overlay.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/dev_admin/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/dev_admin/metrics.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/dev_admin/plan.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/dev_admin/project_index.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/docs.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/docstore/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/dotenv/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/env.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/frond/FROND.md +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/frond/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/frond/engine.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/auth/meta.json +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/database/meta.json +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/error-overlay/meta.json +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/orm/meta.json +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/queue/meta.json +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/rest-api/meta.json +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/templates/meta.json +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/graphql/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/i18n/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/mcp/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/mcp/protocol.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/mcp/tools.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/messenger/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/migration/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/orm/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/orm/fields.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/orm/model.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/__feedback/widget.js +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/css/tina4.css +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/css/tina4.min.css +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/images/logo.svg +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/js/frond.js +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/js/frond.min.js +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/js/tina4-dev-admin.js +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/js/tina4.min.js +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/js/tina4js.min.js +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/query_builder/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/queue/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/queue/job.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/queue/kafka_backend.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/queue/lite_backend.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/queue/mongo_backend.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/queue/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/queue_backends/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/queue_backends/kafka_backend.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/queue_backends/mongo_backend.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_alerts.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_badges.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_buttons.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_cards.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_forms.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_grid.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_modals.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_nav.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_reset.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_tables.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_typography.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_utilities.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/_variables.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/base.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/colors.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/scss/tina4css/tina4.scss +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/seeder/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/service/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/session/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/session_handlers/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/session_handlers/mongodb_handler.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/session_handlers/redis_handler.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/session_handlers/valkey_handler.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/swagger/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/components/crud.twig +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/docker/python/Dockerfile +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/docker/uv/Dockerfile +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/errors/302.twig +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/errors/401.twig +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/errors/502.twig +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/errors/503.twig +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/errors/base.twig +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/frontend/README.md +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/readme.md +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/test/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/test_client/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/validator/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/websocket/__init__.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/websocket/backplane.py +0 -0
- {tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/wsdl/__init__.py +0 -0
|
@@ -200,6 +200,9 @@ def _skill_block(context_file: str) -> str:
|
|
|
200
200
|
"- **tina4-developer** — Read `.claude/skills/tina4-developer/SKILL.md` before building features.\n"
|
|
201
201
|
"- **tina4-js** — Read `.claude/skills/tina4-js/SKILL.md` for frontend work.\n"
|
|
202
202
|
"- **tina4-maintainer** — Read `.claude/skills/tina4-maintainer/SKILL.md` for framework-level changes.\n\n"
|
|
203
|
+
"If Tina4 behaves differently from what these skills describe, that is a bug in the skill. "
|
|
204
|
+
"Tell the developer, then report it at https://tina4.com/report-a-skill "
|
|
205
|
+
"(or open an issue on the matching tina4stack/* GitHub repo).\n\n"
|
|
203
206
|
"See https://tina4.com for full docs."
|
|
204
207
|
)
|
|
205
208
|
else:
|
|
@@ -208,6 +211,8 @@ def _skill_block(context_file: str) -> str:
|
|
|
208
211
|
" .claude/skills/tina4-developer/SKILL.md (feature development)\n"
|
|
209
212
|
" .claude/skills/tina4-js/SKILL.md (frontend / tina4-js)\n"
|
|
210
213
|
" .claude/skills/tina4-maintainer/SKILL.md (framework-level changes)\n"
|
|
214
|
+
"Found a skill that disagrees with how Tina4 actually behaves? Tell the developer,\n"
|
|
215
|
+
"then report it at https://tina4.com/report-a-skill\n"
|
|
211
216
|
"Docs: https://tina4.com"
|
|
212
217
|
)
|
|
213
218
|
return f"{start}\n{body}\n{end}"
|
|
@@ -87,7 +87,7 @@ def _create_v3_table(db) -> None:
|
|
|
87
87
|
db.execute("""
|
|
88
88
|
CREATE TABLE tina4_migration (
|
|
89
89
|
id INTEGER NOT NULL PRIMARY KEY,
|
|
90
|
-
|
|
90
|
+
migration_name VARCHAR(500) NOT NULL UNIQUE,
|
|
91
91
|
description VARCHAR(500),
|
|
92
92
|
batch INTEGER DEFAULT 1 NOT NULL,
|
|
93
93
|
executed_at VARCHAR(50) NOT NULL,
|
|
@@ -98,7 +98,7 @@ def _create_v3_table(db) -> None:
|
|
|
98
98
|
# Engine-aware bookkeeping DDL. Each engine spells an auto-increment
|
|
99
99
|
# integer PK differently (SQLite AUTOINCREMENT, PostgreSQL SERIAL, MySQL
|
|
100
100
|
# AUTO_INCREMENT, MSSQL IDENTITY), and a TEXT column cannot carry a UNIQUE
|
|
101
|
-
# constraint on MySQL -- so
|
|
101
|
+
# constraint on MySQL -- so migration_name is VARCHAR. (SQLite gives VARCHAR
|
|
102
102
|
# TEXT affinity, so this stays behaviour-identical there.) Mirrors the
|
|
103
103
|
# engine-aware DDL in ORM.create_table; without it `migrate()` died with
|
|
104
104
|
# "syntax error at AUTOINCREMENT" on PostgreSQL/MySQL/MSSQL.
|
|
@@ -111,7 +111,7 @@ def _create_v3_table(db) -> None:
|
|
|
111
111
|
db.execute(f"""
|
|
112
112
|
CREATE TABLE tina4_migration (
|
|
113
113
|
{id_column},
|
|
114
|
-
|
|
114
|
+
migration_name VARCHAR(500) NOT NULL UNIQUE,
|
|
115
115
|
description VARCHAR(500),
|
|
116
116
|
batch INTEGER NOT NULL DEFAULT 1,
|
|
117
117
|
executed_at VARCHAR(50) NOT NULL,
|
|
@@ -136,7 +136,7 @@ def _build_stem_map(migration_folder: str | None) -> list[str]:
|
|
|
136
136
|
"""Scan the migrations folder and return a list of file stems.
|
|
137
137
|
|
|
138
138
|
Used by the v2→v3 backfill to match a v2 row's `description` to the
|
|
139
|
-
actual file on disk, so the resulting `
|
|
139
|
+
actual file on disk, so the resulting `migration_name` matches what
|
|
140
140
|
`migrate()` will compare against next.
|
|
141
141
|
"""
|
|
142
142
|
if not migration_folder:
|
|
@@ -153,7 +153,7 @@ def _build_stem_map(migration_folder: str | None) -> list[str]:
|
|
|
153
153
|
return stems
|
|
154
154
|
|
|
155
155
|
|
|
156
|
-
def
|
|
156
|
+
def _resolve_migration_name(description: str, stems: list[str]) -> str:
|
|
157
157
|
"""Find the file stem that corresponds to a v2 description, or fall back.
|
|
158
158
|
|
|
159
159
|
Match strategy, in order:
|
|
@@ -179,11 +179,11 @@ def _resolve_migration_id(description: str, stems: list[str]) -> str:
|
|
|
179
179
|
def _upgrade_v2_to_v3(db, migration_folder: str | None) -> None:
|
|
180
180
|
"""In-place upgrade of a Python-v2 tina4_migration table to v3.
|
|
181
181
|
|
|
182
|
-
1. ALTER TABLE ADD the v3 columns (
|
|
182
|
+
1. ALTER TABLE ADD the v3 columns (migration_name, batch, executed_at)
|
|
183
183
|
alongside the existing v2 columns. v2 columns stay in place so a
|
|
184
184
|
manual rollback path remains open; they are simply ignored from
|
|
185
185
|
now on.
|
|
186
|
-
2. Backfill every v2 row's
|
|
186
|
+
2. Backfill every v2 row's migration_name by matching `description`
|
|
187
187
|
against the on-disk file stems. Falls back to the description
|
|
188
188
|
verbatim when no file matches. All legacy rows get batch=1 and
|
|
189
189
|
executed_at='legacy'.
|
|
@@ -204,13 +204,13 @@ def _upgrade_v2_to_v3(db, migration_folder: str | None) -> None:
|
|
|
204
204
|
|
|
205
205
|
cols = _column_names(db)
|
|
206
206
|
|
|
207
|
-
if "
|
|
207
|
+
if "migration_name" not in cols:
|
|
208
208
|
col_type = "VARCHAR(500)" if _is_firebird(db) else "TEXT"
|
|
209
209
|
try:
|
|
210
|
-
db.execute(f"ALTER TABLE tina4_migration ADD
|
|
210
|
+
db.execute(f"ALTER TABLE tina4_migration ADD migration_name {col_type}")
|
|
211
211
|
db.commit()
|
|
212
212
|
except Exception as e:
|
|
213
|
-
logger.debug(f"v2→v3 upgrade: ALTER ADD
|
|
213
|
+
logger.debug(f"v2→v3 upgrade: ALTER ADD migration_name skipped: {e}")
|
|
214
214
|
|
|
215
215
|
if "batch" not in cols:
|
|
216
216
|
try:
|
|
@@ -234,7 +234,7 @@ def _upgrade_v2_to_v3(db, migration_folder: str | None) -> None:
|
|
|
234
234
|
|
|
235
235
|
try:
|
|
236
236
|
rows = db.fetch(
|
|
237
|
-
"SELECT description, passed FROM tina4_migration WHERE
|
|
237
|
+
"SELECT description, passed FROM tina4_migration WHERE migration_name IS NULL",
|
|
238
238
|
limit=100000,
|
|
239
239
|
).records
|
|
240
240
|
except Exception as e:
|
|
@@ -249,13 +249,13 @@ def _upgrade_v2_to_v3(db, migration_folder: str | None) -> None:
|
|
|
249
249
|
if not desc:
|
|
250
250
|
continue
|
|
251
251
|
|
|
252
|
-
|
|
252
|
+
migration_name = _resolve_migration_name(desc, stems)
|
|
253
253
|
|
|
254
254
|
try:
|
|
255
255
|
db.execute(
|
|
256
|
-
"UPDATE tina4_migration SET
|
|
257
|
-
"WHERE description = ? AND
|
|
258
|
-
[
|
|
256
|
+
"UPDATE tina4_migration SET migration_name = ?, batch = 1 "
|
|
257
|
+
"WHERE description = ? AND migration_name IS NULL",
|
|
258
|
+
[migration_name, desc],
|
|
259
259
|
)
|
|
260
260
|
db.commit()
|
|
261
261
|
backfilled += 1
|
|
@@ -277,8 +277,8 @@ def _upgrade_v2_to_v3(db, migration_folder: str | None) -> None:
|
|
|
277
277
|
def _ensure_tracking_table(db, migration_folder: str | None = None):
|
|
278
278
|
"""Create or upgrade the migration tracking table.
|
|
279
279
|
|
|
280
|
-
Handles v2→v3 upgrade: v2 tables have `description` but no `
|
|
281
|
-
When detected, adds the missing v3 columns and backfills `
|
|
280
|
+
Handles v2→v3 upgrade: v2 tables have `description` but no `migration_name`.
|
|
281
|
+
When detected, adds the missing v3 columns and backfills `migration_name`
|
|
282
282
|
by matching `description` against the on-disk file stems so subsequent
|
|
283
283
|
`migrate()` calls do not re-apply already-applied migrations.
|
|
284
284
|
|
|
@@ -289,8 +289,26 @@ def _ensure_tracking_table(db, migration_folder: str | None = None):
|
|
|
289
289
|
return
|
|
290
290
|
|
|
291
291
|
cols = _column_names(db)
|
|
292
|
+
|
|
293
|
+
# In-place rename for a table created by an older v3 (<= 3.13.54), which
|
|
294
|
+
# named the column `migration_id`. The canonical name is now
|
|
295
|
+
# `migration_name` (unified across all four frameworks). Add the new column
|
|
296
|
+
# and copy the values across so already-applied migrations are still seen as
|
|
297
|
+
# applied — without this the `migration_name`-based read below finds nothing
|
|
298
|
+
# and every migration re-runs. The old `migration_id` column is left in
|
|
299
|
+
# place (harmless, ignored from now on).
|
|
300
|
+
if "migration_id" in cols and "migration_name" not in cols:
|
|
301
|
+
col_type = "VARCHAR(500)" if _is_firebird(db) else "TEXT"
|
|
302
|
+
try:
|
|
303
|
+
db.execute(f"ALTER TABLE tina4_migration ADD migration_name {col_type}")
|
|
304
|
+
db.execute("UPDATE tina4_migration SET migration_name = migration_id")
|
|
305
|
+
db.commit()
|
|
306
|
+
cols = _column_names(db)
|
|
307
|
+
except Exception as e:
|
|
308
|
+
logger.debug(f"v3 migration_id->migration_name rename skipped: {e}")
|
|
309
|
+
|
|
292
310
|
has_v3_shape = (
|
|
293
|
-
"
|
|
311
|
+
"migration_name" in cols
|
|
294
312
|
and "batch" in cols
|
|
295
313
|
and "executed_at" in cols
|
|
296
314
|
)
|
|
@@ -302,12 +320,12 @@ def _get_executed(db) -> set[str]:
|
|
|
302
320
|
"""Get set of already-executed migration IDs."""
|
|
303
321
|
try:
|
|
304
322
|
result = db.fetch(
|
|
305
|
-
"SELECT
|
|
323
|
+
"SELECT migration_name FROM tina4_migration WHERE passed = 1",
|
|
306
324
|
limit=10000,
|
|
307
325
|
)
|
|
308
|
-
return {row["
|
|
326
|
+
return {row["migration_name"] for row in result.records if row.get("migration_name")}
|
|
309
327
|
except Exception:
|
|
310
|
-
# Fallback for v2 tables where
|
|
328
|
+
# Fallback for v2 tables where migration_name may not exist yet
|
|
311
329
|
result = db.fetch(
|
|
312
330
|
"SELECT description FROM tina4_migration WHERE passed = 1",
|
|
313
331
|
limit=10000,
|
|
@@ -647,9 +665,9 @@ def _migrate(db, migration_folder: str = "migrations", delimiter: str = ";") ->
|
|
|
647
665
|
)
|
|
648
666
|
|
|
649
667
|
for mig_file in migration_files:
|
|
650
|
-
|
|
668
|
+
migration_name = mig_file.stem # e.g., "000001_create_users_table"
|
|
651
669
|
|
|
652
|
-
if
|
|
670
|
+
if migration_name in executed:
|
|
653
671
|
continue
|
|
654
672
|
|
|
655
673
|
try:
|
|
@@ -672,18 +690,18 @@ def _migrate(db, migration_folder: str = "migrations", delimiter: str = ";") ->
|
|
|
672
690
|
|
|
673
691
|
# Record as passed
|
|
674
692
|
now = datetime.now(timezone.utc).isoformat()
|
|
675
|
-
desc = re.sub(r"^\d+_", "",
|
|
693
|
+
desc = re.sub(r"^\d+_", "", migration_name, count=1).replace("_", " ")
|
|
676
694
|
if _is_firebird(db):
|
|
677
695
|
row = db.fetch_one("SELECT GEN_ID(GEN_TINA4_MIGRATION_ID, 1) AS next_id FROM RDB$DATABASE")
|
|
678
696
|
next_id = row["next_id"] if row else 1
|
|
679
697
|
db.execute(
|
|
680
|
-
"INSERT INTO tina4_migration (id,
|
|
681
|
-
[next_id,
|
|
698
|
+
"INSERT INTO tina4_migration (id, migration_name, description, batch, executed_at, passed) VALUES (?, ?, ?, ?, ?, 1)",
|
|
699
|
+
[next_id, migration_name, desc, batch, now],
|
|
682
700
|
)
|
|
683
701
|
else:
|
|
684
702
|
db.execute(
|
|
685
|
-
"INSERT INTO tina4_migration (
|
|
686
|
-
[
|
|
703
|
+
"INSERT INTO tina4_migration (migration_name, description, batch, executed_at, passed) VALUES (?, ?, ?, ?, 1)",
|
|
704
|
+
[migration_name, desc, batch, now],
|
|
687
705
|
)
|
|
688
706
|
db.commit()
|
|
689
707
|
ran.append(mig_file.name)
|
|
@@ -713,7 +731,7 @@ def _rollback(db, migration_folder: str = "migrations", delimiter: str = ";") ->
|
|
|
713
731
|
|
|
714
732
|
last_batch = row["max_batch"]
|
|
715
733
|
result = db.fetch(
|
|
716
|
-
"SELECT
|
|
734
|
+
"SELECT migration_name FROM tina4_migration WHERE batch = ? AND passed = 1 ORDER BY migration_name DESC",
|
|
717
735
|
[last_batch],
|
|
718
736
|
limit=10000,
|
|
719
737
|
)
|
|
@@ -722,7 +740,7 @@ def _rollback(db, migration_folder: str = "migrations", delimiter: str = ";") ->
|
|
|
722
740
|
rolled_back = []
|
|
723
741
|
|
|
724
742
|
for migration in result.records:
|
|
725
|
-
mid = migration["
|
|
743
|
+
mid = migration["migration_name"]
|
|
726
744
|
py_file = folder / f"{mid}.py"
|
|
727
745
|
down_file = folder / f"{mid}.down.sql"
|
|
728
746
|
|
|
@@ -745,7 +763,7 @@ def _rollback(db, migration_folder: str = "migrations", delimiter: str = ";") ->
|
|
|
745
763
|
)
|
|
746
764
|
|
|
747
765
|
db.execute(
|
|
748
|
-
"DELETE FROM tina4_migration WHERE
|
|
766
|
+
"DELETE FROM tina4_migration WHERE migration_name = ?",
|
|
749
767
|
[mid],
|
|
750
768
|
)
|
|
751
769
|
db.commit()
|
|
@@ -900,12 +918,12 @@ class Migration:
|
|
|
900
918
|
row = self._db.fetch_one("SELECT GEN_ID(GEN_TINA4_MIGRATION_ID, 1) AS next_id FROM RDB$DATABASE")
|
|
901
919
|
next_id = row["next_id"] if row else 1
|
|
902
920
|
self._db.execute(
|
|
903
|
-
"INSERT INTO tina4_migration (id,
|
|
921
|
+
"INSERT INTO tina4_migration (id, migration_name, description, batch, executed_at, passed) VALUES (?, ?, ?, ?, ?, ?)",
|
|
904
922
|
[next_id, name, desc, batch, now, passed],
|
|
905
923
|
)
|
|
906
924
|
else:
|
|
907
925
|
self._db.execute(
|
|
908
|
-
"INSERT INTO tina4_migration (
|
|
926
|
+
"INSERT INTO tina4_migration (migration_name, description, batch, executed_at, passed) VALUES (?, ?, ?, ?, ?)",
|
|
909
927
|
[name, desc, batch, now, passed],
|
|
910
928
|
)
|
|
911
929
|
|
|
@@ -916,7 +934,7 @@ class Migration:
|
|
|
916
934
|
name: Migration ID to remove (stem of the filename).
|
|
917
935
|
"""
|
|
918
936
|
self._db.execute(
|
|
919
|
-
"DELETE FROM tina4_migration WHERE
|
|
937
|
+
"DELETE FROM tina4_migration WHERE migration_name = ?",
|
|
920
938
|
[name],
|
|
921
939
|
)
|
|
922
940
|
|
|
@@ -925,7 +943,7 @@ def _status(db, migration_folder: str = "migrations") -> dict:
|
|
|
925
943
|
"""Get migration status (internal implementation).
|
|
926
944
|
|
|
927
945
|
Returns {"completed": [...], "pending": [...]}, where each entry is
|
|
928
|
-
a dict with '
|
|
946
|
+
a dict with 'migration_name', 'description', and (for completed)
|
|
929
947
|
'executed_at' and 'batch'.
|
|
930
948
|
"""
|
|
931
949
|
_ensure_tracking_table(db, migration_folder)
|
|
@@ -944,12 +962,12 @@ def _status(db, migration_folder: str = "migrations") -> dict:
|
|
|
944
962
|
|
|
945
963
|
# Build completed list from the database with execution metadata
|
|
946
964
|
result = db.fetch(
|
|
947
|
-
"SELECT
|
|
965
|
+
"SELECT migration_name, description, batch, executed_at FROM tina4_migration WHERE passed = 1 ORDER BY migration_name",
|
|
948
966
|
limit=10000,
|
|
949
967
|
)
|
|
950
968
|
completed = [
|
|
951
969
|
{
|
|
952
|
-
"
|
|
970
|
+
"migration_name": row["migration_name"],
|
|
953
971
|
"description": row["description"],
|
|
954
972
|
"batch": row["batch"],
|
|
955
973
|
"executed_at": row["executed_at"],
|
|
@@ -960,11 +978,11 @@ def _status(db, migration_folder: str = "migrations") -> dict:
|
|
|
960
978
|
# Build pending list from files not yet executed
|
|
961
979
|
pending = []
|
|
962
980
|
for sql_file in sql_files:
|
|
963
|
-
|
|
964
|
-
if
|
|
965
|
-
desc = re.sub(r"^\d+_", "",
|
|
981
|
+
migration_name = sql_file.stem
|
|
982
|
+
if migration_name not in executed:
|
|
983
|
+
desc = re.sub(r"^\d+_", "", migration_name, count=1).replace("_", " ")
|
|
966
984
|
pending.append({
|
|
967
|
-
"
|
|
985
|
+
"migration_name": migration_name,
|
|
968
986
|
"description": desc,
|
|
969
987
|
})
|
|
970
988
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/images/tina4-logo-icon.webp
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/public/swagger/oauth2-redirect.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/queue_backends/rabbitmq_backend.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/session_handlers/mongodb_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/session_handlers/redis_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/session_handlers/valkey_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/docker/distroless/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/docker/poetry/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/templates/docker/python/Dockerfile
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/af/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/af/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/es/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/es/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/fr/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/fr/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/ja/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/ja/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/zh/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.54 → tina4_python-3.13.56}/tina4_python/translations/zh/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|