tina4-python 3.11.24__tar.gz → 3.11.32__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 (153) hide show
  1. {tina4_python-3.11.24 → tina4_python-3.11.32}/.gitignore +4 -0
  2. {tina4_python-3.11.24 → tina4_python-3.11.32}/PKG-INFO +1 -1
  3. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/__init__.py +1 -1
  4. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/database/connection.py +32 -1
  5. {tina4_python-3.11.24 → tina4_python-3.11.32}/README.md +0 -0
  6. {tina4_python-3.11.24 → tina4_python-3.11.32}/pyproject.toml +0 -0
  7. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/CLAUDE.md +0 -0
  8. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/HtmlElement.py +0 -0
  9. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/Testing.py +0 -0
  10. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/ai/__init__.py +0 -0
  11. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/api/__init__.py +0 -0
  12. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/auth/__init__.py +0 -0
  13. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/cache/__init__.py +0 -0
  14. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/cli/__init__.py +0 -0
  15. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/container/__init__.py +0 -0
  16. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/core/__init__.py +0 -0
  17. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/core/cache.py +0 -0
  18. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/core/constants.py +0 -0
  19. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/core/events.py +0 -0
  20. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/core/middleware.py +0 -0
  21. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/core/rate_limiter.py +0 -0
  22. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/core/request.py +0 -0
  23. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/core/response.py +0 -0
  24. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/core/router.py +0 -0
  25. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/core/server.py +0 -0
  26. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/crud/__init__.py +0 -0
  27. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/database/__init__.py +0 -0
  28. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/database/adapter.py +0 -0
  29. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/database/firebird.py +0 -0
  30. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/database/mongodb.py +0 -0
  31. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/database/mssql.py +0 -0
  32. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/database/mysql.py +0 -0
  33. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/database/odbc.py +0 -0
  34. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/database/postgres.py +0 -0
  35. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/database/sqlite.py +0 -0
  36. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/debug/__init__.py +0 -0
  37. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/debug/error_overlay.py +0 -0
  38. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/dev_admin/__init__.py +0 -0
  39. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/dev_admin/metrics.py +0 -0
  40. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/dev_admin/plan.py +0 -0
  41. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/dev_admin/project_index.py +0 -0
  42. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/docs.py +0 -0
  43. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/dotenv/__init__.py +0 -0
  44. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/frond/FROND.md +0 -0
  45. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/frond/__init__.py +0 -0
  46. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/frond/engine.py +0 -0
  47. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/auth/meta.json +0 -0
  48. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  49. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/database/meta.json +0 -0
  50. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  51. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/error-overlay/meta.json +0 -0
  52. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  53. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/orm/meta.json +0 -0
  54. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  55. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  56. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/queue/meta.json +0 -0
  57. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  58. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/rest-api/meta.json +0 -0
  59. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  60. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/templates/meta.json +0 -0
  61. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  62. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  63. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/graphql/__init__.py +0 -0
  64. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/i18n/__init__.py +0 -0
  65. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/mcp/__init__.py +0 -0
  66. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/mcp/protocol.py +0 -0
  67. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/mcp/tools.py +0 -0
  68. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/messenger/__init__.py +0 -0
  69. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/migration/__init__.py +0 -0
  70. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/migration/runner.py +0 -0
  71. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/orm/__init__.py +0 -0
  72. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/orm/fields.py +0 -0
  73. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/orm/model.py +0 -0
  74. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/public/css/tina4.css +0 -0
  75. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/public/css/tina4.min.css +0 -0
  76. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/public/favicon.ico +0 -0
  77. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/public/images/logo.svg +0 -0
  78. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  79. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/public/js/frond.min.js +0 -0
  80. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/public/js/tina4-dev-admin.js +0 -0
  81. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  82. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/public/js/tina4.min.js +0 -0
  83. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/public/js/tina4js.min.js +0 -0
  84. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/public/swagger/index.html +0 -0
  85. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  86. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/query_builder/__init__.py +0 -0
  87. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/queue/__init__.py +0 -0
  88. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/queue/job.py +0 -0
  89. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/queue/kafka_backend.py +0 -0
  90. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/queue/lite_backend.py +0 -0
  91. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/queue/mongo_backend.py +0 -0
  92. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/queue/rabbitmq_backend.py +0 -0
  93. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/queue_backends/__init__.py +0 -0
  94. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/queue_backends/kafka_backend.py +0 -0
  95. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/queue_backends/mongo_backend.py +0 -0
  96. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
  97. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/__init__.py +0 -0
  98. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_alerts.scss +0 -0
  99. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_badges.scss +0 -0
  100. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_buttons.scss +0 -0
  101. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_cards.scss +0 -0
  102. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_forms.scss +0 -0
  103. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_grid.scss +0 -0
  104. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_modals.scss +0 -0
  105. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_nav.scss +0 -0
  106. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_reset.scss +0 -0
  107. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_tables.scss +0 -0
  108. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_typography.scss +0 -0
  109. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_utilities.scss +0 -0
  110. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/_variables.scss +0 -0
  111. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/base.scss +0 -0
  112. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/colors.scss +0 -0
  113. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/scss/tina4css/tina4.scss +0 -0
  114. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/seeder/__init__.py +0 -0
  115. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/service/__init__.py +0 -0
  116. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/session/__init__.py +0 -0
  117. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/session_handlers/__init__.py +0 -0
  118. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/session_handlers/mongodb_handler.py +0 -0
  119. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/session_handlers/redis_handler.py +0 -0
  120. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/session_handlers/valkey_handler.py +0 -0
  121. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/swagger/__init__.py +0 -0
  122. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/components/crud.twig +0 -0
  123. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  124. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  125. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/docker/python/Dockerfile +0 -0
  126. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  127. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/errors/302.twig +0 -0
  128. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/errors/401.twig +0 -0
  129. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/errors/403.twig +0 -0
  130. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/errors/404.twig +0 -0
  131. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/errors/500.twig +0 -0
  132. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/errors/502.twig +0 -0
  133. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/errors/503.twig +0 -0
  134. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/errors/base.twig +0 -0
  135. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/frontend/README.md +0 -0
  136. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/templates/readme.md +0 -0
  137. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/test_client/__init__.py +0 -0
  138. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  139. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  140. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  141. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  142. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  143. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  144. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  145. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  146. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  147. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  148. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  149. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  150. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/validator/__init__.py +0 -0
  151. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/websocket/__init__.py +0 -0
  152. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/websocket/backplane.py +0 -0
  153. {tina4_python-3.11.24 → tina4_python-3.11.32}/tina4_python/wsdl/__init__.py +0 -0
