tina4-python 3.13.127__tar.gz → 3.13.129__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.127 → tina4_python-3.13.129}/PKG-INFO +1 -1
- {tina4_python-3.13.127 → tina4_python-3.13.129}/pyproject.toml +1 -2
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/__init__.py +1 -1
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/cli/__init__.py +152 -14
- {tina4_python-3.13.127 → tina4_python-3.13.129}/.gitignore +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/README.md +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/CLAUDE.md +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/Testing.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/_import_helper.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/ai/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/ai/client.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/api/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/auth/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/cache/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/container/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/context/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/context/chunker.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/core/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/core/cache.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/core/constants.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/core/events.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/core/middleware.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/core/port_takeover.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/core/rate_limiter.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/core/request.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/core/response.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/core/router.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/core/server.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/crud/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/database/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/database/adapter.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/database/connection.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/database/database_url.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/database/firebird.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/database/mongodb.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/database/mssql.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/database/mysql.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/database/odbc.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/database/postgres.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/database/sql_translator.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/database/sqlite.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/debug/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/debug/error_overlay.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/dev_admin/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/dev_admin/metrics.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/dev_admin/plan.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/dev_admin/project_index.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/docs.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/docstore/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/dotenv/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/env.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/frond/FROND.md +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/frond/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/frond/compiler.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/frond/engine.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/frond/parser.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/auth/meta.json +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/database/meta.json +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/error-overlay/meta.json +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/orm/meta.json +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/queue/meta.json +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/rest-api/meta.json +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/templates/meta.json +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/graph/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/graph/adapter.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/graph/adapters/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/graph/adapters/arango.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/graph/adapters/bolt.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/graph/adapters/ultipa.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/graph/graph_url.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/graphql/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/i18n/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/mcp/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/mcp/protocol.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/mcp/tools.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/messenger/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/migration/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/migration/runner.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/mqtt/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/mqtt/message.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/orm/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/orm/fields.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/orm/model.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/orm/point.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/__feedback/widget.js +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/css/tina4.css +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/css/tina4.min.css +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/images/logo.svg +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/js/frond.js +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/js/frond.min.js +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/js/tina4.min.js +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/js/tina4js.min.js +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/query_builder/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue/amqp_url.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue/job.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue/kafka_backend.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue/lite_backend.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue/mongo_backend.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue_backends/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue_backends/kafka_backend.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue_backends/mongo_backend.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/realtime/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/realtime/models/Attachment.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/realtime/models/Channel.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/realtime/models/ChannelMember.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/realtime/models/Message.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/realtime/models/Workspace.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/realtime/models/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/realtime/storage.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/seeder/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/service/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/session/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/session_handlers/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/session_handlers/memcached_handler.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/session_handlers/mongodb_handler.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/session_handlers/redis_handler.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/session_handlers/valkey_handler.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/sso.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/swagger/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/components/crud.twig +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/docker/python/Dockerfile +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/docker/uv/Dockerfile +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/errors/302.twig +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/errors/401.twig +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/errors/502.twig +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/errors/503.twig +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/errors/base.twig +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/frontend/README.md +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/readme.md +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/test/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/test_client/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/validator/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/websocket/__init__.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/websocket/backplane.py +0 -0
- {tina4_python-3.13.127 → tina4_python-3.13.129}/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.129"
|
|
4
4
|
description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
|
|
@@ -105,7 +105,6 @@ dev = [
|
|
|
105
105
|
"pytest-asyncio>=0.23",
|
|
106
106
|
"pytest-cov>=5.0",
|
|
107
107
|
"reportlab>=4.4.10",
|
|
108
|
-
"ruff>=0.4",
|
|
109
108
|
"setuptools>=82.0.1",
|
|
110
109
|
"uvicorn>=0.42.0",
|
|
111
110
|
]
|
|
@@ -95,6 +95,46 @@ def _to_table(name: str) -> str:
|
|
|
95
95
|
return table
|
|
96
96
|
|
|
97
97
|
|
|
98
|
+
def _resolve_table(name: str, flags: dict | None = None, *, announce: bool = False) -> str:
|
|
99
|
+
"""The table name a generator uses, honouring ``--table-name`` and speaking up
|
|
100
|
+
instead of renaming silently.
|
|
101
|
+
|
|
102
|
+
``announce`` prints the note/warning; it is True only for ``_gen_model`` (where
|
|
103
|
+
the table is born). Composite generators (crud) let the model sub-call
|
|
104
|
+
announce, and generators that target an EXISTING table (route/seeder/form/view)
|
|
105
|
+
still honour ``--table-name`` but stay quiet so the note is not repeated.
|
|
106
|
+
|
|
107
|
+
* ``--table-name <name>`` wins verbatim. If that name is ITSELF a reserved
|
|
108
|
+
word, warn loudly: Tina4 interpolates table names UNQUOTED, so the ORM's
|
|
109
|
+
generated SQL will fail on it and quoting it in raw SQL + migrations is now
|
|
110
|
+
the developer's job (we do not silently quote -- identifier quoting is a
|
|
111
|
+
global storage invariant, not a local fix).
|
|
112
|
+
* Otherwise fall back to :func:`_to_table`. When that auto-pluralises a
|
|
113
|
+
reserved-word class name (``Order`` -> ``orders``), print a one-line note
|
|
114
|
+
naming the rename and the ``--table-name`` escape hatch, so the developer is
|
|
115
|
+
informed rather than surprised.
|
|
116
|
+
|
|
117
|
+
Called once per generate (``_gen_model`` threads the result into the migration
|
|
118
|
+
and test), so the note prints once.
|
|
119
|
+
"""
|
|
120
|
+
flags = flags or {}
|
|
121
|
+
override = flags.get("table-name")
|
|
122
|
+
if override and not isinstance(override, bool):
|
|
123
|
+
if announce and _to_snake(override) in SQL_RESERVED_TABLE_NAMES:
|
|
124
|
+
print(f" ! table_name '{override}' is a SQL reserved word. Tina4 interpolates "
|
|
125
|
+
f"table names UNQUOTED, so the ORM's generated SQL will fail on it -- "
|
|
126
|
+
f"quote it yourself in raw SQL and migrations.")
|
|
127
|
+
return override
|
|
128
|
+
bare = _to_snake(name)
|
|
129
|
+
if bare in SQL_RESERVED_TABLE_NAMES:
|
|
130
|
+
table = _pluralize_table(bare)
|
|
131
|
+
if announce:
|
|
132
|
+
print(f" · '{bare}' is a SQL reserved word; using table_name '{table}' "
|
|
133
|
+
f"(Tina4 interpolates table names unquoted). Override with --table-name <name>.")
|
|
134
|
+
return table
|
|
135
|
+
return bare
|
|
136
|
+
|
|
137
|
+
|
|
98
138
|
def _parse_fields(fields_str: str) -> list[tuple[str, str]]:
|
|
99
139
|
"""Parse 'name:string,price:float' → [('name','string'), ('price','float')]."""
|
|
100
140
|
if not fields_str or not fields_str.strip():
|
|
@@ -129,7 +169,7 @@ def _parse_flags(args: list[str]) -> tuple[dict, list[str]]:
|
|
|
129
169
|
"""Parse --key value and --flag from args. Returns (flags, positional)."""
|
|
130
170
|
# Boolean-only flags that never take a value argument
|
|
131
171
|
boolean_flags = {"no-browser", "no-reload", "no-kill", "production", "managed", "all", "clear",
|
|
132
|
-
"json", "public", "no-migration", "once", "dry-run", "
|
|
172
|
+
"json", "public", "no-migration", "once", "dry-run", "fix", "no-install"}
|
|
133
173
|
|
|
134
174
|
flags = {}
|
|
135
175
|
positional = []
|
|
@@ -927,6 +967,103 @@ def _test(args):
|
|
|
927
967
|
sys.exit(1 if (inline_failed or pytest_code) else 0)
|
|
928
968
|
|
|
929
969
|
|
|
970
|
+
def _resolve_ruff(root: Path):
|
|
971
|
+
"""Locate the PROJECT's own ruff and return its command prefix (a list), or
|
|
972
|
+
None. Only the project virtualenv is consulted (where ``uv add --dev ruff``
|
|
973
|
+
puts it), on POSIX and Windows layouts -- deliberately NOT a global ruff on
|
|
974
|
+
PATH: linting must be reproducible from the project's own pinned dev
|
|
975
|
+
dependency, and a global tool is not the project's choice. When the project
|
|
976
|
+
has none, the caller installs it dev-only rather than borrowing a stray
|
|
977
|
+
global one (which would also make a global install non-deterministic)."""
|
|
978
|
+
for candidate in (root / ".venv" / "bin" / "ruff", root / ".venv" / "Scripts" / "ruff.exe"):
|
|
979
|
+
if candidate.is_file():
|
|
980
|
+
return [str(candidate)]
|
|
981
|
+
return None
|
|
982
|
+
|
|
983
|
+
|
|
984
|
+
def _lint(args):
|
|
985
|
+
"""Lint the project's source. The framework ships NO linter (so a Tina4 app
|
|
986
|
+
stays zero-dependency); ``tina4 lint`` uses the project's own ``ruff`` and
|
|
987
|
+
INSTALLS it as a DEV dependency on demand when it is absent. Layers:
|
|
988
|
+
|
|
989
|
+
* **ruff present** (on PATH or in the project ``.venv``): run it. ``--fix``
|
|
990
|
+
runs ``ruff check --fix`` (safe autofixes). ruff reports syntax too, so it
|
|
991
|
+
is the whole pass when present.
|
|
992
|
+
* **ruff absent:** silently ``uv add --dev ruff`` -- running ``tina4 lint`` is
|
|
993
|
+
the consent to add it -- then run it. It lands in the DEV group only, never
|
|
994
|
+
the app's runtime dependencies. ``--no-install`` skips this.
|
|
995
|
+
* **Baseline (zero dependency):** the built-in ``compile()`` syntax parse --
|
|
996
|
+
no execution, no ``.pyc``, no dependency. Used with ``--no-install`` or when
|
|
997
|
+
the install cannot run (no ``uv`` on PATH, offline).
|
|
998
|
+
|
|
999
|
+
Contract (identical across all four frameworks): exit 0 = clean, non-zero =
|
|
1000
|
+
findings; the summary names the tool that ran. Scope is the user's app
|
|
1001
|
+
(``src/`` + ``app.py``), mirroring how ``tina4 test`` runs the project's own
|
|
1002
|
+
tests -- not the framework's code.
|
|
1003
|
+
|
|
1004
|
+
tina4 lint # ruff (installed dev-only on demand), else baseline
|
|
1005
|
+
tina4 lint --fix # ruff --fix
|
|
1006
|
+
tina4 lint --no-install # ruff if already present, else the syntax baseline
|
|
1007
|
+
"""
|
|
1008
|
+
flags, _ = _parse_flags(list(args))
|
|
1009
|
+
fix = bool(flags.get("fix"))
|
|
1010
|
+
no_install = bool(flags.get("no-install"))
|
|
1011
|
+
root = Path.cwd()
|
|
1012
|
+
|
|
1013
|
+
py_files = []
|
|
1014
|
+
src = root / "src"
|
|
1015
|
+
if src.is_dir():
|
|
1016
|
+
py_files.extend(sorted(src.rglob("*.py")))
|
|
1017
|
+
if (root / "app.py").is_file():
|
|
1018
|
+
py_files.append(root / "app.py")
|
|
1019
|
+
if not py_files:
|
|
1020
|
+
print(" lint: nothing to lint (no src/ or app.py).")
|
|
1021
|
+
sys.exit(0)
|
|
1022
|
+
|
|
1023
|
+
ruff = _resolve_ruff(root)
|
|
1024
|
+
|
|
1025
|
+
# Silent on-demand install: running `tina4 lint` is the consent to add ruff
|
|
1026
|
+
# as a DEV dependency of the project. --no-install opts out (CI / offline) and
|
|
1027
|
+
# falls through to the zero-dependency baseline. Needs `uv` on PATH.
|
|
1028
|
+
if not ruff and not no_install and shutil.which("uv"):
|
|
1029
|
+
print(" · ruff not found -- adding it as a dev dependency (uv add --dev ruff)...")
|
|
1030
|
+
rc = subprocess.run(["uv", "add", "--dev", "ruff"]).returncode
|
|
1031
|
+
if rc == 0:
|
|
1032
|
+
ruff = _resolve_ruff(root)
|
|
1033
|
+
else:
|
|
1034
|
+
print(" · could not install ruff -- using the zero-dependency syntax baseline.")
|
|
1035
|
+
|
|
1036
|
+
if ruff:
|
|
1037
|
+
cmd = ruff + ["check"] + (["--fix"] if fix else []) + [str(p) for p in py_files]
|
|
1038
|
+
code = subprocess.run(cmd).returncode
|
|
1039
|
+
label = "ruff --fix" if fix else "ruff"
|
|
1040
|
+
if code != 0:
|
|
1041
|
+
print(f" ✗ lint failed -- {len(py_files)} file(s) [{label}]")
|
|
1042
|
+
sys.exit(1)
|
|
1043
|
+
print(f" ✓ lint clean -- {len(py_files)} file(s) [{label}]")
|
|
1044
|
+
sys.exit(0)
|
|
1045
|
+
|
|
1046
|
+
# Zero-dependency baseline: built-in compile() syntax parse. No execution,
|
|
1047
|
+
# no bytecode written, no dependency added.
|
|
1048
|
+
if fix:
|
|
1049
|
+
print(" · syntax baseline has no autofix (--no-install, or ruff unavailable).")
|
|
1050
|
+
syntax_errors = 0
|
|
1051
|
+
for pf in py_files:
|
|
1052
|
+
try:
|
|
1053
|
+
compile(pf.read_text(encoding="utf-8", errors="ignore"), str(pf), "exec")
|
|
1054
|
+
except SyntaxError as exc:
|
|
1055
|
+
print(f" ✗ {pf.relative_to(root)}:{exc.lineno}: {exc.msg}")
|
|
1056
|
+
syntax_errors += 1
|
|
1057
|
+
except OSError as exc:
|
|
1058
|
+
print(f" ✗ {pf.relative_to(root)}: cannot read ({exc})")
|
|
1059
|
+
syntax_errors += 1
|
|
1060
|
+
if syntax_errors:
|
|
1061
|
+
print(f" ✗ lint failed -- {syntax_errors} syntax error(s) in {len(py_files)} file(s) [compile]")
|
|
1062
|
+
sys.exit(1)
|
|
1063
|
+
print(f" ✓ lint clean -- {len(py_files)} file(s) [compile, syntax only]")
|
|
1064
|
+
sys.exit(0)
|
|
1065
|
+
|
|
1066
|
+
|
|
930
1067
|
def _build(args):
|
|
931
1068
|
"""Build the deployable Docker image for this Tina4 app.
|
|
932
1069
|
|
|
@@ -1176,7 +1313,7 @@ def _resolve_generation(target: str, name: str, flags: dict,
|
|
|
1176
1313
|
|
|
1177
1314
|
if target == "model":
|
|
1178
1315
|
raw_table = _to_snake(name)
|
|
1179
|
-
table =
|
|
1316
|
+
table = _resolve_table(name, flags)
|
|
1180
1317
|
transformations = []
|
|
1181
1318
|
if raw_table in SQL_RESERVED_TABLE_NAMES and raw_table != table:
|
|
1182
1319
|
transformations.append({
|
|
@@ -1185,8 +1322,8 @@ def _resolve_generation(target: str, name: str, flags: dict,
|
|
|
1185
1322
|
"to": table,
|
|
1186
1323
|
"reason": f"SQL reserved word '{raw_table}' would break CREATE TABLE",
|
|
1187
1324
|
"override": (
|
|
1188
|
-
|
|
1189
|
-
"
|
|
1325
|
+
"--table-name <name> (table names interpolate unquoted; "
|
|
1326
|
+
"forcing a reserved name is yours to quote in raw SQL)"
|
|
1190
1327
|
),
|
|
1191
1328
|
})
|
|
1192
1329
|
migration_filename = f"{ts}_create_{table}.sql"
|
|
@@ -1340,11 +1477,11 @@ def _format_resolution_block(name: str, target: str, resolution: dict,
|
|
|
1340
1477
|
|
|
1341
1478
|
if pluralize:
|
|
1342
1479
|
lines.append("")
|
|
1343
|
-
lines.append(
|
|
1480
|
+
lines.append(" To set the table name yourself:")
|
|
1481
|
+
lines.append(f" tina4 generate {target} {name} --table-name <name>")
|
|
1344
1482
|
lines.append(
|
|
1345
|
-
f"
|
|
1346
|
-
f"
|
|
1347
|
-
"(opt-in, ADR-0062 forthcoming)"
|
|
1483
|
+
f" Tina4 interpolates table names unquoted; if you force the "
|
|
1484
|
+
f"reserved '{pluralize['from']}', you own the quoting in raw SQL."
|
|
1348
1485
|
)
|
|
1349
1486
|
|
|
1350
1487
|
# ADR-0063 additive sections. Each renders only when there is content, so
|
|
@@ -1540,7 +1677,7 @@ def _gen_model(name: str, flags: dict, *, emit_test: bool = True):
|
|
|
1540
1677
|
and emit their own broader test instead.
|
|
1541
1678
|
"""
|
|
1542
1679
|
fields = _fields_or_default(flags.get("fields", ""))
|
|
1543
|
-
table =
|
|
1680
|
+
table = _resolve_table(name, flags, announce=True)
|
|
1544
1681
|
|
|
1545
1682
|
# Determine which ORM field types we need to import
|
|
1546
1683
|
used_types = {"IntegerField"} # always need for id
|
|
@@ -1822,7 +1959,7 @@ def _gen_crud(name: str, flags: dict):
|
|
|
1822
1959
|
tina4python generate crud Product --fields "name:string,price:float"
|
|
1823
1960
|
"""
|
|
1824
1961
|
fields = _fields_or_default(flags.get("fields", ""))
|
|
1825
|
-
table =
|
|
1962
|
+
table = _resolve_table(name, flags)
|
|
1826
1963
|
route_name = table + "s" # routes are plural
|
|
1827
1964
|
|
|
1828
1965
|
print(f"\n Generating CRUD for {name}...\n")
|
|
@@ -2920,7 +3057,7 @@ def _gen_seeder(name: str, flags: dict = None):
|
|
|
2920
3057
|
|
|
2921
3058
|
tina4python generate seeder Product
|
|
2922
3059
|
"""
|
|
2923
|
-
table =
|
|
3060
|
+
table = _resolve_table(name, flags)
|
|
2924
3061
|
target = Path("src/seeds")
|
|
2925
3062
|
target.mkdir(parents=True, exist_ok=True)
|
|
2926
3063
|
path = target / f"{table}_seeder.py"
|
|
@@ -3096,7 +3233,7 @@ def _gen_form(name: str, flags: dict = None):
|
|
|
3096
3233
|
"""
|
|
3097
3234
|
flags = flags or {}
|
|
3098
3235
|
fields = _fields_or_default(flags.get("fields", ""))
|
|
3099
|
-
table =
|
|
3236
|
+
table = _resolve_table(name, flags)
|
|
3100
3237
|
route_name = table + "s"
|
|
3101
3238
|
|
|
3102
3239
|
# Input type mapping
|
|
@@ -3180,7 +3317,7 @@ def _gen_view(name: str, flags: dict = None):
|
|
|
3180
3317
|
"""
|
|
3181
3318
|
flags = flags or {}
|
|
3182
3319
|
fields = _fields_or_default(flags.get("fields", ""))
|
|
3183
|
-
table =
|
|
3320
|
+
table = _resolve_table(name, flags)
|
|
3184
3321
|
route_name = table + "s"
|
|
3185
3322
|
|
|
3186
3323
|
target = Path("src/templates/pages")
|
|
@@ -3725,7 +3862,7 @@ def _commands(args=None):
|
|
|
3725
3862
|
# "subcommands"?: [str]} # sub-names for the manifest (generate)
|
|
3726
3863
|
|
|
3727
3864
|
GENERATORS = {
|
|
3728
|
-
"model": {"handler": _gen_model, "usage": '<Name> [--fields "name:string,price:float"]', "summary": "ORM model + matching migration"},
|
|
3865
|
+
"model": {"handler": _gen_model, "usage": '<Name> [--fields "name:string,price:float"] [--table-name <name>]', "summary": "ORM model + matching migration"},
|
|
3729
3866
|
"route": {"handler": _gen_route, "usage": "<name> [--model Name] [--public]", "summary": "CRUD route file, secure by default (--public opens writes)"},
|
|
3730
3867
|
"crud": {"handler": _gen_crud, "usage": '<Name> [--fields "..."] [--public]', "summary": "Model + migration + routes + form + view + test"},
|
|
3731
3868
|
"migration": {"handler": _gen_migration, "usage": "<description>", "summary": "Timestamped migration file (UP/DOWN)"},
|
|
@@ -3756,6 +3893,7 @@ COMMANDS = {
|
|
|
3756
3893
|
"test": {"handler": _test, "summary": "Run test suite"},
|
|
3757
3894
|
"queue": {"handler": _queue, "usage": "<work|stats|retry|clear> [topic]", "subcommands": list(_QUEUE_SUBCOMMANDS), "summary": "Run queue workers and manage jobs"},
|
|
3758
3895
|
"build": {"handler": _build, "usage": "[--tag NAME] [--file PATH]", "summary": "Build the deployable Docker image"},
|
|
3896
|
+
"lint": {"handler": _lint, "usage": "[--fix] [--no-install]", "summary": "Lint src/ + app.py (installs ruff dev-only on demand; zero-dep syntax baseline with --no-install)"},
|
|
3759
3897
|
"ai": {"handler": _ai, "usage": "[--all]", "summary": "Install AI coding assistant context"},
|
|
3760
3898
|
"generate": {"handler": _generate, "usage": "<what> <name> [options]", "subcommands": list(GENERATORS), "summary": "Generate scaffolding (see Generators below)"},
|
|
3761
3899
|
"console": {"handler": _console, "summary": "Start interactive REPL with framework loaded"},
|
|
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.127 → tina4_python-3.13.129}/tina4_python/gallery/error-overlay/meta.json
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
|
|
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.127 → tina4_python-3.13.129}/tina4_python/public/images/tina4-logo-icon.webp
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/public/js/tina4-dev-admin.min.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/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
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue_backends/kafka_backend.py
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue_backends/mongo_backend.py
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/queue_backends/rabbitmq_backend.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/realtime/models/ChannelMember.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
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/session_handlers/memcached_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/session_handlers/mongodb_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/session_handlers/redis_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/session_handlers/valkey_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/docker/distroless/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/templates/docker/poetry/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/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.127 → tina4_python-3.13.129}/tina4_python/translations/af/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/af/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/es/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/es/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/fr/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/fr/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/ja/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/ja/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/zh/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.127 → tina4_python-3.13.129}/tina4_python/translations/zh/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|