tina4-python 3.13.14__tar.gz → 3.13.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 (157) hide show
  1. {tina4_python-3.13.14 → tina4_python-3.13.15}/PKG-INFO +1 -1
  2. {tina4_python-3.13.14 → tina4_python-3.13.15}/pyproject.toml +1 -1
  3. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/__init__.py +1 -1
  4. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/database/postgres.py +40 -1
  5. {tina4_python-3.13.14 → tina4_python-3.13.15}/.gitignore +0 -0
  6. {tina4_python-3.13.14 → tina4_python-3.13.15}/README.md +0 -0
  7. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/CLAUDE.md +0 -0
  8. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/HtmlElement.py +0 -0
  9. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/Testing.py +0 -0
  10. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/ai/__init__.py +0 -0
  11. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/api/__init__.py +0 -0
  12. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/auth/__init__.py +0 -0
  13. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/cache/__init__.py +0 -0
  14. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/cli/__init__.py +0 -0
  15. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/container/__init__.py +0 -0
  16. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/core/__init__.py +0 -0
  17. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/core/cache.py +0 -0
  18. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/core/constants.py +0 -0
  19. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/core/events.py +0 -0
  20. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/core/middleware.py +0 -0
  21. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/core/rate_limiter.py +0 -0
  22. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/core/request.py +0 -0
  23. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/core/response.py +0 -0
  24. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/core/router.py +0 -0
  25. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/core/server.py +0 -0
  26. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/crud/__init__.py +0 -0
  27. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/database/__init__.py +0 -0
  28. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/database/adapter.py +0 -0
  29. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/database/connection.py +0 -0
  30. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/database/firebird.py +0 -0
  31. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/database/mongodb.py +0 -0
  32. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/database/mssql.py +0 -0
  33. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/database/mysql.py +0 -0
  34. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/database/odbc.py +0 -0
  35. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/database/sqlite.py +0 -0
  36. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/debug/__init__.py +0 -0
  37. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/debug/error_overlay.py +0 -0
  38. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/dev_admin/__init__.py +0 -0
  39. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/dev_admin/metrics.py +0 -0
  40. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/dev_admin/plan.py +0 -0
  41. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/dev_admin/project_index.py +0 -0
  42. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/docs.py +0 -0
  43. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/dotenv/__init__.py +0 -0
  44. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/env.py +0 -0
  45. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/frond/FROND.md +0 -0
  46. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/frond/__init__.py +0 -0
  47. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/frond/engine.py +0 -0
  48. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/auth/meta.json +0 -0
  49. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  50. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/database/meta.json +0 -0
  51. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  52. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/error-overlay/meta.json +0 -0
  53. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  54. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/orm/meta.json +0 -0
  55. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  56. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  57. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/queue/meta.json +0 -0
  58. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  59. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/rest-api/meta.json +0 -0
  60. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  61. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/templates/meta.json +0 -0
  62. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  63. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  64. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/graphql/__init__.py +0 -0
  65. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/i18n/__init__.py +0 -0
  66. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/mcp/__init__.py +0 -0
  67. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/mcp/protocol.py +0 -0
  68. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/mcp/tools.py +0 -0
  69. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/messenger/__init__.py +0 -0
  70. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/migration/__init__.py +0 -0
  71. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/migration/runner.py +0 -0
  72. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/orm/__init__.py +0 -0
  73. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/orm/fields.py +0 -0
  74. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/orm/model.py +0 -0
  75. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/__feedback/widget.js +0 -0
  76. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/css/tina4.css +0 -0
  77. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/css/tina4.min.css +0 -0
  78. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/favicon.ico +0 -0
  79. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/images/logo.svg +0 -0
  80. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  81. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/js/frond.js +0 -0
  82. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/js/frond.min.js +0 -0
  83. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/js/tina4-dev-admin.js +0 -0
  84. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  85. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/js/tina4.min.js +0 -0
  86. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/js/tina4js.min.js +0 -0
  87. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/swagger/index.html +0 -0
  88. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  89. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/query_builder/__init__.py +0 -0
  90. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/queue/__init__.py +0 -0
  91. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/queue/job.py +0 -0
  92. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/queue/kafka_backend.py +0 -0
  93. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/queue/lite_backend.py +0 -0
  94. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/queue/mongo_backend.py +0 -0
  95. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/queue/rabbitmq_backend.py +0 -0
  96. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/queue_backends/__init__.py +0 -0
  97. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/queue_backends/kafka_backend.py +0 -0
  98. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/queue_backends/mongo_backend.py +0 -0
  99. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
  100. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/__init__.py +0 -0
  101. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_alerts.scss +0 -0
  102. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_badges.scss +0 -0
  103. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_buttons.scss +0 -0
  104. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_cards.scss +0 -0
  105. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_forms.scss +0 -0
  106. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_grid.scss +0 -0
  107. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_modals.scss +0 -0
  108. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_nav.scss +0 -0
  109. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_reset.scss +0 -0
  110. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_tables.scss +0 -0
  111. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_typography.scss +0 -0
  112. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_utilities.scss +0 -0
  113. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/_variables.scss +0 -0
  114. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/base.scss +0 -0
  115. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/colors.scss +0 -0
  116. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/scss/tina4css/tina4.scss +0 -0
  117. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/seeder/__init__.py +0 -0
  118. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/service/__init__.py +0 -0
  119. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/session/__init__.py +0 -0
  120. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/session_handlers/__init__.py +0 -0
  121. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/session_handlers/mongodb_handler.py +0 -0
  122. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/session_handlers/redis_handler.py +0 -0
  123. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/session_handlers/valkey_handler.py +0 -0
  124. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/swagger/__init__.py +0 -0
  125. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/components/crud.twig +0 -0
  126. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  127. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  128. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/docker/python/Dockerfile +0 -0
  129. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  130. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/errors/302.twig +0 -0
  131. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/errors/401.twig +0 -0
  132. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/errors/403.twig +0 -0
  133. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/errors/404.twig +0 -0
  134. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/errors/500.twig +0 -0
  135. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/errors/502.twig +0 -0
  136. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/errors/503.twig +0 -0
  137. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/errors/base.twig +0 -0
  138. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/frontend/README.md +0 -0
  139. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/templates/readme.md +0 -0
  140. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/test/__init__.py +0 -0
  141. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/test_client/__init__.py +0 -0
  142. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  143. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  144. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  145. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  146. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  147. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  148. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  149. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  150. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  151. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  152. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  153. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  154. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/validator/__init__.py +0 -0
  155. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/websocket/__init__.py +0 -0
  156. {tina4_python-3.13.14 → tina4_python-3.13.15}/tina4_python/websocket/backplane.py +0 -0
  157. {tina4_python-3.13.14 → tina4_python-3.13.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.13.14
3
+ Version: 3.13.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.13.14"
3
+ version = "3.13.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"}
@@ -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.13.14"
11
+ __version__ = "3.13.15"
12
12
 