@@ -74,3 +74,7 @@ example/store/__pycache__/
74
74
  example/store/src/**/__pycache__/
75
75
  .claude/settings.local.json
76
76
  .claude/worktrees/
77
+ /nonexistent_path/
78
+ /data/store.db
79
+ /example/store/
80
+ /example/uv.lock
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tina4-python
3
- Version: 3.11.24
3
+ Version: 3.11.32
4
4
  Summary: Tina4 for Python — 54 built-in features, zero dependencies
5
5
  Author-email: Andre van Zuydam <andrevanzuydam@gmail.com>
6
6
  License: MIT
@@ -8,7 +8,7 @@ Tina4 Python v3.0 — Zero-dependency, lightweight web framework.
8
8
 
9
9
  One import, everything works.
10
10
  """
11
- __version__ = "3.11.24"
11
+ __version__ = "3.11.32"
12
12
 
13
13
  # ── Route decorators ──
14
14
  from tina4_python.core.router import ( # noqa: E402, F401
@@ -166,6 +166,11 @@ class Database:
166
166
  self._adapter: DatabaseAdapter = self._create_adapter()
167
167
  self._adapter.connect(self._connection_path(), username=self.username, password=self.password, **kwargs)
168
168
 
169
+ # Per-thread transaction adapter pin. While set, every operation
170
+ # on this thread routes to the same adapter — so the round-robin
171
+ # pool can't rotate mid-transaction and silently break atomicity.
172
+ self._tx_local = threading.local()
173
+
169
174
  # Query cache — off by default, opt-in via TINA4_DB_CACHE=true
170
175
  from tina4_python.dotenv import is_truthy
171
176
  self._cache_enabled: bool = is_truthy(os.environ.get("TINA4_DB_CACHE", "false"))
@@ -308,7 +313,25 @@ class Database:
308
313
  # ── Pool-aware adapter access ─────────────────────────────
309
314
 
310
315
  def _get_adapter(self) -> DatabaseAdapter:
311
- """Get an adapter from pool (round-robin) or single connection."""
316
+ """Get an adapter for the next operation.
317
+
318
+ With pooling enabled, ordinary calls round-robin through the pool.
319
+ Inside a transaction, however, all calls must land on the SAME
320
+ adapter — otherwise start_transaction(), execute() and commit()
321
+ each rotate to a different connection and the transaction is
322
+ meaningless (executes autocommit on whatever adapter they hit;
323
+ the final commit lands on yet another adapter that has nothing
324
+ to commit; rollback() is silently no-op'd).
325
+
326
+ We pin the adapter to the calling thread for the duration of the
327
+ transaction. start_transaction() sets the pin, commit()/rollback()
328
+ clear it. While pinned, _get_adapter() returns that same adapter
329
+ for every call so the whole transaction is atomic on one
330
+ connection.
331
+ """
332
+ pinned = getattr(self._tx_local, "adapter", None)
333
+ if pinned is not None:
334
+ return pinned
312
335
  if self._pool is not None:
313
336
  return self._pool.checkout()
314
337
  return self._adapter
@@ -422,16 +445,24 @@ class Database:
422
445
  return adapter.delete(table, filter_sql, params)
423
446
 
424
447
  def start_transaction(self):
448
+ """Begin a transaction. Pins the adapter to this thread for the
449
+ whole transaction so executes and the final commit/rollback all
450
+ run on the same connection."""
425
451
  adapter = self._get_adapter()
452
+ self._tx_local.adapter = adapter
426
453
  adapter.start_transaction()
427
454
 
428
455
  def commit(self):
456
+ """Commit the current transaction and release the adapter pin."""
429
457
  adapter = self._get_adapter()
430
458
  adapter.commit()
459
+ self._tx_local.adapter = None
431
460
 
432
461
  def rollback(self):
462
+ """Roll back the current transaction and release the adapter pin."""
433
463
  adapter = self._get_adapter()
434
464
  adapter.rollback()
465
+ self._tx_local.adapter = None
435
466
 
436
467
  def table_exists(self, name: str) -> bool:
437
468
  adapter = self._get_adapter()
File without changes