tina4-python 3.13.92__tar.gz → 3.13.95__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 (184) hide show
  1. {tina4_python-3.13.92 → tina4_python-3.13.95}/.gitignore +12 -0
  2. {tina4_python-3.13.92 → tina4_python-3.13.95}/PKG-INFO +21 -3
  3. {tina4_python-3.13.92 → tina4_python-3.13.95}/README.md +2 -2
  4. tina4_python-3.13.95/pyproject.toml +121 -0
  5. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/CLAUDE.md +23 -7
  6. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/__init__.py +9 -1
  7. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/auth/__init__.py +239 -35
  8. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/cache/__init__.py +630 -163
  9. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/cli/__init__.py +5 -1
  10. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/core/middleware.py +269 -31
  11. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/core/rate_limiter.py +28 -2
  12. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/core/request.py +108 -6
  13. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/core/response.py +92 -3
  14. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/core/router.py +55 -11
  15. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/core/server.py +858 -293
  16. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/database/adapter.py +404 -135
  17. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/database/connection.py +288 -26
  18. tina4_python-3.13.95/tina4_python/database/database_url.py +365 -0
  19. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/database/firebird.py +163 -48
  20. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/database/mongodb.py +15 -3
  21. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/database/mssql.py +58 -49
  22. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/database/mysql.py +47 -49
  23. tina4_python-3.13.95/tina4_python/database/odbc.py +224 -0
  24. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/database/postgres.py +39 -48
  25. tina4_python-3.13.95/tina4_python/database/sql_translator.py +272 -0
  26. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/database/sqlite.py +19 -38
  27. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/debug/__init__.py +262 -45
  28. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/dev_admin/__init__.py +96 -49
  29. tina4_python-3.13.95/tina4_python/dev_admin/metrics.py +375 -0
  30. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/docstore/__init__.py +240 -49
  31. tina4_python-3.13.95/tina4_python/dotenv/__init__.py +288 -0
  32. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/frond/engine.py +68 -8
  33. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/mcp/tools.py +12 -2
  34. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/messenger/__init__.py +89 -27
  35. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/mqtt/__init__.py +5 -1
  36. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/orm/model.py +91 -18
  37. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/queue/__init__.py +35 -28
  38. tina4_python-3.13.95/tina4_python/queue/amqp_url.py +73 -0
  39. tina4_python-3.13.95/tina4_python/queue/kafka_backend.py +210 -0
  40. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/queue/lite_backend.py +23 -1
  41. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/queue/mongo_backend.py +65 -6
  42. tina4_python-3.13.95/tina4_python/queue/rabbitmq_backend.py +221 -0
  43. tina4_python-3.13.95/tina4_python/queue_backends/kafka_backend.py +603 -0
  44. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/queue_backends/mongo_backend.py +20 -7
  45. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/queue_backends/rabbitmq_backend.py +31 -17
  46. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/session/__init__.py +190 -16
  47. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/session_handlers/__init__.py +8 -2
  48. tina4_python-3.13.95/tina4_python/session_handlers/memcached_handler.py +155 -0
  49. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/session_handlers/mongodb_handler.py +81 -29
  50. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/session_handlers/redis_handler.py +4 -1
  51. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/session_handlers/valkey_handler.py +4 -1
  52. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/websocket/__init__.py +9 -3
  53. tina4_python-3.13.92/pyproject.toml +0 -73
  54. tina4_python-3.13.92/tina4_python/database/odbc.py +0 -177
  55. tina4_python-3.13.92/tina4_python/dev_admin/metrics.py +0 -867
  56. tina4_python-3.13.92/tina4_python/dev_admin/metrics_engine.py +0 -108
  57. tina4_python-3.13.92/tina4_python/dotenv/__init__.py +0 -163
  58. tina4_python-3.13.92/tina4_python/queue/kafka_backend.py +0 -151
  59. tina4_python-3.13.92/tina4_python/queue/rabbitmq_backend.py +0 -186
  60. tina4_python-3.13.92/tina4_python/queue_backends/kafka_backend.py +0 -414
  61. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/HtmlElement.py +0 -0
  62. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/Testing.py +0 -0
  63. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/ai/__init__.py +0 -0
  64. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/api/__init__.py +0 -0
  65. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/container/__init__.py +0 -0
  66. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/context/__init__.py +0 -0
  67. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/context/chunker.py +0 -0
  68. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/core/__init__.py +0 -0
  69. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/core/cache.py +0 -0
  70. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/core/constants.py +0 -0
  71. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/core/events.py +0 -0
  72. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/crud/__init__.py +0 -0
  73. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/database/__init__.py +0 -0
  74. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/debug/error_overlay.py +0 -0
  75. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/dev_admin/plan.py +0 -0
  76. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/dev_admin/project_index.py +0 -0
  77. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/docs.py +0 -0
  78. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/env.py +0 -0
  79. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/frond/FROND.md +0 -0
  80. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/frond/__init__.py +0 -0
  81. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/frond/compiler.py +0 -0
  82. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/frond/parser.py +0 -0
  83. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/auth/meta.json +0 -0
  84. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  85. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/database/meta.json +0 -0
  86. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  87. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/error-overlay/meta.json +0 -0
  88. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  89. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/orm/meta.json +0 -0
  90. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  91. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  92. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/queue/meta.json +0 -0
  93. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  94. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/rest-api/meta.json +0 -0
  95. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  96. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/templates/meta.json +0 -0
  97. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  98. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  99. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/graphql/__init__.py +0 -0
  100. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/i18n/__init__.py +0 -0
  101. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/mcp/__init__.py +0 -0
  102. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/mcp/protocol.py +0 -0
  103. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/migration/__init__.py +0 -0
  104. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/migration/runner.py +0 -0
  105. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/mqtt/message.py +0 -0
  106. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/orm/__init__.py +0 -0
  107. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/orm/fields.py +0 -0
  108. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/__feedback/widget.js +0 -0
  109. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/css/tina4.css +0 -0
  110. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/css/tina4.min.css +0 -0
  111. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/favicon.ico +0 -0
  112. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/images/logo.svg +0 -0
  113. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  114. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/js/frond.js +0 -0
  115. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/js/frond.min.js +0 -0
  116. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  117. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/js/tina4.min.js +0 -0
  118. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/js/tina4js.min.js +0 -0
  119. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/swagger/index.html +0 -0
  120. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  121. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/query_builder/__init__.py +0 -0
  122. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/queue/job.py +0 -0
  123. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/queue_backends/__init__.py +0 -0
  124. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/realtime/__init__.py +0 -0
  125. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/realtime/models/Attachment.py +0 -0
  126. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/realtime/models/Channel.py +0 -0
  127. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/realtime/models/ChannelMember.py +0 -0
  128. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/realtime/models/Message.py +0 -0
  129. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/realtime/models/Workspace.py +0 -0
  130. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/realtime/models/__init__.py +0 -0
  131. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/realtime/storage.py +0 -0
  132. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/__init__.py +0 -0
  133. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_alerts.scss +0 -0
  134. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_badges.scss +0 -0
  135. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_buttons.scss +0 -0
  136. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_cards.scss +0 -0
  137. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_forms.scss +0 -0
  138. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_grid.scss +0 -0
  139. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_modals.scss +0 -0
  140. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_nav.scss +0 -0
  141. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_pagination.scss +0 -0
  142. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_reset.scss +0 -0
  143. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_tables.scss +0 -0
  144. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_typography.scss +0 -0
  145. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_utilities.scss +0 -0
  146. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/_variables.scss +0 -0
  147. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/base.scss +0 -0
  148. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/colors.scss +0 -0
  149. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/scss/tina4css/tina4.scss +0 -0
  150. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/seeder/__init__.py +0 -0
  151. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/service/__init__.py +0 -0
  152. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/swagger/__init__.py +0 -0
  153. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/components/crud.twig +0 -0
  154. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  155. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  156. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/docker/python/Dockerfile +0 -0
  157. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  158. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/errors/302.twig +0 -0
  159. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/errors/401.twig +0 -0
  160. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/errors/403.twig +0 -0
  161. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/errors/404.twig +0 -0
  162. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/errors/500.twig +0 -0
  163. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/errors/502.twig +0 -0
  164. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/errors/503.twig +0 -0
  165. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/errors/base.twig +0 -0
  166. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/frontend/README.md +0 -0
  167. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/templates/readme.md +0 -0
  168. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/test/__init__.py +0 -0
  169. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/test_client/__init__.py +0 -0
  170. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  171. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  172. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  173. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  174. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  175. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  176. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  177. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  178. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  179. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  180. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  181. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  182. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/validator/__init__.py +0 -0
  183. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/websocket/backplane.py +0 -0
  184. {tina4_python-3.13.92 → tina4_python-3.13.95}/tina4_python/wsdl/__init__.py +0 -0
