tina4-python 3.13.83__tar.gz → 3.13.85__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 (171) hide show
  1. {tina4_python-3.13.83 → tina4_python-3.13.85}/PKG-INFO +1 -1
  2. {tina4_python-3.13.83 → tina4_python-3.13.85}/pyproject.toml +1 -1
  3. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/cli/__init__.py +75 -13
  4. tina4_python-3.13.83/tina4_python/public/js/tina4-dev-admin.js +0 -1124
  5. {tina4_python-3.13.83 → tina4_python-3.13.85}/.gitignore +0 -0
  6. {tina4_python-3.13.83 → tina4_python-3.13.85}/README.md +0 -0
  7. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/CLAUDE.md +0 -0
  8. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/HtmlElement.py +0 -0
  9. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/Testing.py +0 -0
  10. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/__init__.py +0 -0
  11. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/ai/__init__.py +0 -0
  12. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/api/__init__.py +0 -0
  13. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/auth/__init__.py +0 -0
  14. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/cache/__init__.py +0 -0
  15. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/container/__init__.py +0 -0
  16. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/context/__init__.py +0 -0
  17. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/context/chunker.py +0 -0
  18. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/core/__init__.py +0 -0
  19. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/core/cache.py +0 -0
  20. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/core/constants.py +0 -0
  21. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/core/events.py +0 -0
  22. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/core/middleware.py +0 -0
  23. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/core/rate_limiter.py +0 -0
  24. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/core/request.py +0 -0
  25. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/core/response.py +0 -0
  26. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/core/router.py +0 -0
  27. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/core/server.py +0 -0
  28. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/crud/__init__.py +0 -0
  29. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/database/__init__.py +0 -0
  30. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/database/adapter.py +0 -0
  31. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/database/connection.py +0 -0
  32. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/database/firebird.py +0 -0
  33. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/database/mongodb.py +0 -0
  34. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/database/mssql.py +0 -0
  35. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/database/mysql.py +0 -0
  36. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/database/odbc.py +0 -0
  37. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/database/postgres.py +0 -0
  38. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/database/sqlite.py +0 -0
  39. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/debug/__init__.py +0 -0
  40. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/debug/error_overlay.py +0 -0
  41. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/dev_admin/__init__.py +0 -0
  42. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/dev_admin/metrics.py +0 -0
  43. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/dev_admin/plan.py +0 -0
  44. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/dev_admin/project_index.py +0 -0
  45. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/docs.py +0 -0
  46. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/docstore/__init__.py +0 -0
  47. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/dotenv/__init__.py +0 -0
  48. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/env.py +0 -0
  49. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/frond/FROND.md +0 -0
  50. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/frond/__init__.py +0 -0
  51. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/frond/engine.py +0 -0
  52. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/auth/meta.json +0 -0
  53. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
  54. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/database/meta.json +0 -0
  55. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
  56. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/error-overlay/meta.json +0 -0
  57. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
  58. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/orm/meta.json +0 -0
  59. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
  60. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
  61. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/queue/meta.json +0 -0
  62. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
  63. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/rest-api/meta.json +0 -0
  64. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
  65. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/templates/meta.json +0 -0
  66. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
  67. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
  68. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/graphql/__init__.py +0 -0
  69. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/i18n/__init__.py +0 -0
  70. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/mcp/__init__.py +0 -0
  71. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/mcp/protocol.py +0 -0
  72. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/mcp/tools.py +0 -0
  73. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/messenger/__init__.py +0 -0
  74. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/migration/__init__.py +0 -0
  75. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/migration/runner.py +0 -0
  76. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/mqtt/__init__.py +0 -0
  77. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/mqtt/message.py +0 -0
  78. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/orm/__init__.py +0 -0
  79. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/orm/fields.py +0 -0
  80. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/orm/model.py +0 -0
  81. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/__feedback/widget.js +0 -0
  82. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/css/tina4.css +0 -0
  83. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/css/tina4.min.css +0 -0
  84. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/favicon.ico +0 -0
  85. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/images/logo.svg +0 -0
  86. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
  87. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/js/frond.js +0 -0
  88. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/js/frond.min.js +0 -0
  89. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
  90. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/js/tina4.min.js +0 -0
  91. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/js/tina4js.min.js +0 -0
  92. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/swagger/index.html +0 -0
  93. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
  94. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/query_builder/__init__.py +0 -0
  95. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/queue/__init__.py +0 -0
  96. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/queue/job.py +0 -0
  97. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/queue/kafka_backend.py +0 -0
  98. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/queue/lite_backend.py +0 -0
  99. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/queue/mongo_backend.py +0 -0
  100. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/queue/rabbitmq_backend.py +0 -0
  101. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/queue_backends/__init__.py +0 -0
  102. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/queue_backends/kafka_backend.py +0 -0
  103. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/queue_backends/mongo_backend.py +0 -0
  104. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
  105. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/realtime/__init__.py +0 -0
  106. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/realtime/models/Attachment.py +0 -0
  107. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/realtime/models/Channel.py +0 -0
  108. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/realtime/models/ChannelMember.py +0 -0
  109. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/realtime/models/Message.py +0 -0
  110. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/realtime/models/Workspace.py +0 -0
  111. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/realtime/models/__init__.py +0 -0
  112. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/realtime/storage.py +0 -0
  113. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/__init__.py +0 -0
  114. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_alerts.scss +0 -0
  115. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_badges.scss +0 -0
  116. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_buttons.scss +0 -0
  117. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_cards.scss +0 -0
  118. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_forms.scss +0 -0
  119. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_grid.scss +0 -0
  120. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_modals.scss +0 -0
  121. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_nav.scss +0 -0
  122. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_pagination.scss +0 -0
  123. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_reset.scss +0 -0
  124. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_tables.scss +0 -0
  125. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_typography.scss +0 -0
  126. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_utilities.scss +0 -0
  127. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/_variables.scss +0 -0
  128. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/base.scss +0 -0
  129. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/colors.scss +0 -0
  130. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/scss/tina4css/tina4.scss +0 -0
  131. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/seeder/__init__.py +0 -0
  132. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/service/__init__.py +0 -0
  133. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/session/__init__.py +0 -0
  134. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/session_handlers/__init__.py +0 -0
  135. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/session_handlers/mongodb_handler.py +0 -0
  136. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/session_handlers/redis_handler.py +0 -0
  137. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/session_handlers/valkey_handler.py +0 -0
  138. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/swagger/__init__.py +0 -0
  139. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/components/crud.twig +0 -0
  140. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
  141. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
  142. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/docker/python/Dockerfile +0 -0
  143. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/docker/uv/Dockerfile +0 -0
  144. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/errors/302.twig +0 -0
  145. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/errors/401.twig +0 -0
  146. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/errors/403.twig +0 -0
  147. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/errors/404.twig +0 -0
  148. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/errors/500.twig +0 -0
  149. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/errors/502.twig +0 -0
  150. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/errors/503.twig +0 -0
  151. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/errors/base.twig +0 -0
  152. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/frontend/README.md +0 -0
  153. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/templates/readme.md +0 -0
  154. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/test/__init__.py +0 -0
  155. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/test_client/__init__.py +0 -0
  156. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
  157. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
  158. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
  159. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
  160. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
  161. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
  162. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
  163. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
  164. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
  165. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
  166. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
  167. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
  168. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/validator/__init__.py +0 -0
  169. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/websocket/__init__.py +0 -0
  170. {tina4_python-3.13.83 → tina4_python-3.13.85}/tina4_python/websocket/backplane.py +0 -0
  171. {tina4_python-3.13.83 → tina4_python-3.13.85}/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.83
