tina4-python 3.10.13__tar.gz → 3.10.15__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.10.13 → tina4_python-3.10.15}/PKG-INFO +1 -1
  2. {tina4_python-3.10.13 → tina4_python-3.10.15}/pyproject.toml +1 -1
  3. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/database/connection.py +49 -0
  4. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/frond/engine.py +25 -3
  5. {tina4_python-3.10.13 → tina4_python-3.10.15}/.gitignore +0 -0
  6. {tina4_python-3.10.13 → tina4_python-3.10.15}/README.md +0 -0
  7. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/CLAUDE.md +0 -0
  8. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/HtmlElement.py +0 -0
  9. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/Testing.py +0 -0
  10. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/__init__.py +0 -0
  11. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/ai/__init__.py +0 -0
  12. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/api/__init__.py +0 -0
  13. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/auth/__init__.py +0 -0
  14. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/cache/__init__.py +0 -0
  15. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/cli/__init__.py +0 -0
  16. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/container/__init__.py +0 -0
  17. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/core/__init__.py +0 -0
  18. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/core/cache.py +0 -0
  19. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/core/constants.py +0 -0
  20. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/core/events.py +0 -0
  21. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/core/middleware.py +0 -0
  22. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/core/request.py +0 -0
  23. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/core/response.py +0 -0
  24. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/core/router.py +0 -0
  25. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/core/server.py +0 -0
  26. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/crud/__init__.py +0 -0
  27. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/database/__init__.py +0 -0
  28. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/database/adapter.py +0 -0
  29. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/database/firebird.py +0 -0
  30. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/database/mssql.py +0 -0
  31. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/database/mysql.py +0 -0
  32. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/database/odbc.py +0 -0
  33. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/database/postgres.py +0 -0
  34. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/database/sqlite.py +0 -0
  35. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/debug/__init__.py +0 -0
  36. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/debug/error_overlay.py +0 -0
  37. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/dev_admin/__init__.py +0 -0
  38. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/dev_reload.py +0 -0
  39. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/dotenv/__init__.py +0 -0
  40. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/frond/FROND.md +0 -0
  41. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/frond/__init__.py +0 -0
  42. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/auth/meta.json +0 -0
  43. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  44. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/database/meta.json +0 -0
  45. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  46. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/error-overlay/meta.json +0 -0
  47. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  48. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/orm/meta.json +0 -0
  49. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  50. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  51. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/queue/meta.json +0 -0
  52. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  53. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/rest-api/meta.json +0 -0
  54. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  55. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/templates/meta.json +0 -0
  56. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  57. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  58. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/graphql/__init__.py +0 -0
  59. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/i18n/__init__.py +0 -0
  60. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/messenger/__init__.py +0 -0
  61. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/migration/__init__.py +0 -0
  62. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/migration/runner.py +0 -0
  63. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/orm/__init__.py +0 -0
  64. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/orm/fields.py +0 -0
  65. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/orm/model.py +0 -0
  66. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/public/css/tina4.css +0 -0
  67. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/public/css/tina4.min.css +0 -0
  68. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/public/favicon.ico +0 -0
  69. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/public/images/logo.svg +0 -0
  70. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  71. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/public/js/frond.min.js +0 -0
  72. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  73. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/public/js/tina4.min.js +0 -0
  74. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/public/js/tina4js.min.js +0 -0
  75. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/public/swagger/index.html +0 -0
  76. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  77. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/query_builder/__init__.py +0 -0
  78. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/queue/__init__.py +0 -0
  79. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/queue_backends/__init__.py +0 -0
  80. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/queue_backends/kafka_backend.py +0 -0
  81. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/queue_backends/mongo_backend.py +0 -0
  82. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
  83. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/__init__.py +0 -0
  84. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_alerts.scss +0 -0
  85. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_badges.scss +0 -0
  86. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_buttons.scss +0 -0
  87. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_cards.scss +0 -0
  88. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_forms.scss +0 -0
  89. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_grid.scss +0 -0
  90. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_modals.scss +0 -0
  91. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_nav.scss +0 -0
  92. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_reset.scss +0 -0
  93. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_tables.scss +0 -0
  94. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_typography.scss +0 -0
  95. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_utilities.scss +0 -0
  96. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/_variables.scss +0 -0
  97. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/base.scss +0 -0
  98. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/colors.scss +0 -0
  99. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/scss/tina4css/tina4.scss +0 -0
  100. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/seeder/__init__.py +0 -0
  101. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/service/__init__.py +0 -0
  102. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/session/__init__.py +0 -0
  103. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/session_handlers/__init__.py +0 -0
  104. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/session_handlers/mongodb_handler.py +0 -0
  105. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/session_handlers/redis_handler.py +0 -0
  106. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/session_handlers/valkey_handler.py +0 -0
  107. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/swagger/__init__.py +0 -0
  108. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/components/crud.twig +0 -0
  109. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  110. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  111. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/docker/python/Dockerfile +0 -0
  112. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  113. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/errors/302.twig +0 -0
  114. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/errors/401.twig +0 -0
  115. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/errors/403.twig +0 -0
  116. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/errors/404.twig +0 -0
  117. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/errors/500.twig +0 -0
  118. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/errors/502.twig +0 -0
  119. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/errors/503.twig +0 -0
  120. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/errors/base.twig +0 -0
  121. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/frontend/README.md +0 -0
  122. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/templates/readme.md +0 -0
  123. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/test_client/__init__.py +0 -0
  124. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  125. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  126. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  127. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  128. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  129. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  130. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  131. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  132. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  133. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  134. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  135. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  136. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/validator/__init__.py +0 -0
  137. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/websocket/__init__.py +0 -0
  138. {tina4_python-3.10.13 → tina4_python-3.10.15}/tina4_python/websocket/backplane.py +0 -0
  139. {tina4_python-3.10.13 → tina4_python-3.10.15}/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.10.13