@@ -10,6 +10,13 @@
10
10
  .env.local
11
11
  /public/css/test.css
12
12
  /sessions/
13
+ # Runtime session files written by the suite. This was the ONE gap of the four:
14
+ # php ignores /data/, node ignores data/, ruby ignores sessions/ (and defaults to
15
+ # pwd/sessions anyway) - only python left data/sessions/ visible, so a plain test
16
+ # run dirtied the tree and `git add -A` committed session payloads. It did: five
17
+ # of them are tracked on feature/audit-auth. FileSessionHandler already does
18
+ # mkdir(parents=True, exist_ok=True), so a fresh clone needs nothing here.
19
+ /data/sessions/
13
20
  /tests/secrets/domain.cert
14
21
  /tests/secrets/private.key
15
22
  /tests/secrets/public.key
@@ -98,3 +105,8 @@ example/src/**/__pycache__/
98
105
  # relative to cwd would create these under the repo. Never commit them.
99
106
  /private/
100
107
  /var/folders/
108
+ /data/sessions/
109
+
110
+ # Local file-backend queue store - runtime artefacts, never committed.
111
+ # Four probe .queue-data files were swept in by `git add -A` on 2026-08-03.
112
+ data/queue/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tina4-python
3
- Version: 3.13.92
3
+ Version: 3.13.95
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
@@ -14,6 +14,8 @@ Requires-Dist: pymssql>=2.3; extra == 'all-db'
14
14
  Requires-Dist: pyodbc>=5.0; extra == 'all-db'
