tina4-python 3.9.2__tar.gz → 3.10.0__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.
Files changed (139) hide show
  1. {tina4_python-3.9.2 → tina4_python-3.10.0}/PKG-INFO +1 -1
  2. {tina4_python-3.9.2 → tina4_python-3.10.0}/pyproject.toml +1 -1
  3. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/CLAUDE.md +11 -2
  4. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/core/response.py +75 -21
  5. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/core/server.py +12 -17
  6. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/messenger/__init__.py +2 -3
  7. {tina4_python-3.9.2 → tina4_python-3.10.0}/.gitignore +0 -0
  8. {tina4_python-3.9.2 → tina4_python-3.10.0}/README.md +0 -0
  9. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/HtmlElement.py +0 -0
  10. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/Testing.py +0 -0
  11. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/__init__.py +0 -0
  12. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/ai/__init__.py +0 -0
  13. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/api/__init__.py +0 -0
  14. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/auth/__init__.py +0 -0
  15. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/cache/__init__.py +0 -0
  16. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/cli/__init__.py +0 -0
  17. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/container/__init__.py +0 -0
  18. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/core/__init__.py +0 -0
  19. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/core/cache.py +0 -0
  20. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/core/constants.py +0 -0
  21. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/core/events.py +0 -0
  22. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/core/middleware.py +0 -0
  23. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/core/request.py +0 -0
  24. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/core/router.py +0 -0
  25. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/crud/__init__.py +0 -0
  26. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/database/__init__.py +0 -0
  27. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/database/adapter.py +0 -0
  28. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/database/connection.py +0 -0
  29. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/database/firebird.py +0 -0
  30. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/database/mssql.py +0 -0
  31. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/database/mysql.py +0 -0
  32. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/database/odbc.py +0 -0
  33. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/database/postgres.py +0 -0
  34. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/database/sqlite.py +0 -0
  35. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/debug/__init__.py +0 -0
  36. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/debug/error_overlay.py +0 -0
  37. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/dev_admin/__init__.py +0 -0
  38. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/dev_reload.py +0 -0
  39. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/dotenv/__init__.py +0 -0
  40. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/frond/FROND.md +0 -0
  41. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/frond/__init__.py +0 -0
  42. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/frond/engine.py +0 -0
  43. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/auth/meta.json +0 -0
  44. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  45. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/database/meta.json +0 -0
  46. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  47. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/error-overlay/meta.json +0 -0
  48. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  49. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/orm/meta.json +0 -0
  50. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  51. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  52. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/queue/meta.json +0 -0
  53. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  54. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/rest-api/meta.json +0 -0
  55. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  56. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/templates/meta.json +0 -0
  57. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  58. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  59. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/graphql/__init__.py +0 -0
  60. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/i18n/__init__.py +0 -0
  61. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/migration/__init__.py +0 -0
  62. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/migration/runner.py +0 -0
  63. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/orm/__init__.py +0 -0
  64. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/orm/fields.py +0 -0
  65. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/orm/model.py +0 -0
  66. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/public/css/tina4.css +0 -0
  67. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/public/css/tina4.min.css +0 -0
  68. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/public/favicon.ico +0 -0
  69. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/public/images/logo.svg +0 -0
  70. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  71. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/public/js/frond.min.js +0 -0
  72. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  73. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/public/js/tina4.min.js +0 -0
  74. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/public/js/tina4js.min.js +0 -0
  75. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/public/swagger/index.html +0 -0
  76. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  77. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/query_builder/__init__.py +0 -0
  78. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/queue/__init__.py +0 -0
  79. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/queue_backends/__init__.py +0 -0
  80. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/queue_backends/kafka_backend.py +0 -0
  81. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/queue_backends/mongo_backend.py +0 -0
  82. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
  83. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/__init__.py +0 -0
  84. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_alerts.scss +0 -0
  85. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_badges.scss +0 -0
  86. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_buttons.scss +0 -0
  87. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_cards.scss +0 -0
  88. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_forms.scss +0 -0
  89. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_grid.scss +0 -0
  90. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_modals.scss +0 -0
  91. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_nav.scss +0 -0
  92. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_reset.scss +0 -0
  93. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_tables.scss +0 -0
  94. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_typography.scss +0 -0
  95. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_utilities.scss +0 -0
  96. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/_variables.scss +0 -0
  97. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/base.scss +0 -0
  98. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/colors.scss +0 -0
  99. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/scss/tina4css/tina4.scss +0 -0
  100. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/seeder/__init__.py +0 -0
  101. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/service/__init__.py +0 -0
  102. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/session/__init__.py +0 -0
  103. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/session_handlers/__init__.py +0 -0
  104. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/session_handlers/mongodb_handler.py +0 -0
  105. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/session_handlers/redis_handler.py +0 -0
  106. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/session_handlers/valkey_handler.py +0 -0
  107. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/swagger/__init__.py +0 -0
  108. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/components/crud.twig +0 -0
  109. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  110. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  111. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/docker/python/Dockerfile +0 -0
  112. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  113. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/errors/302.twig +0 -0
  114. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/errors/401.twig +0 -0
  115. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/errors/403.twig +0 -0
  116. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/errors/404.twig +0 -0
  117. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/errors/500.twig +0 -0
  118. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/errors/502.twig +0 -0
  119. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/errors/503.twig +0 -0
  120. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/errors/base.twig +0 -0
  121. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/frontend/README.md +0 -0
  122. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/templates/readme.md +0 -0
  123. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/test_client/__init__.py +0 -0
  124. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  125. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  126. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  127. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  128. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  129. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  130. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  131. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  132. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  133. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  134. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  135. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  136. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/validator/__init__.py +0 -0
  137. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/websocket/__init__.py +0 -0
  138. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/websocket/backplane.py +0 -0
  139. {tina4_python-3.9.2 → tina4_python-3.10.0}/tina4_python/wsdl/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tina4-python
