tina4-python 3.10.41__tar.gz → 3.10.42__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.
- {tina4_python-3.10.41 → tina4_python-3.10.42}/PKG-INFO +2 -2
- {tina4_python-3.10.41 → tina4_python-3.10.42}/README.md +1 -1
- {tina4_python-3.10.41 → tina4_python-3.10.42}/pyproject.toml +1 -1
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/core/__init__.py +2 -2
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/core/server.py +69 -70
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/dev_admin/__init__.py +9 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/dev_admin/metrics.py +18 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/.gitignore +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/CLAUDE.md +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/Testing.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/ai/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/api/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/auth/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/cache/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/cli/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/container/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/core/cache.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/core/constants.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/core/events.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/core/middleware.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/core/request.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/core/response.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/core/router.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/crud/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/database/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/database/adapter.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/database/connection.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/database/firebird.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/database/mssql.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/database/mysql.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/database/odbc.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/database/postgres.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/database/sqlite.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/debug/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/debug/error_overlay.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/dev_reload.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/dotenv/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/frond/FROND.md +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/frond/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/frond/engine.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/auth/meta.json +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/database/meta.json +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/error-overlay/meta.json +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/orm/meta.json +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/queue/meta.json +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/rest-api/meta.json +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/templates/meta.json +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/graphql/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/i18n/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/mcp/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/mcp/protocol.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/mcp/tools.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/messenger/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/migration/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/migration/runner.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/orm/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/orm/fields.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/orm/model.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/css/tina4.css +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/css/tina4.min.css +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/images/logo.svg +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/js/frond.min.js +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/js/tina4.min.js +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/js/tina4js.min.js +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/query_builder/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/queue/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/queue_backends/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/queue_backends/kafka_backend.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/queue_backends/mongo_backend.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_alerts.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_badges.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_buttons.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_cards.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_forms.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_grid.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_modals.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_nav.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_reset.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_tables.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_typography.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_utilities.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/_variables.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/base.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/colors.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/scss/tina4css/tina4.scss +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/seeder/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/service/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/session/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/session_handlers/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/session_handlers/mongodb_handler.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/session_handlers/redis_handler.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/session_handlers/valkey_handler.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/swagger/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/components/crud.twig +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/docker/python/Dockerfile +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/docker/uv/Dockerfile +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/errors/302.twig +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/errors/401.twig +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/errors/502.twig +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/errors/503.twig +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/errors/base.twig +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/frontend/README.md +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/readme.md +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/test_client/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/validator/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/websocket/__init__.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/websocket/backplane.py +0 -0
- {tina4_python-3.10.41 → tina4_python-3.10.42}/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.
|
|
3
|
+
Version: 3.10.42
|
|
4
4
|
Summary: Tina4 for Python — 54 built-in features, zero dependencies
|
|
5
5
|
Author-email: Andre van Zuydam <andrevanzuydam@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
|
|
|
42
42
|
|
|
43
43
|
<p align="center">
|
|
44
44
|
<a href="https://pypi.org/project/tina4-python/"><img src="https://img.shields.io/pypi/v/tina4-python?color=7b1fa2&label=PyPI" alt="PyPI"></a>
|
|
45
|
-
<img src="https://img.shields.io/badge/tests-2%
|
|
45
|
+
<img src="https://img.shields.io/badge/tests-2%2C068%20passing-brightgreen" alt="Tests">
|
|
46
46
|
<img src="https://img.shields.io/badge/features-54-blue" alt="Features">
|
|
47
47
|
<img src="https://img.shields.io/badge/dependencies-0-brightgreen" alt="Zero Deps">
|
|
48
48
|
<a href="https://tina4.com"><img src="https://img.shields.io/badge/docs-tina4.com-7b1fa2" alt="Docs"></a>
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
12
|
<a href="https://pypi.org/project/tina4-python/"><img src="https://img.shields.io/pypi/v/tina4-python?color=7b1fa2&label=PyPI" alt="PyPI"></a>
|
|
13
|
-
<img src="https://img.shields.io/badge/tests-2%
|
|
13
|
+
<img src="https://img.shields.io/badge/tests-2%2C068%20passing-brightgreen" alt="Tests">
|
|
14
14
|
<img src="https://img.shields.io/badge/features-54-blue" alt="Features">
|
|
15
15
|
<img src="https://img.shields.io/badge/dependencies-0-brightgreen" alt="Zero Deps">
|
|
16
16
|
<a href="https://tina4.com"><img src="https://img.shields.io/badge/docs-tina4.com-7b1fa2" alt="Docs"></a>
|
|
@@ -19,7 +19,7 @@ from tina4_python.core.router import (
|
|
|
19
19
|
from tina4_python.core.middleware import CorsMiddleware, RateLimiter
|
|
20
20
|
from tina4_python.core.cache import Cache
|
|
21
21
|
from tina4_python.core.events import on, off, emit, emit_async, once, listeners, events, clear as clear_events
|
|
22
|
-
from tina4_python.core.server import run, resolve_config
|
|
22
|
+
from tina4_python.core.server import run, resolve_config, handle
|
|
23
23
|
|
|
24
24
|
__all__ = [
|
|
25
25
|
"Request", "Response", "Router",
|
|
@@ -28,5 +28,5 @@ __all__ = [
|
|
|
28
28
|
"CorsMiddleware", "RateLimiter",
|
|
29
29
|
"Cache",
|
|
30
30
|
"on", "off", "emit", "emit_async", "once", "listeners", "events", "clear_events",
|
|
31
|
-
"run", "resolve_config",
|
|
31
|
+
"run", "resolve_config", "handle",
|
|
32
32
|
]
|
|
@@ -607,54 +607,33 @@ async def _handle_dev_websocket(reader, writer, headers, path):
|
|
|
607
607
|
pass
|
|
608
608
|
|
|
609
609
|
|
|
610
|
-
async def app(scope: dict, receive, send):
|
|
611
|
-
"""ASGI entry point — compatible with uvicorn, hypercorn, granian."""
|
|
612
|
-
if scope["type"] == "lifespan":
|
|
613
|
-
msg = await receive()
|
|
614
|
-
if msg["type"] == "lifespan.startup":
|
|
615
|
-
import time
|
|
616
|
-
global _start_time
|
|
617
|
-
_start_time = time.time()
|
|
618
|
-
await send({"type": "lifespan.startup.complete"})
|
|
619
|
-
elif msg["type"] == "lifespan.shutdown":
|
|
620
|
-
await send({"type": "lifespan.shutdown.complete"})
|
|
621
|
-
return
|
|
622
|
-
|
|
623
|
-
if scope["type"] == "websocket":
|
|
624
|
-
await _handle_asgi_websocket(scope, receive, send)
|
|
625
|
-
return
|
|
626
|
-
|
|
627
|
-
if scope["type"] != "http":
|
|
628
|
-
return
|
|
629
610
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
while True:
|
|
633
|
-
msg = await receive()
|
|
634
|
-
body += msg.get("body", b"")
|
|
635
|
-
if not msg.get("more_body", False):
|
|
636
|
-
break
|
|
611
|
+
async def handle(request: Request) -> Response:
|
|
612
|
+
"""Dispatch a pre-built Request through the Tina4 router and return a Response.
|
|
637
613
|
|
|
638
|
-
|
|
639
|
-
|
|
614
|
+
Handles session setup, CORS, rate limiting, routing, auth, middleware,
|
|
615
|
+
dev toolbar injection, and session saving. The caller is responsible
|
|
616
|
+
for sending the response over the wire. Useful for testing and embedding.
|
|
617
|
+
"""
|
|
640
618
|
request_id = request.headers.get("x-request-id", str(uuid.uuid4())[:8])
|
|
641
619
|
set_request_id(request_id)
|
|
642
620
|
|
|
643
621
|
# Auto-start session — lazy, reads cookie, saves on response
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
part
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
622
|
+
if request.session is None:
|
|
623
|
+
try:
|
|
624
|
+
from tina4_python.session import Session
|
|
625
|
+
cookie_header = request.headers.get("cookie", "")
|
|
626
|
+
sid_match = None
|
|
627
|
+
for part in cookie_header.split(";"):
|
|
628
|
+
part = part.strip()
|
|
629
|
+
if part.startswith("tina4_session="):
|
|
630
|
+
sid_match = part.split("=", 1)[1]
|
|
631
|
+
break
|
|
632
|
+
sess = Session()
|
|
633
|
+
sess.start(sid_match)
|
|
634
|
+
request.session = sess
|
|
635
|
+
except Exception:
|
|
636
|
+
pass # Session module not available — session stays None
|
|
658
637
|
|
|
659
638
|
response = Response()
|
|
660
639
|
response.header("x-request-id", request_id)
|
|
@@ -663,9 +642,7 @@ async def app(scope: dict, receive, send):
|
|
|
663
642
|
if _cors.is_preflight(request):
|
|
664
643
|
_cors.apply(request, response)
|
|
665
644
|
response.status(204)
|
|
666
|
-
|
|
667
|
-
await send({"type": "http.response.body", "body": b""})
|
|
668
|
-
return
|
|
645
|
+
return response
|
|
669
646
|
|
|
670
647
|
# Rate limiting
|
|
671
648
|
rate_enabled = os.environ.get("TINA4_RATE_LIMIT", "")
|
|
@@ -680,9 +657,7 @@ async def app(scope: dict, receive, send):
|
|
|
680
657
|
"status": 429,
|
|
681
658
|
})
|
|
682
659
|
response.header("retry-after", str(info["reset"]))
|
|
683
|
-
|
|
684
|
-
await send({"type": "http.response.body", "body": response.content})
|
|
685
|
-
return
|
|
660
|
+
return response
|
|
686
661
|
|
|
687
662
|
import time as _time
|
|
688
663
|
_req_start = _time.perf_counter()
|
|
@@ -727,12 +702,9 @@ async def app(scope: dict, receive, send):
|
|
|
727
702
|
else:
|
|
728
703
|
response.status(404).json({"error": "Not found"})
|
|
729
704
|
|
|
730
|
-
#
|
|
705
|
+
# Dev admin response (skip overlay injection)
|
|
731
706
|
_cors.apply(request, response)
|
|
732
|
-
|
|
733
|
-
await send({"type": "http.response.start", "status": response.status_code, "headers": headers})
|
|
734
|
-
await send({"type": "http.response.body", "body": response.content})
|
|
735
|
-
return
|
|
707
|
+
return response
|
|
736
708
|
|
|
737
709
|
# Swagger auto-register: serve /swagger and /swagger/openapi.json when debug is on
|
|
738
710
|
if _is_dev and request.method == "GET":
|
|
@@ -749,10 +721,7 @@ async def app(scope: dict, receive, send):
|
|
|
749
721
|
)
|
|
750
722
|
response.html(swagger_html)
|
|
751
723
|
_cors.apply(request, response)
|
|
752
|
-
|
|
753
|
-
await send({"type": "http.response.start", "status": response.status_code, "headers": headers})
|
|
754
|
-
await send({"type": "http.response.body", "body": response.content})
|
|
755
|
-
return
|
|
724
|
+
return response
|
|
756
725
|
elif request.path == "/swagger/openapi.json":
|
|
757
726
|
# Serve OpenAPI spec JSON from all registered routes
|
|
758
727
|
from tina4_python.swagger import Swagger as _SwaggerGen
|
|
@@ -760,10 +729,7 @@ async def app(scope: dict, receive, send):
|
|
|
760
729
|
_spec = _swagger.generate(Router.get_routes())
|
|
761
730
|
response.json(_spec)
|
|
762
731
|
_cors.apply(request, response)
|
|
763
|
-
|
|
764
|
-
await send({"type": "http.response.start", "status": response.status_code, "headers": headers})
|
|
765
|
-
await send({"type": "http.response.body", "body": response.content})
|
|
766
|
-
return
|
|
732
|
+
return response
|
|
767
733
|
|
|
768
734
|
# Match route
|
|
769
735
|
route, params = Router.match(request.method, request.path)
|
|
@@ -931,13 +897,13 @@ async def app(scope: dict, receive, send):
|
|
|
931
897
|
request.method, request.path, matched_pattern,
|
|
932
898
|
request_id, len(Router.get_routes()),
|
|
933
899
|
).encode()
|
|
934
|
-
|
|
900
|
+
content_body = response.content
|
|
935
901
|
# Inject before </body> if present, else append
|
|
936
|
-
if b"</body>" in
|
|
937
|
-
|
|
902
|
+
if b"</body>" in content_body:
|
|
903
|
+
content_body = content_body.replace(b"</body>", toolbar + b"\n</body>", 1)
|
|
938
904
|
else:
|
|
939
|
-
|
|
940
|
-
response.content =
|
|
905
|
+
content_body = content_body + toolbar
|
|
906
|
+
response.content = content_body
|
|
941
907
|
except Exception:
|
|
942
908
|
pass
|
|
943
909
|
|
|
@@ -954,9 +920,6 @@ async def app(scope: dict, receive, send):
|
|
|
954
920
|
except Exception:
|
|
955
921
|
pass
|
|
956
922
|
|
|
957
|
-
# ETag check — 304 Not Modified
|
|
958
|
-
if_none_match = request.headers.get("if-none-match", "")
|
|
959
|
-
|
|
960
923
|
# Save session and set cookie if session was used
|
|
961
924
|
if request.session is not None:
|
|
962
925
|
try:
|
|
@@ -973,7 +936,43 @@ async def app(scope: dict, receive, send):
|
|
|
973
936
|
except Exception:
|
|
974
937
|
pass
|
|
975
938
|
|
|
976
|
-
|
|
939
|
+
return response
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
async def app(scope: dict, receive, send):
|
|
943
|
+
"""ASGI entry point — compatible with uvicorn, hypercorn, granian."""
|
|
944
|
+
if scope["type"] == "lifespan":
|
|
945
|
+
msg = await receive()
|
|
946
|
+
if msg["type"] == "lifespan.startup":
|
|
947
|
+
import time
|
|
948
|
+
global _start_time
|
|
949
|
+
_start_time = time.time()
|
|
950
|
+
await send({"type": "lifespan.startup.complete"})
|
|
951
|
+
elif msg["type"] == "lifespan.shutdown":
|
|
952
|
+
await send({"type": "lifespan.shutdown.complete"})
|
|
953
|
+
return
|
|
954
|
+
|
|
955
|
+
if scope["type"] == "websocket":
|
|
956
|
+
await _handle_asgi_websocket(scope, receive, send)
|
|
957
|
+
return
|
|
958
|
+
|
|
959
|
+
if scope["type"] != "http":
|
|
960
|
+
return
|
|
961
|
+
|
|
962
|
+
# Read full body
|
|
963
|
+
body = b""
|
|
964
|
+
while True:
|
|
965
|
+
msg = await receive()
|
|
966
|
+
body += msg.get("body", b"")
|
|
967
|
+
if not msg.get("more_body", False):
|
|
968
|
+
break
|
|
969
|
+
|
|
970
|
+
# Build request and dispatch
|
|
971
|
+
request = Request.from_scope(scope, body)
|
|
972
|
+
response = await handle(request)
|
|
973
|
+
|
|
974
|
+
# ETag check — 304 Not Modified
|
|
975
|
+
if_none_match = request.headers.get("if-none-match", "")
|
|
977
976
|
accept_encoding = request.headers.get("accept-encoding", "")
|
|
978
977
|
headers = response.build_headers(accept_encoding)
|
|
979
978
|
|
|
@@ -1924,6 +1924,15 @@ function drillDownFile(path){
|
|
|
1924
1924
|
});
|
|
1925
1925
|
html+='</div>';
|
|
1926
1926
|
}
|
|
1927
|
+
if(d.warnings&&d.warnings.length){
|
|
1928
|
+
html+='<h3 style="margin:0.75rem 0 0.25rem;color:#eab308;font-size:0.85rem">⚠ Warnings</h3>';
|
|
1929
|
+
html+='<div style="display:flex;flex-direction:column;gap:4px">';
|
|
1930
|
+
d.warnings.forEach(function(w){
|
|
1931
|
+
html+='<div style="padding:4px 8px;background:rgba(234,179,8,0.08);border-left:3px solid #eab308;border-radius:0 4px 4px 0;font-size:0.75rem;font-family:var(--mono);color:var(--text)">';
|
|
1932
|
+
html+='<span style="color:#eab308;margin-right:6px">L'+w.line+'</span>'+w.message+'</div>';
|
|
1933
|
+
});
|
|
1934
|
+
html+='</div>';
|
|
1935
|
+
}
|
|
1927
1936
|
dd.querySelector('.p-md').innerHTML=html;
|
|
1928
1937
|
}).catch(function(e){
|
|
1929
1938
|
dd.querySelector('.p-md').innerHTML='<p style="color:var(--danger)">Error: '+e.message+'</p>';
|
|
@@ -327,6 +327,23 @@ def file_detail(file_path: str) -> dict:
|
|
|
327
327
|
classes = sum(1 for n in ast.walk(tree) if isinstance(n, ast.ClassDef))
|
|
328
328
|
imports = _extract_imports(tree, file_path)
|
|
329
329
|
|
|
330
|
+
# Detect empty methods/functions (body is only `pass` or a docstring)
|
|
331
|
+
warnings = []
|
|
332
|
+
for node in ast.walk(tree):
|
|
333
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
334
|
+
body = node.body
|
|
335
|
+
# Strip leading docstring
|
|
336
|
+
effective = body[1:] if body and isinstance(body[0], ast.Expr) and isinstance(body[0].value, ast.Constant) else body
|
|
337
|
+
if not effective or all(isinstance(s, ast.Pass) for s in effective):
|
|
338
|
+
parent = _get_parent_class(tree, node)
|
|
339
|
+
name = f"{parent}.{node.name}" if parent else node.name
|
|
340
|
+
warnings.append({"type": "empty_method", "message": f"Method '{name}' appears to be empty", "line": node.lineno})
|
|
341
|
+
elif isinstance(node, ast.ClassDef):
|
|
342
|
+
body = node.body
|
|
343
|
+
effective = body[1:] if body and isinstance(body[0], ast.Expr) and isinstance(body[0].value, ast.Constant) else body
|
|
344
|
+
if not effective or all(isinstance(s, ast.Pass) for s in effective):
|
|
345
|
+
warnings.append({"type": "empty_class", "message": f"Class '{node.name}' appears to be empty", "line": node.lineno})
|
|
346
|
+
|
|
330
347
|
return {
|
|
331
348
|
"path": file_path,
|
|
332
349
|
"loc": loc,
|
|
@@ -334,6 +351,7 @@ def file_detail(file_path: str) -> dict:
|
|
|
334
351
|
"classes": classes,
|
|
335
352
|
"functions": functions,
|
|
336
353
|
"imports": imports,
|
|
354
|
+
"warnings": warnings,
|
|
337
355
|
}
|
|
338
356
|
|
|
339
357
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/images/tina4-logo-icon.webp
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/public/swagger/oauth2-redirect.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/queue_backends/rabbitmq_backend.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/session_handlers/mongodb_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/session_handlers/redis_handler.py
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/session_handlers/valkey_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/docker/distroless/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/docker/poetry/Dockerfile
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/templates/docker/python/Dockerfile
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/af/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/af/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/es/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/es/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/fr/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/fr/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/ja/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/ja/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/zh/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.10.41 → tina4_python-3.10.42}/tina4_python/translations/zh/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|