15
15
  Provides-Extra: firebird
16
16
  Requires-Dist: firebird-driver>=1.10; extra == 'firebird'
17
+ Provides-Extra: kafka
18
+ Requires-Dist: confluent-kafka>=2.0; extra == 'kafka'
17
19
  Provides-Extra: memcache
18
20
  Requires-Dist: pymemcache>=4.0; extra == 'memcache'
19
21
  Provides-Extra: mongo
@@ -26,8 +28,24 @@ Provides-Extra: odbc
26
28
  Requires-Dist: pyodbc>=5.0; extra == 'odbc'
27
29
  Provides-Extra: postgres
28
30
  Requires-Dist: psycopg2-binary>=2.9; extra == 'postgres'
31
+ Provides-Extra: rabbitmq
32
+ Requires-Dist: pika>=1.3; extra == 'rabbitmq'
29
33
  Provides-Extra: redis
30
34
  Requires-Dist: redis>=5.0; extra == 'redis'
35
+ Provides-Extra: s3
36
+ Requires-Dist: boto3>=1.34; extra == 's3'
37
+ Provides-Extra: test
38
+ Requires-Dist: boto3>=1.34; extra == 'test'
39
+ Requires-Dist: confluent-kafka>=2.0; extra == 'test'
40
+ Requires-Dist: firebird-driver>=1.10; extra == 'test'
41
+ Requires-Dist: mysql-connector-python>=9.0; extra == 'test'
42
+ Requires-Dist: pika>=1.3; extra == 'test'
43
+ Requires-Dist: psycopg2-binary>=2.9; extra == 'test'
44
+ Requires-Dist: pymemcache>=4.0; extra == 'test'
45
+ Requires-Dist: pymongo>=4.0; extra == 'test'
46
+ Requires-Dist: pymssql>=2.3; extra == 'test'
47
+ Requires-Dist: pyodbc>=5.0; extra == 'test'
48
+ Requires-Dist: redis>=5.0; extra == 'test'
31
49
  Description-Content-Type: text/markdown