3
+ Version: 3.13.85
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.83"
3
+ version = "3.13.85"
4
4
  description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
5
5
  authors = [
6
6
  {name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
@@ -170,24 +170,85 @@ def _parse_every(every: str) -> int:
170
170
  return 60
171
171
 
172
172
 
173
+ def _in_container() -> bool:
174
+ """True when this process is running inside a container.
175
+
176
+ Reclaiming a port makes sense on a dev machine, where a previous
177
+ ``tina4 serve`` may still hold it. Inside a container the server IS the
178
+ container, so there is never a stale sibling to reclaim from -- and trying
179
+ is actively dangerous (see ``_kill_process_on_port``).
180
+ """
181
+ if os.path.exists("/.dockerenv") or os.path.exists("/run/.containerenv"):
182
+ return True
183
+ try:
184
+ with open("/proc/1/cgroup", "r", encoding="utf-8", errors="replace") as fh:
185
+ blob = fh.read()
186
+ return "docker" in blob or "containerd" in blob or "kubepods" in blob
187
+ except OSError:
188
+ return False
189
+
190
+
191
+ def selectable_pids(lsof_output: str, me: int, my_group: int | None = None) -> list[int]:
192
+ """The PIDs from ``lsof -ti`` output that are safe to signal.
193
+
194
+ Pure so the safety rule can be tested directly. An unvalidated parse is a
195
+ footgun with real teeth: when ``lsof`` is present but prints a different
196
+ shape than ``-ti`` implies, a non-numeric field coerces to 0, and
197
+ signalling PID 0 sends the signal to EVERY process in the caller's own
198
+ process group -- the server kills itself. That is exactly what happened in
199
+ a container, where the log read "Killed existing process on port 7148
200
+ (PID: 1 ...)" and the container exited 143.
201
+
202
+ So: accept only all-digit tokens, and never PID 0 (our process group),
203
+ PID 1 (init), ourselves, or our own process group.
204
+ """
205
+ pids = []
206
+ for token in lsof_output.split():
207
+ if not token.isdigit():
208
+ continue # never coerce junk into a PID
209
+ pid = int(token)
210
+ if pid <= 1 or pid == me:
211
+ continue # 0 = our process group, 1 = init, me = suicide
212
+ if my_group is not None and pid == my_group:
213
+ continue
214
+ if pid not in pids:
215
+ pids.append(pid)
216
+ return pids
217
+
218
+
173
219
  def _kill_process_on_port(port: int) -> bool:
174
- """Kill any process listening on the given port. Returns True if killed."""
220
+ """Kill any process listening on the given port. Returns True if killed.
221
+
222
+ Skipped entirely in a container, where the server IS the container and
223
+ there is no stale sibling to reclaim from. The PID safety rule lives in
224
+ :func:`selectable_pids`.
225
+ """
226
+ if _in_container():
227
+ return False
175
228
  try:
176
229
  result = subprocess.run(
177
230
  ["lsof", "-ti", f":{port}"],
178
231
  capture_output=True, text=True, timeout=5,
179
232
  )
180
- if result.returncode == 0 and result.stdout.strip():
181
- pids = result.stdout.strip().split("\n")
182
- for pid in pids:
183
- try:
184
- os.kill(int(pid), signal.SIGTERM)
185
- except (ProcessLookupError, PermissionError):
186
- pass
187
- import time
188
- time.sleep(0.5)
189
- print(f" ⚠ Killed existing process on port {port} (PID: {', '.join(pids)})")
190
- return True
233
+ if result.returncode != 0 or not result.stdout.strip():
234
+ return False
235
+
236
+ me = os.getpid()
237
+ my_group = os.getpgrp() if hasattr(os, "getpgrp") else None
238
+ killed = []
239
+ for pid in selectable_pids(result.stdout, me, my_group):
240
+ try:
241
+ os.kill(pid, signal.SIGTERM)
242
+ killed.append(str(pid))
243
+ except (ProcessLookupError, PermissionError):
244
+ pass
245
+
246
+ if not killed:
247
+ return False
248
+ import time
249
+ time.sleep(0.5)
250
+ print(f" ⚠ Killed existing process on port {port} (PID: {', '.join(killed)})")
251
+ return True
191
252
  except (FileNotFoundError, subprocess.TimeoutExpired):
192
253
  pass
193
254
  return False
@@ -663,7 +724,8 @@ def _init(args):
663
724
  'FROM python:3.13-slim\nWORKDIR /app\n'
664
725
  'COPY --from=build /app .\nCOPY --from=build /usr/local/bin/uv /usr/local/bin/uv\n'
665
726
  'ENV PATH="/app/.venv/bin:$PATH"\nENV HOST=0.0.0.0\nENV PORT=7146\n'
666
- 'EXPOSE 7146\nCMD ["python", "app.py"]\n',
727
+ 'ENV TINA4_OVERRIDE_CLIENT=true\nENV TINA4_DEBUG=false\n'
728
+ 'EXPOSE 7146\nCMD ["tina4python", "serve", "--production"]\n',
667
729
  encoding="utf-8",
668
730
  )
669
731