tina4-python 3.13.75__tar.gz → 3.13.77__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.75 → tina4_python-3.13.77}/PKG-INFO +1 -1
- {tina4_python-3.13.75 → tina4_python-3.13.77}/pyproject.toml +4 -1
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/core/server.py +60 -26
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/migration/runner.py +33 -11
- {tina4_python-3.13.75 → tina4_python-3.13.77}/.gitignore +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/README.md +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/CLAUDE.md +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/Testing.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/ai/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/api/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/auth/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/cache/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/cli/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/container/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/context/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/context/chunker.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/core/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/core/cache.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/core/constants.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/core/events.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/core/middleware.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/core/rate_limiter.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/core/request.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/core/response.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/core/router.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/crud/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/database/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/database/adapter.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/database/connection.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/database/firebird.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/database/mongodb.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/database/mssql.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/database/mysql.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/database/odbc.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/database/postgres.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/database/sqlite.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/debug/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/debug/error_overlay.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/dev_admin/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/dev_admin/metrics.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/dev_admin/plan.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/dev_admin/project_index.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/docs.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/docstore/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/dotenv/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/env.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/frond/FROND.md +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/frond/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/frond/engine.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/auth/meta.json +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/database/meta.json +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/error-overlay/meta.json +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/orm/meta.json +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/queue/meta.json +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/rest-api/meta.json +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/templates/meta.json +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/graphql/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/i18n/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/mcp/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/mcp/protocol.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/mcp/tools.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/messenger/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/migration/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/orm/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/orm/fields.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/orm/model.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/__feedback/widget.js +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/css/tina4.css +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/css/tina4.min.css +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/images/logo.svg +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/js/frond.js +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/js/frond.min.js +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/js/tina4-dev-admin.js +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/js/tina4.min.js +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/js/tina4js.min.js +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/query_builder/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/queue/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/queue/job.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/queue/kafka_backend.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/queue/lite_backend.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/queue/mongo_backend.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/queue/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/queue_backends/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/queue_backends/kafka_backend.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/queue_backends/mongo_backend.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/realtime/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/realtime/models/Attachment.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/realtime/models/Channel.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/realtime/models/ChannelMember.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/realtime/models/Message.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/realtime/models/Workspace.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/realtime/models/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/realtime/storage.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_alerts.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_badges.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_buttons.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_cards.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_forms.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_grid.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_modals.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_nav.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_reset.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_tables.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_typography.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_utilities.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/_variables.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/base.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/colors.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/scss/tina4css/tina4.scss +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/seeder/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/service/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/session/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/session_handlers/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/session_handlers/mongodb_handler.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/session_handlers/redis_handler.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/session_handlers/valkey_handler.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/swagger/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/components/crud.twig +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/docker/python/Dockerfile +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/docker/uv/Dockerfile +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/errors/302.twig +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/errors/401.twig +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/errors/502.twig +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/errors/503.twig +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/errors/base.twig +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/frontend/README.md +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/readme.md +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/test/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/test_client/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/validator/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/websocket/__init__.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/websocket/backplane.py +0 -0
- {tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/wsdl/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tina4-python"
|
|
3
|
-
version = "3.13.
|
|
3
|
+
version = "3.13.77"
|
|
4
4
|
description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
|
|
@@ -64,6 +64,9 @@ tina4python = "tina4_python.cli:main"
|
|
|
64
64
|
[tool.pytest.ini_options]
|
|
65
65
|
asyncio_mode = "auto"
|
|
66
66
|
testpaths = ["tests"]
|
|
67
|
+
markers = [
|
|
68
|
+
"slow: a test that needs real wall-clock time to reproduce a timing bug (deselect with -m 'not slow')",
|
|
69
|
+
]
|
|
67
70
|
|
|
68
71
|
[tool.ruff]
|
|
69
72
|
target-version = "py312"
|
|
@@ -49,6 +49,58 @@ def background(callback, interval: float = 1.0):
|
|
|
49
49
|
_background_tasks.append({"callback": callback, "interval": interval})
|
|
50
50
|
|
|
51
51
|
|
|
52
|
+
async def background_tick_loop(callback, interval: float, executor, shutdown):
|
|
53
|
+
"""Run one registered background task on its interval until shutdown.
|
|
54
|
+
|
|
55
|
+
A task NEVER overlaps itself: each run is awaited to completion before the
|
|
56
|
+
interval sleep and the next tick. That is the only sound reading of an
|
|
57
|
+
interval scheduler, and it is what makes the one-worker-per-task pool sizing
|
|
58
|
+
in `_serve` safe.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
callback: The registered callable (sync or async, no arguments).
|
|
62
|
+
interval: Seconds to sleep between runs.
|
|
63
|
+
executor: ThreadPoolExecutor used for sync callbacks.
|
|
64
|
+
shutdown: asyncio.Event that ends the loop when set.
|
|
65
|
+
"""
|
|
66
|
+
timeout = max(interval * 2, 5.0)
|
|
67
|
+
loop = asyncio.get_running_loop()
|
|
68
|
+
while not shutdown.is_set():
|
|
69
|
+
try:
|
|
70
|
+
if asyncio.iscoroutinefunction(callback):
|
|
71
|
+
# A coroutine IS cancellable, so the timeout really interrupts it.
|
|
72
|
+
await asyncio.wait_for(callback(), timeout=timeout)
|
|
73
|
+
else:
|
|
74
|
+
# Run sync callback in a thread pool — never blocks the event loop.
|
|
75
|
+
#
|
|
76
|
+
# A callable already running in a ThreadPoolExecutor CANNOT be
|
|
77
|
+
# cancelled: Future.cancel() returns False once it has started and
|
|
78
|
+
# there is no way to interrupt the thread. So this must NOT use
|
|
79
|
+
# wait_for() — that abandons the wrapper future while the thread
|
|
80
|
+
# keeps running, and the next tick then starts a SECOND copy
|
|
81
|
+
# alongside the first (silent double-execution of a slow sweep,
|
|
82
|
+
# with every later tick piling on another). Wait for the run to
|
|
83
|
+
# finish and only WARN about the overrun.
|
|
84
|
+
fut = loop.run_in_executor(executor, callback)
|
|
85
|
+
done, _pending = await asyncio.wait({fut}, timeout=timeout)
|
|
86
|
+
if not done:
|
|
87
|
+
Log.warning(
|
|
88
|
+
f"Background task is still running after {timeout:.1f}s. "
|
|
89
|
+
f"The next run is deferred until it finishes (a running sync "
|
|
90
|
+
f"task cannot be interrupted). Use non-blocking calls "
|
|
91
|
+
f"(e.g. queue.pop() instead of queue.consume())."
|
|
92
|
+
)
|
|
93
|
+
await fut # never overlap; re-raises the callback's error
|
|
94
|
+
except asyncio.TimeoutError:
|
|
95
|
+
Log.warning(
|
|
96
|
+
f"Background task exceeded {timeout:.1f}s timeout and was interrupted. "
|
|
97
|
+
f"Use non-blocking calls (e.g. queue.pop() instead of queue.consume())."
|
|
98
|
+
)
|
|
99
|
+
except Exception as e:
|
|
100
|
+
Log.error(f"Background task error: {e}")
|
|
101
|
+
await asyncio.sleep(interval)
|
|
102
|
+
|
|
103
|
+
|
|
52
104
|
# module_name → source-file mtime at the last (re)import. Drives the
|
|
53
105
|
# changed-file detection in ``_auto_discover``: a file whose mtime is newer
|
|
54
106
|
# than the recorded value is re-executed in place, so editing an existing
|
|
@@ -2743,7 +2795,9 @@ def run(host: str | None = None, port: int | None = None, no_browser: bool = Fal
|
|
|
2743
2795
|
|
|
2744
2796
|
# Start registered background tasks as asyncio tasks.
|
|
2745
2797
|
# Sync callbacks run in a thread pool so they CANNOT block the event loop.
|
|
2746
|
-
#
|
|
2798
|
+
# max_workers is one per registered task — sound only because a task never
|
|
2799
|
+
# overlaps itself (see background_tick_loop), so tasks cannot starve each
|
|
2800
|
+
# other of workers.
|
|
2747
2801
|
import concurrent.futures
|
|
2748
2802
|
_executor = concurrent.futures.ThreadPoolExecutor(
|
|
2749
2803
|
max_workers=max(len(_background_tasks), 2),
|
|
@@ -2751,31 +2805,11 @@ def run(host: str | None = None, port: int | None = None, no_browser: bool = Fal
|
|
|
2751
2805
|
)
|
|
2752
2806
|
bg_tasks = []
|
|
2753
2807
|
for task_def in _background_tasks:
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
while not shutdown.is_set():
|
|
2760
|
-
try:
|
|
2761
|
-
if asyncio.iscoroutinefunction(_cb):
|
|
2762
|
-
await asyncio.wait_for(_cb(), timeout=timeout)
|
|
2763
|
-
else:
|
|
2764
|
-
# Run sync callback in thread pool — never blocks the event loop
|
|
2765
|
-
await asyncio.wait_for(
|
|
2766
|
-
loop.run_in_executor(_executor, _cb),
|
|
2767
|
-
timeout=timeout,
|
|
2768
|
-
)
|
|
2769
|
-
except asyncio.TimeoutError:
|
|
2770
|
-
Log.warning(
|
|
2771
|
-
f"Background task exceeded {timeout:.1f}s timeout and was interrupted. "
|
|
2772
|
-
f"Use non-blocking calls (e.g. queue.pop() instead of queue.consume())."
|
|
2773
|
-
)
|
|
2774
|
-
except Exception as e:
|
|
2775
|
-
Log.error(f"Background task error: {e}")
|
|
2776
|
-
await asyncio.sleep(_iv)
|
|
2777
|
-
|
|
2778
|
-
bg_tasks.append(asyncio.create_task(_tick_loop()))
|
|
2808
|
+
bg_tasks.append(asyncio.create_task(
|
|
2809
|
+
background_tick_loop(
|
|
2810
|
+
task_def["callback"], task_def["interval"], _executor, shutdown,
|
|
2811
|
+
)
|
|
2812
|
+
))
|
|
2779
2813
|
|
|
2780
2814
|
await shutdown.wait()
|
|
2781
2815
|
|
|
@@ -296,7 +296,11 @@ def _ensure_tracking_table(db, migration_folder: str | None = None):
|
|
|
296
296
|
# and copy the values across so already-applied migrations are still seen as
|
|
297
297
|
# applied — without this the `migration_name`-based read below finds nothing
|
|
298
298
|
# and every migration re-runs. The old `migration_id` column is left in
|
|
299
|
-
# place (
|
|
299
|
+
# place: it cannot be dropped portably (SQLite cannot drop a NOT NULL or a
|
|
300
|
+
# column without a full table rebuild). It is ignored on READS, but it is
|
|
301
|
+
# NOT ignorable on WRITES — it is NOT NULL on that table, so
|
|
302
|
+
# `_record_applied` must keep populating it (see there). Leaving it unwritten
|
|
303
|
+
# is what broke every migration on an upgraded database in 3.13.55-3.13.75.
|
|
300
304
|
if "migration_id" in cols and "migration_name" not in cols:
|
|
301
305
|
col_type = "VARCHAR(500)" if _is_firebird(db) else "TEXT"
|
|
302
306
|
try:
|
|
@@ -641,18 +645,36 @@ def _record_applied(db, name: str, desc: str, batch: int, passed: int = 1) -> No
|
|
|
641
645
|
"""
|
|
642
646
|
now = datetime.now(timezone.utc).isoformat()
|
|
643
647
|
db.execute("DELETE FROM tina4_migration WHERE migration_name = ?", [name])
|
|
648
|
+
|
|
649
|
+
# Build the column list from the columns that ACTUALLY exist on this table,
|
|
650
|
+
# so a legacy column an earlier in-place upgrade left behind is populated
|
|
651
|
+
# rather than defaulted to NULL. A table created by v3 <= 3.13.54 carries
|
|
652
|
+
# `migration_id NOT NULL UNIQUE`; the rename upgrade adds `migration_name`
|
|
653
|
+
# beside it but cannot drop the NOT NULL portably (SQLite cannot drop it at
|
|
654
|
+
# all without a table rebuild), so this insert has to satisfy it or every
|
|
655
|
+
# migration on that database fails and none can ever apply.
|
|
656
|
+
cols = _column_names(db)
|
|
657
|
+
insert_cols = ["migration_name", "description", "batch", "executed_at", "passed"]
|
|
658
|
+
values = [name, desc, batch, now, passed]
|
|
659
|
+
|
|
660
|
+
# v3 <= 3.13.54: `migration_id` held exactly the value `migration_name` holds
|
|
661
|
+
# now, so writing `name` restores that invariant. Safe against its UNIQUE:
|
|
662
|
+
# the DELETE above removed this migration's own row first, so a re-run
|
|
663
|
+
# replaces its row instead of colliding.
|
|
664
|
+
if "migration_id" in cols:
|
|
665
|
+
insert_cols.append("migration_id")
|
|
666
|
+
values.append(name)
|
|
667
|
+
|
|
644
668
|
if _is_firebird(db):
|
|
645
669
|
row = db.fetch_one("SELECT GEN_ID(GEN_TINA4_MIGRATION_ID, 1) AS next_id FROM RDB$DATABASE")
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
[name, desc, batch, now, passed],
|
|
655
|
-
)
|
|
670
|
+
insert_cols.insert(0, "id")
|
|
671
|
+
values.insert(0, row["next_id"] if row else 1)
|
|
672
|
+
|
|
673
|
+
placeholders = ", ".join("?" for _ in insert_cols)
|
|
674
|
+
db.execute(
|
|
675
|
+
f"INSERT INTO tina4_migration ({', '.join(insert_cols)}) VALUES ({placeholders})",
|
|
676
|
+
values,
|
|
677
|
+
)
|
|
656
678
|
|
|
657
679
|
|
|
658
680
|
def _migrate(db, migration_folder: str = "migrations", delimiter: str = ";") -> list[str]:
|
|
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
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/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.75 → tina4_python-3.13.77}/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.75 → tina4_python-3.13.77}/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
|
|
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.75 → tina4_python-3.13.77}/tina4_python/session_handlers/mongodb_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/session_handlers/redis_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/session_handlers/valkey_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/docker/distroless/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/templates/docker/poetry/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/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.75 → tina4_python-3.13.77}/tina4_python/translations/af/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/af/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/es/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/es/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/fr/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/fr/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/ja/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/ja/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/zh/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.75 → tina4_python-3.13.77}/tina4_python/translations/zh/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|