32
50
 
33
51
  <p align="center">
@@ -116,7 +134,7 @@ Every feature is built from scratch -- no pip install, no node_modules, no third
116
134
  | **Core HTTP** (7) | Router with path params (`{id:int}`, `{p:path}`), Server, Request/Response, Middleware pipeline, Static file serving, CORS |
117
135
  | **Database** (6) | SQLite, PostgreSQL, MySQL, MSSQL, Firebird: unified adapter, connection pooling, query cache, transactions, race-safe ID generation, SQL dialect translation |
118
136
  | **ORM** (7) | Active Record with typed fields, relationships (`has_one`/`has_many`/`belongs_to`), soft delete, QueryBuilder + MongoDB support, Auto-CRUD generator, migrations with rollback |
119
- | **Auth & Security** (5) | JWT (HS256/RS256), password hashing (PBKDF2-SHA256), API key validation, rate limiting, CSRF form tokens |
137
+ | **Auth & Security** (5) | JWT (HS256/HS384/HS512, zero-dependency; RS256 opt-in, needs `cryptography`), password hashing (PBKDF2-SHA256), API key validation, rate limiting, CSRF form tokens |
120
138
  | **Templating** (3) | Frond engine (Twig/Jinja2-compatible, pre-compiled 2.8x faster), SCSS auto-compilation, built-in CSS (~24 KB) |
121
139
  | **API & Integration** (5) | HTTP client (zero-dep), GraphQL with ORM auto-schema + GraphiQL IDE, WSDL/SOAP with auto WSDL, WebSocket (RFC 6455) + Redis backplane, MCP server (24 dev tools) |
122
140
  | **Background** (3) | Job queue (File/RabbitMQ/Kafka/MongoDB) with priority, delay, retry, dead letters; service runner; event system (on/emit/once/off) |
@@ -742,7 +760,7 @@ Run benchmarks locally: `python benchmarks/benchmark.py --python`
742
760
 
743
761
  ## Cross-Framework Parity
744
762
 
745
- Tina4 ships identical features across four languages: same architecture, same conventions, same 97 features:
763
+ Tina4 ships identical features across four languages: same architecture, same conventions, same 98 features:
746
764
 
747
765
  | | Python | PHP | Ruby | Node.js |
748
766
  |---|--------|-----|------|---------|
@@ -84,7 +84,7 @@ Every feature is built from scratch -- no pip install, no node_modules, no third
84
84
  | **Core HTTP** (7) | Router with path params (`{id:int}`, `{p:path}`), Server, Request/Response, Middleware pipeline, Static file serving, CORS |
85
85
  | **Database** (6) | SQLite, PostgreSQL, MySQL, MSSQL, Firebird: unified adapter, connection pooling, query cache, transactions, race-safe ID generation, SQL dialect translation |
86
86
  | **ORM** (7) | Active Record with typed fields, relationships (`has_one`/`has_many`/`belongs_to`), soft delete, QueryBuilder + MongoDB support, Auto-CRUD generator, migrations with rollback |
87
- | **Auth & Security** (5) | JWT (HS256/RS256), password hashing (PBKDF2-SHA256), API key validation, rate limiting, CSRF form tokens |
87
+ | **Auth & Security** (5) | JWT (HS256/HS384/HS512, zero-dependency; RS256 opt-in, needs `cryptography`), password hashing (PBKDF2-SHA256), API key validation, rate limiting, CSRF form tokens |
88
88
  | **Templating** (3) | Frond engine (Twig/Jinja2-compatible, pre-compiled 2.8x faster), SCSS auto-compilation, built-in CSS (~24 KB) |
89
89
  | **API & Integration** (5) | HTTP client (zero-dep), GraphQL with ORM auto-schema + GraphiQL IDE, WSDL/SOAP with auto WSDL, WebSocket (RFC 6455) + Redis backplane, MCP server (24 dev tools) |
90
90
  | **Background** (3) | Job queue (File/RabbitMQ/Kafka/MongoDB) with priority, delay, retry, dead letters; service runner; event system (on/emit/once/off) |