3
- Version: 3.9.2
3
+ Version: 3.10.0
4
4
  Summary: Tina4 Python v3 — Zero-dependency, lightweight web framework
5
5
  Author-email: Andre van Zuydam <andrevanzuydam@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tina4-python"
3
- version = "3.9.2"
3
+ version = "3.10.0"
4
4
  description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
5
5
  authors = [
6
6
  {name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
@@ -233,7 +233,7 @@ Tina4 provides a full toolkit. Before writing custom code, check if the framewor
233
233
  | GraphQL API | `GraphQL` from `tina4_python.graphql` |
234
234
  | SOAP/WSDL services | `WSDL` from `tina4_python.wsdl` |
235
235
  | Database migrations | `migrate`, `create_migration` from `tina4_python.migration` |
236
- | WebSockets | `WebSocketServer` from `tina4_python.websocket` |
236
+ | WebSockets | `WebSocketServer` from `tina4_python.websocket`. Backplane via Redis pub/sub (`TINA4_WS_BACKPLANE`, `TINA4_WS_BACKPLANE_URL`) |
237
237
  | SCSS compilation | Drop `.scss` in `src/scss/` — auto-compiled |
238
238
  | Static file serving | Put files in `src/public/` — auto-served |
239
239
  | Translations / i18n | `I18n` from `tina4_python.i18n` |
@@ -336,7 +336,7 @@ tina4_python/ # Core framework package (v3.0.0)
336
336
  ├── migration/ # SQL-file migrations (migrate, create_migration, rollback)
337
337
  │ └── runner.py # Migration runner
338
338
  ├── session/ # Pluggable sessions (Session, FileSessionHandler, DatabaseSessionHandler)
339
- ├── websocket/ # RFC 6455 WebSocket server (WebSocketServer, WebSocketConnection)
339
+ ├── websocket/ # RFC 6455 WebSocket server (WebSocketServer, WebSocketConnection, backplane)
340
340
  ├── graphql/ # Zero-dep GraphQL engine (GraphQL, Schema)
341
341
  ├── wsdl/ # SOAP 1.1 / WSDL server (WSDL, wsdl_operation)
342
342
  ├── crud/ # Auto-CRUD REST endpoint generator (AutoCrud)
@@ -504,6 +504,7 @@ TINA4_SESSION_MONGO_USERNAME= # optional
504
504
  TINA4_SESSION_MONGO_PASSWORD= # optional
505
505
  TINA4_SESSION_MONGO_DB=tina4_sessions # default database
506
506
  TINA4_SESSION_MONGO_COLLECTION=sessions # default collection
507
+ TINA4_SESSION_SAMESITE=Lax # SameSite attribute for session cookies (default: Lax)
507
508
  ```
508
509
 
509
510
  ### Authentication & Security
@@ -836,6 +837,9 @@ result = User().select(filter="name = ?", params=["Alice"], limit=10)
836
837
 
837
838
  # Convert to dict
838
839
  user.to_dict()
840
+
841
+ # NoSQL support: to_mongo() generates MongoDB query documents from the same fluent API
842
+ result.to_mongo()
839
843
  ```
840
844
 
841
845
  ### Available field types
@@ -1532,6 +1536,7 @@ HOST_NAME=localhost:7145
1532
1536
 
1533
1537
  # Sessions
1534
1538
  TINA4_SESSION_HANDLER=SessionFileHandler # SessionFileHandler, SessionRedisHandler, SessionValkeyHandler, SessionMongoHandler
1539
+ TINA4_SESSION_SAMESITE=Lax # SameSite attribute for session cookies (default: Lax)
1535
1540
 
1536
1541
  # Swagger/OpenAPI
1537
1542
  SWAGGER_TITLE=Tina4 API # API title (default: "Tina4 API")
@@ -1768,6 +1773,10 @@ async def dashboard(request, response):
1768
1773
  - **Messenger**: .env driven SMTP/IMAP
1769
1774
  - **ORM relationships**: `has_many`, `has_one`, `belongs_to` with eager loading (`include=`)
1770
1775
  - **Frond pre-compilation**: 2.8x template render improvement, `Frond.clear_cache()`
1776
+ - **QueryBuilder** with NoSQL/MongoDB support (`to_mongo()`)
1777
+ - **WebSocket backplane** (Redis pub/sub) for horizontal scaling
1778
+ - **SameSite=Lax** default on session cookies (`TINA4_SESSION_SAMESITE`)
1779
+ - **`tina4 init`** generates Dockerfile and .dockerignore
1771
1780
  - **Gallery**: 7 interactive examples with Try It deploy at `/__dev/`
1772
1781
 
1773
1782
 
@@ -22,6 +22,54 @@ import mimetypes
22
22
  from pathlib import Path
23
23
 
24
24
 
25
+ # ---------------------------------------------------------------------------
26
+ # Global Frond template engine registry
27
+ # ---------------------------------------------------------------------------
28
+ _global_frond = None
29
+ _framework_frond = None
30
+
31
+
32
+ def get_frond():
33
+ """Return the global Frond engine, creating a default if needed."""
34
+ global _global_frond
35
+ if _global_frond is None:
36
+ from tina4_python.frond.engine import Frond
37
+ _global_frond = Frond("src/templates")
38
+ return _global_frond
39
+
40
+
41
+ def get_framework_frond():
42
+ """Return the singleton Frond engine for built-in framework templates."""
43
+ global _framework_frond
44
+ framework_dir = Path(__file__).resolve().parent.parent / "templates"
45
+ if _framework_frond is None and framework_dir.is_dir():
46
+ from tina4_python.frond.engine import Frond
47
+ _framework_frond = Frond(str(framework_dir))
48
+ # Sync custom filters/globals from the user engine
49
+ if _framework_frond is not None:
50
+ user_engine = get_frond()
51
+ _framework_frond._filters.update(user_engine._filters)
52
+ _framework_frond._globals.update(user_engine._globals)
53
+ return _framework_frond
54
+
55
+
56
+ def set_frond(engine):
57
+ """Register a pre-configured Frond engine for response.render().
58
+
59
+ Call this at startup after registering custom filters and globals:
60
+
61
+ from tina4_python.frond import Frond
62
+ from tina4_python.core.response import set_frond
63
+
64
+ engine = Frond("src/templates")
65
+ engine.add_filter("money", my_money_filter)
66
+ engine.add_global("APP_VERSION", "1.0")
67
+ set_frond(engine)
68
+ """
69
+ global _global_frond
70
+ _global_frond = engine
71
+
72
+
25
73
  class Response:
26
74
  """HTTP response builder with compression and ETag support."""
27
75
 
@@ -175,27 +223,33 @@ class Response:
175
223
  return self
176
224
 
177
225
  def render(self, template: str, data: dict = None) -> "Response":
178
- """Render a Frond/Twig template with data."""
179
- from tina4_python.frond.engine import Frond
180
- from pathlib import Path
181
-
182
- # Search for templates in user dir first, then framework dir
183
- template_dirs = []
184
- user_dir = Path("src/templates")
185
- if user_dir.is_dir():
186
- template_dirs.append(str(user_dir))
187
- framework_dir = Path(__file__).resolve().parent.parent / "templates"
188
- if framework_dir.is_dir():
189
- template_dirs.append(str(framework_dir))
190
-
191
- for tdir in template_dirs:
192
- if (Path(tdir) / template).exists():
193
- try:
194
- frond = Frond(tdir)
195
- html = frond.render(template, data or {})
196
- return self.html(html)
197
- except Exception as e:
198
- return self.html(f"<pre>Template error: {e}</pre>", 500)
226
+ """Render a Frond/Twig template with data.
227
+
228
+ Uses the global Frond engine (registered via set_frond()) so that
229
+ custom filters and globals are available in all templates.
230
+ Falls back to framework templates if not found in user dir.
231
+ """
232
+ engine = get_frond()
233
+
234
+ # Try user templates first (the global engine's directory)
235
+ try:
236
+ html = engine.render(template, data or {})
237
+ return self.html(html)
238
+ except FileNotFoundError:
239
+ pass
240
+ except Exception as e:
241
+ return self.html(f"<pre>Template error: {e}</pre>", 500)
242
+
243
+ # Fallback: framework templates (singleton, filters/globals synced)
244
+ fw_engine = get_framework_frond()
245
+ if fw_engine is not None:
246
+ try:
247
+ html = fw_engine.render(template, data or {})
248
+ return self.html(html)
249
+ except FileNotFoundError:
250
+ pass
251
+ except Exception as e:
252
+ return self.html(f"<pre>Template error: {e}</pre>", 500)
199
253
 
200
254
  return self.html(f"<pre>Template not found: {template}</pre>", 404)
201
255
 
@@ -105,7 +105,7 @@ def _render_error_page(status_code: int, path: str, request_id: str, error_messa
105
105
 
106
106
  Returns rendered HTML string, or None if no template found.
107
107
  """
108
- from tina4_python.frond.engine import Frond
108
+ from tina4_python.core.response import get_frond, get_framework_frond
109
109
 
110
110
  template_name = f"errors/{status_code}.twig"
111
111
  data = {
@@ -115,21 +115,17 @@ def _render_error_page(status_code: int, path: str, request_id: str, error_messa
115
115
  "status_code": status_code,
116
116
  }
117
117
 
118
- # 1. Try user override
119
- user_dir = Path("src/templates")
120
- if (user_dir / template_name).exists():
121
- try:
122
- engine = Frond(str(user_dir))
123
- return engine.render(template_name, data)
124
- except Exception:
125
- pass
118
+ # 1. Try user override (singleton engine with custom filters/globals)
119
+ try:
120
+ return get_frond().render(template_name, data)
121
+ except (FileNotFoundError, Exception):
122
+ pass
126
123
 
127
- # 2. Try framework default
128
- framework_dir = Path(__file__).resolve().parent.parent / "templates"
129
- if (framework_dir / template_name).exists():
124
+ # 2. Try framework default (singleton, filters/globals synced)
125
+ fw_engine = get_framework_frond()
126
+ if fw_engine is not None:
130
127
  try:
131
- engine = Frond(str(framework_dir))
132
- return engine.render(template_name, data)
128
+ return fw_engine.render(template_name, data)
133
129
  except Exception:
134
130
  pass
135
131
 
@@ -848,9 +844,8 @@ async def app(scope: dict, receive, send):
848
844
  # Try serving a template file (e.g. /hello -> src/templates/hello.twig or hello.html)
849
845
  tpl_file = _resolve_template(request.path)
850
846
  if tpl_file:
851
- from tina4_python.frond import Frond
852
- frond = Frond()
853
- html = frond.render(tpl_file, {})
847
+ from tina4_python.core.response import get_frond
848
+ html = get_frond().render(tpl_file, {})
854
849
  response.html(html)
855
850
  elif request.path == "/":
856
851
  response.html(_render_landing_page())
@@ -203,9 +203,8 @@ class Messenger:
203
203
  **kwargs: Passed to send() (cc, bcc, attachments, etc.)
204
204
  """
205
205
  try:
206
- from tina4_python.frond import Frond
207
- engine = Frond()
208
- body = engine.render_string(template, data or {})
206
+ from tina4_python.core.response import get_frond
207
+ body = get_frond().render_string(template, data or {})
209
208
  except ImportError:
210
209
  body = template
211
210
 
File without changes
File without changes