13
13
  # ── Route decorators ──
14
14
  from tina4_python.core.router import ( # noqa: E402, F401
@@ -207,6 +207,35 @@ class PostgreSQLAdapter(DatabaseAdapter):
207
207
  except Exception:
208
208
  pass
209
209
 
210
+ def _end_read_txn(self):
211
+ """v3.13.15 (issue #51): close the implicit transaction a read
212
+ opens, so the connection doesn't leak as ``idle in transaction``.
213
+
214
+ psycopg2 runs with ``autocommit = False`` (the framework's safe
215
+ default — writes must be committed explicitly). A bare ``SELECT``
216
+ therefore opens a transaction too, and with nothing to commit it
217
+ sits ``idle in transaction`` for the life of the connection,
218
+ holding a pool slot and any locks it touched. Short-lived boot
219
+ connections (the migration runner's ``MAX(batch)`` lookup) leak
220
+ one each, eventually exhausting ``max_connections`` — at which
221
+ point module autodiscovery fails mid-boot and every route 404s
222
+ while ``/health-check`` still passes ("ready but broken").
223
+
224
+ A read has nothing to persist, so a ROLLBACK is the safe close.
225
+ Inside an explicit ``start_transaction()`` the caller owns the
226
+ transaction, so we defer (mirrors the heal / error-path guards).
227
+ Writes go through :meth:`execute`, which is deliberately left
228
+ untouched — with autocommit off they must be committed explicitly,
229
+ so auto-closing them here would silently drop data.
230
+ """
231
+ if self._in_transaction or self._conn is None:
232
+ return
233
+ try:
234
+ self._conn.rollback()
235
+ except Exception:
236
+ # Never let bookkeeping mask the rows we already fetched.
237
+ pass
238
+
210
239
  def connect(self, connection_string: str, username: str = "", password: str = "", **kwargs):
211
240
  """Connect to PostgreSQL.
212
241
 
@@ -357,6 +386,10 @@ class PostgreSQLAdapter(DatabaseAdapter):
357
386
  self._exec_with_handling(cursor, paginated_sql, paginated_params)
358
387
  rows = [self._decode_blobs(dict(row)) for row in cursor.fetchall()]
359
388
 
389
+ # v3.13.15 (#51): rows are materialised above — close the implicit
390
+ # read transaction so the connection doesn't sit idle-in-transaction.
391
+ self._end_read_txn()
392
+
360
393
  return DatabaseResult(records=rows, count=total, limit=limit, offset=offset, sql=sql, adapter=self)
361
394
 
362
395
  def fetch_one(self, sql: str, params: list = None) -> dict | None:
@@ -368,7 +401,13 @@ class PostgreSQLAdapter(DatabaseAdapter):
368
401
  cursor = self._conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
369
402
  self._exec_with_handling(cursor, sql, params)
370
403
  row = cursor.fetchone()
371
- return self._decode_blobs(dict(row)) if row else None
404
+ result = self._decode_blobs(dict(row)) if row else None
405
+ # v3.13.15 (#51): close the implicit read transaction so the
406
+ # connection doesn't sit 'idle in transaction' (psycopg2 autocommit
407
+ # is off). This is the path the migration runner's MAX(batch)
408
+ # lookup leaked through.
409
+ self._end_read_txn()
410
+ return result
372
411
 
373
412
  @staticmethod
374
413
  def _decode_blobs(row: dict) -> dict:
File without changes