@@ -710,7 +710,7 @@ Run benchmarks locally: `python benchmarks/benchmark.py --python`
710
710
 
711
711
  ## Cross-Framework Parity
712
712
 
713
- Tina4 ships identical features across four languages: same architecture, same conventions, same 97 features:
713
+ Tina4 ships identical features across four languages: same architecture, same conventions, same 98 features:
714
714
 
715
715
  | | Python | PHP | Ruby | Node.js |
716
716
  |---|--------|-----|------|---------|
@@ -0,0 +1,121 @@
1
+ [project]
2
+ name = "tina4-python"
3
+ version = "3.13.95"
4
+ description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
5
+ authors = [
6
+ {name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
7
+ ]
8
+ readme = "README.md"
9
+ requires-python = ">=3.12"
10
+ license = {text = "MIT"}
11
+
12
+ # ZERO core dependencies — this is v3's defining feature
13
+ dependencies = []
14
+
15
+ [project.optional-dependencies]
16
+ # Database drivers — install what you need
17
+ postgres = ["psycopg2-binary>=2.9"]
18
+ mysql = ["mysql-connector-python>=9.0"]
19
+ mssql = ["pymssql>=2.3"]
20
+ firebird = ["firebird-driver>=1.10"]
21
+ mongo = ["pymongo>=4.0"]
22
+ odbc = ["pyodbc>=5.0"]
23
+
24
+ # Session backends — install what you need
25
+ redis = ["redis>=5.0"]
26
+ memcache = ["pymemcache>=4.0"]
27
+
28
+ # File-storage backends — install what you need.
29
+ # The `local` filesystem backend is the zero-dependency default; S3Storage
30
+ # (TINA4_STORAGE_BACKEND=s3) is opt-in and imports boto3 lazily.
31
+ s3 = ["boto3>=1.34"]
32
+
33
+ # Queue backends — install what you need
34
+ kafka = ["confluent-kafka>=2.0"]
35
+ rabbitmq = ["pika>=1.3"]
36
+
37
+ # All database drivers
38
+ all-db = [
39
+ "psycopg2-binary>=2.9",
40
+ "mysql-connector-python>=9.0",
41
+ "pymssql>=2.3",
42
+ "firebird-driver>=1.10",
43
+ "pymongo>=4.0",
44
+ "pyodbc>=5.0",
45
+ ]
46
+
47
+ # Every client the TEST SUITE needs against live services.
48
+ #
49
+ # These were installed ad hoc by CI (`uv pip install pymongo confluent-kafka
50
+ # pika ...`) and declared nowhere, so a fresh host had no way to know what the
51
+ # suite requires. confluent-kafka and pika in particular were in NO extra: the
52
+ # lab host's first full-suite run failed with three "confluent-kafka not
53
+ # installed" errors, correctly raised by the TINA4_REQUIRE_SERVICES gate, and
54
+ # `uv sync --all-extras` could not have fixed it because the dependency was not
55
+ # expressed anywhere. One declared extra means `uv sync --extra test` is
56
+ # reproducible on any machine.
57
+ #
58
+ # Includes firebird-driver even though the Firebird CI job installs it on its
59
+ # own: `uv sync --extra X` PRUNES anything outside the named extras, so a test
60
+ # extra that omitted it would silently uninstall it on a host that had it.
61
+ #
62
+ # boto3 is here for exactly that reason, MEASURED on the lab 2026-08-06
63
+ # (Ubuntu 24.04.4 LTS x86_64, Python 3.13.3): it was declared in NO extra, but
64
+ # happened to be installed ad hoc, so the two REAL-MinIO tests in
65
+ # tests/test_realtime_files.py were passing. Running `uv sync --extra test`
66
+ # pruned boto3 (plus s3transfer and jmespath) and those tests went straight from
67
+ # "2 passed" to "2 skipped" -- with the suite still GREEN under
68
+ # TINA4_REQUIRE_SERVICES=1, because the skip reason matched neither a service
69
+ # keyword nor an unavailable hint in the conftest gate. Live S3 coverage
70
+ # evaporated silently. Declaring it here is the fix; tests/conftest.py now also
71
+ # recognises a missing boto3 so the gate can never miss it again.
72
+ test = [
73
+ "psycopg2-binary>=2.9",
74
+ "mysql-connector-python>=9.0",
75
+ "pymssql>=2.3",
76
+ "firebird-driver>=1.10",
77
+ "pymongo>=4.0",
78
+ "pyodbc>=5.0",
79
+ "redis>=5.0",
80
+ "pymemcache>=4.0",
81
+ "confluent-kafka>=2.0",
82
+ "pika>=1.3",
83
+ "boto3>=1.34",
84
+ ]
85
+
86
+ [dependency-groups]
87
+ dev = [
88
+ "bottle>=0.13.4",
89
+ "cython>=3.2.4",
90
+ "django>=6.0.3",
91
+ "fastapi>=0.135.1",
92
+ "flask>=3.1.3",
93
+ "pytest>=8.0",
94
+ "pytest-asyncio>=0.23",
95
+ "pytest-cov>=5.0",
96
+ "reportlab>=4.4.10",
97
+ "ruff>=0.4",
98
+ "setuptools>=82.0.1",
99
+ "uvicorn>=0.42.0",
100
+ ]
101
+
102
+ [build-system]
103
+ requires = ["hatchling"]
104
+ build-backend = "hatchling.build"
105
+
106
+ [tool.hatch.build]
107
+ include = ["tina4_python/**/*"]
108
+
109
+ [project.scripts]
110
+ tina4python = "tina4_python.cli:main"
111
+
112
+ [tool.pytest.ini_options]
113
+ asyncio_mode = "auto"
114
+ testpaths = ["tests"]
115
+ markers = [
116
+ "slow: a test that needs real wall-clock time to reproduce a timing bug (deselect with -m 'not slow')",
117
+ ]
118
+
119
+ [tool.ruff]
120
+ target-version = "py312"
121
+ line-length = 120
@@ -514,8 +514,11 @@ TINA4_TOKEN_LIMIT is used to set the session time, default 60 minutes
514
514
 
515
515
  Set `TINA4_SESSION_BACKEND` to choose a backend:
516
516
 
517
- The value is the backend NAME, not a class name. An unknown value falls back to
518
- the file backend without raising.
517
+ The value is the backend NAME, not a class name, and it is normalised (trimmed and
518
+ lowercased), so " Redis " resolves. An UNRECOGNISED value raises `ValueError` at
519
+ startup, naming the bad value and the valid ones -- it used to fall back to the
520
+ file backend silently, which turned a typo into sessions quietly written to local
521
+ disk. Unset or blank still means file.
519
522
 
520
523
  | Value | Backend | Required package |
521
524
  |---------|---------|-----------------|
@@ -794,11 +797,18 @@ db.insert("users", {"name": "Alice", "email": "alice@example.com"})
794
797
  # Insert multiple
795
798
  db.insert("users", [{"name": "Bob"}, {"name": "Eve"}])
796
799
 
797
- # Update (by primary key — default "id")
800
+ # Update (the primary key in the data IS the filter introspected, not assumed)
798
801
  db.update("users", {"id": 1, "name": "Alice Updated"})
799
802
 
800
- # Delete
803
+ # Update many rows — the filter must be explicit
804
+ db.update("users", {"active": 0}, "last_login < ?", ["2025-01-01"])
805
+
806
+ # Delete (dict filter or string + params, both accepted)
801
807
  db.delete("users", {"id": 1})
808
+ db.delete("users", "age < ?", [18])
809
+
810
+ # Empty a table — explicit, never accidental
811
+ db.truncate("users")
802
812
 
803
813
  # Raw query
804
814
  result = db.fetch("SELECT * FROM users WHERE age > ?", [18])
@@ -1634,17 +1644,23 @@ TINA4_TOKEN_LIMIT=60 # Token lifetime in minutes (default: 60)
1634
1644
  TINA4_DATABASE_URL=sqlite:///app.db # Connection URL (driver://host:port/database)
1635
1645
  TINA4_DATABASE_USERNAME= # DB username (for PostgreSQL, MySQL, etc.)
1636
1646
  TINA4_DATABASE_PASSWORD= # DB password
1647
+ TINA4_DATABASE_CONNECT_TIMEOUT=10 # Seconds a database connect may block (default 10). Set <= 0 to wait
1648
+ # indefinitely (the old behaviour); a non-numeric value warns and uses 10.
1649
+ # On expiry the error names the host, port, elapsed seconds and this variable.
1650
+ # Applies to postgres/mysql/mssql/odbc/firebird, and to mongodb's deferred
1651
+ # connect. SQLite is exempt: it opens a local file, there is no peer to hang on
1637
1652
 
1638
1653
  # Framework
1639
1654
  TINA4_DEBUG=true # Enable dev mode (toolbar, live reload, error overlay)
1640
1655
  TINA4_LOG_LEVEL=INFO # Log verbosity: ALL, DEBUG, INFO, WARNING, ERROR, CRITICAL (default: INFO)
1641
1656
  TINA4_LOCALE=en # Language for framework messages (en, fr, af, zh, ja, es)
1642
- TINA4_DEFAULT_WEBSERVER=FALSE # Set to TRUE to use Tina4's built-in webserver instead of ASGI
1657
+ TINA4_DEFAULT_WEBSERVER=FALSE # TRUE pins Tina4's built-in asyncio webserver even when a production ASGI server (uvicorn/hypercorn/granian) is installed. Unset/FALSE: a production server is used when one is importable and TINA4_DEBUG is off
1658
+ TINA4_SHUTDOWN_TIMEOUT=30 # Seconds to drain in-flight requests on SIGTERM/SIGINT (default 30). Honoured on the built-in server, and mapped onto uvicorn's timeout_graceful_shutdown / hypercorn's graceful_timeout. granian has no drain deadline, so it is not honoured there (logged at startup). A non-positive or unparseable value warns and uses 30
1643
1659
  TINA4_OVERRIDE_CLIENT=false # Set to true to allow running without tina4 CLI (e.g. Docker)
1644
1660
  HOST_NAME=localhost:7145
1645
1661
 
1646
1662
  # Sessions
1647
- TINA4_SESSION_BACKEND=file # file, redis, valkey, mongodb, database (unknown value falls back to file)
1663
+ TINA4_SESSION_BACKEND=file # file, redis, valkey, mongodb, memcached, database (an unknown value RAISES; unset/blank = file)
1648
1664
  TINA4_SESSION_SAMESITE=Lax # SameSite attribute for session cookies (default: Lax)
1649
1665
 
1650
1666
  # Swagger/OpenAPI
@@ -1706,7 +1722,7 @@ anywhere (the production on/off switch, wired for real in 3.13.40).
1706
1722
 
1707
1723
  ## CORS
1708
1724
 
1709
- Built-in all origins allowed by default. CORS headers and OPTIONS pre-flight are handled automatically.
1725
+ Built-in, and it DENIES by default (ADR-0018). Set `TINA4_CORS_ORIGINS` to the origins you want to allow (comma-separated), or `*` for any. With it unset no `Access-Control-Allow-Origin` is sent and the browser blocks the cross-origin request. `TINA4_CORS_CREDENTIALS=true` is never sent alongside a `*` origin (the Fetch Standard forbids the pair). OPTIONS pre-flight is handled automatically.
1710
1726
 
1711
1727
  ## SCSS Workflow
1712
1728
 
@@ -43,7 +43,15 @@ def _resolve_version() -> str:
43
43
  pass
44
44
  except Exception:
45
45
  pass
46
- return "3.13.56"
46
+ # Floor literal, reached only when BOTH lookups above miss. That is not
47
+ # theoretical: the Docker build used to prune dist-info from the installed
48
+ # package, so path 2 raised PackageNotFoundError and the published
49
+ # tina4-python:3.13.94 image served this literal on /health -- then reading
50
+ # "3.13.56", 36 releases stale, because nothing kept it current.
51
+ #
52
+ # test_version_constant.py now asserts this literal equals the pyproject
53
+ # version, so the release bump cannot leave it behind again.
54
+ return "3.13.95"
47
55
 
48
56
 
49
57
  __version__ = _resolve_version()