3
+ Version: 3.10.15
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.10.13"
3
+ version = "3.10.15"
4
4
  description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
5
5
  authors = [
6
6
  {name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
@@ -383,6 +383,55 @@ class Database:
383
383
  elif self._adapter is not None:
384
384
  self._adapter.autocommit = value
385
385
 
386
+ def get_next_id(self, table: str, pk_column: str = "id", generator_name: str = None) -> int:
387
+ """Get the next available ID for a table.
388
+
389
+ Engine-specific strategies:
390
+ - Firebird: uses GEN_ID(generator, 1) to increment and return
391
+ - PostgreSQL: uses nextval(sequence) if sequence exists
392
+ - SQLite/MySQL/MSSQL: SELECT MAX(pk) + 1 (fallback)
393
+
394
+ Args:
395
+ table: Table name.
396
+ pk_column: Primary key column name (default: "id").
397
+ generator_name: Firebird generator or PostgreSQL sequence name.
398
+ Auto-detected if not provided.
399
+
400
+ Returns:
401
+ The next integer ID.
402
+ """
403
+ engine = self.get_database_type()
404
+
405
+ if engine == "firebird":
406
+ gen_name = generator_name or f"GEN_{table.upper()}_ID"
407
+ # Create generator if it doesn't exist
408
+ try:
409
+ self.execute(f"CREATE GENERATOR {gen_name}")
410
+ self.commit()
411
+ except Exception:
412
+ pass # Already exists
413
+ row = self.fetch_one(
414
+ f"SELECT GEN_ID({gen_name}, 1) AS next_id FROM RDB$DATABASE"
415
+ )
416
+ return row["next_id"] if row else 1
417
+
418
+ elif engine == "postgresql":
419
+ seq_name = generator_name or f"{table}_{pk_column}_seq"
420
+ try:
421
+ row = self.fetch_one(f"SELECT nextval('{seq_name}') AS next_id")
422
+ return row["next_id"] if row else 1
423
+ except Exception:
424
+ pass # Sequence doesn't exist, fall through to MAX
425
+
426
+ # Fallback: MAX(pk) + 1
427
+ try:
428
+ row = self.fetch_one(
429
+ f"SELECT MAX({pk_column}) AS max_id FROM {table}"
430
+ )
431
+ return (row["max_id"] or 0) + 1 if row else 1
432
+ except Exception:
433
+ return 1
434
+
386
435
  def register_function(self, name: str, num_params: int, func: callable, deterministic: bool = True):
387
436
  """Register a custom SQL function (SQLite only).
388
437
 
@@ -669,7 +669,7 @@ def _parse_filter_chain(expr: str) -> tuple[str, list[tuple[str, list[str]]]]:
669
669
 
670
670
 
671
671
  def _parse_args(raw: str) -> list[str]:
672
- """Parse filter arguments, respecting quoted strings."""
672
+ """Parse filter arguments, respecting quoted strings and backslash escapes."""
673
673
  args = []
674
674
  current = ""
675
675
  in_quote = None
@@ -685,17 +685,39 @@ def _parse_args(raw: str) -> list[str]:
685
685
  elif ch == ")" and not in_quote:
686
686
  depth -= 1
687
687
  elif ch == "," and not in_quote and depth == 0:
688
- args.append(current.strip().strip("'\""))
688
+ args.append(_strip_outer_quotes(current.strip()))
689
689
  current = ""
690
690
  continue
691
691
  current += ch
692
692
 
693
693
  if current.strip():
694
- args.append(current.strip().strip("'\""))
694
+ args.append(_strip_outer_quotes(current.strip()))
695
695
 
696
696
  return args
697
697
 
698
698
 
699
+ def _strip_outer_quotes(s: str) -> str:
700
+ """Remove only the outermost matching quotes from a string.
701
+
702
+ 'hello' → hello, "world" → world, \\' → \\' (no matching quotes).
703
+ Handles backslash escapes inside: "\\'" → \\' (preserves backslash).
704
+ """
705
+ if len(s) >= 2 and s[0] == s[-1] and s[0] in ('"', "'"):
706
+ inner = s[1:-1]
707
+ # Process backslash escapes: \' → ', \\" → ", \\\\ → \\
708
+ result = []
709
+ i = 0
710
+ while i < len(inner):
711
+ if inner[i] == '\\' and i + 1 < len(inner):
712
+ result.append(inner[i + 1])
713
+ i += 2
714
+ else:
715
+ result.append(inner[i])
716
+ i += 1
717
+ return "".join(result)
718
+ return s
719
+
720
+
699
721
  # Built-in filters
700
722
  _BUILTIN_FILTERS = {
701
723
  "upper": lambda v, *a: str(v).upper(),
File without changes