tina4-python 0.2.193__tar.gz → 0.2.194__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-0.2.193 → tina4_python-0.2.194}/PKG-INFO +1 -1
- {tina4_python-0.2.193 → tina4_python-0.2.194}/pyproject.toml +1 -1
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Router.py +11 -4
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Session.py +89 -1
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Webserver.py +6 -8
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/__init__.py +8 -12
- {tina4_python-0.2.193 → tina4_python-0.2.194}/.gitignore +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/README.md +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Api.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Auth.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/CLAUDE.md +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/CRUD.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Constant.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Database.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/DatabaseResult.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/DatabaseTypes.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Debug.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/DevReload.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Env.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/FieldTypes.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Localization.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Messages.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/MiddleWare.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Migration.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/ORM.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Queue.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Request.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Response.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/ShellColors.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Swagger.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Template.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Testing.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/WSDL.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/Websocket.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/cli.py +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/messages.pot +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/public/css/readme.md +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/public/images/403.png +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/public/images/404.png +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/public/images/500.png +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/public/images/logo.png +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/public/images/readme.md +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/public/js/readme.md +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/public/js/reconnecting-websocket.js +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/public/js/tina4helper.js +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/templates/components/crud.twig +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/templates/readme.md +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
|
@@ -354,6 +354,7 @@ class Router:
|
|
|
354
354
|
tina4_python.tina4_current_request = {"url": url, "headers": headers}
|
|
355
355
|
|
|
356
356
|
validated = False
|
|
357
|
+
has_form_token = False
|
|
357
358
|
# we can add other methods later but right now we validate gets, posts and other risky methods
|
|
358
359
|
if method in [Constant.TINA4_GET, Constant.TINA4_POST, Constant.TINA4_PUT, Constant.TINA4_PATCH,
|
|
359
360
|
Constant.TINA4_DELETE]:
|
|
@@ -377,11 +378,13 @@ class Router:
|
|
|
377
378
|
token = request["params"]["formToken"]
|
|
378
379
|
if tina4_python.tina4_auth.valid(token):
|
|
379
380
|
validated = True
|
|
381
|
+
has_form_token = True
|
|
380
382
|
|
|
381
383
|
if request["body"] is not None and "formToken" in request["body"]:
|
|
382
384
|
token = request["body"]["formToken"]
|
|
383
385
|
if tina4_python.tina4_auth.valid(token):
|
|
384
386
|
validated = True
|
|
387
|
+
has_form_token = True
|
|
385
388
|
|
|
386
389
|
if request["body"] is not None and "formToken" in request["body"]:
|
|
387
390
|
request["params"]["formToken"] = request["body"]["formToken"]
|
|
@@ -521,7 +524,8 @@ class Router:
|
|
|
521
524
|
return Response(result.content, result.http_code, result.content_type)
|
|
522
525
|
|
|
523
526
|
if result is not None:
|
|
524
|
-
|
|
527
|
+
if has_form_token:
|
|
528
|
+
result.headers["FreshToken"] = tina4_python.tina4_auth.get_token({"path": url})
|
|
525
529
|
if "cache" in route and route["cache"] is not None:
|
|
526
530
|
if not route["cache"]["cached"]:
|
|
527
531
|
result.headers["Cache-Control"] = "max-age=1, must-revalidate"
|
|
@@ -542,7 +546,9 @@ class Router:
|
|
|
542
546
|
if result is None and route_matched:
|
|
543
547
|
output = buffer.getvalue()
|
|
544
548
|
if output:
|
|
545
|
-
fresh_headers = {
|
|
549
|
+
fresh_headers = {}
|
|
550
|
+
if has_form_token:
|
|
551
|
+
fresh_headers["FreshToken"] = tina4_python.tina4_auth.get_token({"path": url})
|
|
546
552
|
try:
|
|
547
553
|
return Response(json.loads(output), Constant.HTTP_OK, Constant.APPLICATION_JSON, fresh_headers)
|
|
548
554
|
except Exception:
|
|
@@ -566,10 +572,11 @@ class Router:
|
|
|
566
572
|
)
|
|
567
573
|
|
|
568
574
|
twig_headers = {
|
|
569
|
-
"FreshToken": tina4_python.tina4_auth.get_token({"path": url}),
|
|
570
575
|
"Cache-Control": "max-age=-1, public",
|
|
571
576
|
"Pragma": "no-cache"
|
|
572
577
|
}
|
|
578
|
+
if has_form_token:
|
|
579
|
+
twig_headers["FreshToken"] = tina4_python.tina4_auth.get_token({"path": url})
|
|
573
580
|
content = Template.render_twig_template(twig_file, {"request": tina4_python.tina4_current_request})
|
|
574
581
|
if content != "":
|
|
575
582
|
return Response(content, Constant.HTTP_OK, Constant.TEXT_HTML, twig_headers)
|
|
@@ -579,7 +586,7 @@ class Router:
|
|
|
579
586
|
"errors/404.twig", {"server": {"url": url}})
|
|
580
587
|
return Response(content, Constant.HTTP_NOT_FOUND, Constant.TEXT_HTML)
|
|
581
588
|
|
|
582
|
-
|
|
589
|
+
# FreshToken already set on line 524 inside the route loop
|
|
583
590
|
return result
|
|
584
591
|
|
|
585
592
|
@staticmethod
|
|
@@ -30,7 +30,7 @@ Typical usage inside a route handler::
|
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
32
|
__all__ = [
|
|
33
|
-
"Session", "SessionHandler", "SessionFileHandler",
|
|
33
|
+
"Session", "LazySession", "SessionHandler", "SessionFileHandler",
|
|
34
34
|
"SessionRedisHandler", "SessionValkeyHandler",
|
|
35
35
|
"SessionMongoHandler",
|
|
36
36
|
]
|
|
@@ -685,3 +685,91 @@ class Session:
|
|
|
685
685
|
for key, value in self.session_values.items():
|
|
686
686
|
if key != "expires":
|
|
687
687
|
yield key, value
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
class LazySession:
|
|
691
|
+
"""Proxy that defers expensive Session creation until first use.
|
|
692
|
+
|
|
693
|
+
Creating and starting a ``Session`` requires RSA key signing (~1ms
|
|
694
|
+
per call) which dominates request latency for API routes that never
|
|
695
|
+
touch the session. ``LazySession`` wraps the session creation
|
|
696
|
+
parameters and only instantiates the real ``Session`` when a method
|
|
697
|
+
like ``set()``, ``get()``, or ``load()`` is called.
|
|
698
|
+
|
|
699
|
+
The ``activated`` property lets the response builder know whether
|
|
700
|
+
to emit a ``Set-Cookie`` header.
|
|
701
|
+
"""
|
|
702
|
+
|
|
703
|
+
def __init__(self, name, path, handler, cookies):
|
|
704
|
+
self._name = name
|
|
705
|
+
self._path = path
|
|
706
|
+
self._handler = handler
|
|
707
|
+
self._cookies = cookies
|
|
708
|
+
self._real = None
|
|
709
|
+
|
|
710
|
+
@property
|
|
711
|
+
def activated(self):
|
|
712
|
+
"""True if the real Session has been created."""
|
|
713
|
+
return self._real is not None
|
|
714
|
+
|
|
715
|
+
def _activate(self):
|
|
716
|
+
"""Create and start/load the real Session on first access."""
|
|
717
|
+
if self._real is None:
|
|
718
|
+
self._real = Session(self._name, self._path, self._handler)
|
|
719
|
+
if self._name in self._cookies:
|
|
720
|
+
self._real.load(self._cookies[self._name])
|
|
721
|
+
else:
|
|
722
|
+
self._cookies[self._name] = self._real.start()
|
|
723
|
+
return self._real
|
|
724
|
+
|
|
725
|
+
# --- Forwarded Session API ---
|
|
726
|
+
|
|
727
|
+
@property
|
|
728
|
+
def session_name(self):
|
|
729
|
+
return self._name
|
|
730
|
+
|
|
731
|
+
@property
|
|
732
|
+
def session_values(self):
|
|
733
|
+
if self._real is None:
|
|
734
|
+
return {}
|
|
735
|
+
return self._real.session_values
|
|
736
|
+
|
|
737
|
+
@property
|
|
738
|
+
def session_hash(self):
|
|
739
|
+
if self._real is None:
|
|
740
|
+
return ""
|
|
741
|
+
return self._real.session_hash
|
|
742
|
+
|
|
743
|
+
@session_hash.setter
|
|
744
|
+
def session_hash(self, value):
|
|
745
|
+
self._activate().session_hash = value
|
|
746
|
+
|
|
747
|
+
def start(self, session_hash=None):
|
|
748
|
+
return self._activate().start(session_hash)
|
|
749
|
+
|
|
750
|
+
def load(self, session_hash):
|
|
751
|
+
return self._activate().load(session_hash)
|
|
752
|
+
|
|
753
|
+
def set(self, key, value):
|
|
754
|
+
return self._activate().set(key, value)
|
|
755
|
+
|
|
756
|
+
def get(self, key):
|
|
757
|
+
return self._activate().get(key)
|
|
758
|
+
|
|
759
|
+
def unset(self, key):
|
|
760
|
+
return self._activate().unset(key)
|
|
761
|
+
|
|
762
|
+
def close(self):
|
|
763
|
+
if self._real is not None:
|
|
764
|
+
return self._real.close()
|
|
765
|
+
return True
|
|
766
|
+
|
|
767
|
+
def save(self):
|
|
768
|
+
if self._real is not None:
|
|
769
|
+
return self._real.save()
|
|
770
|
+
return True
|
|
771
|
+
|
|
772
|
+
def __iter__(self):
|
|
773
|
+
if self._real is not None:
|
|
774
|
+
return iter(self._real)
|
|
775
|
+
return iter([])
|
|
@@ -434,9 +434,10 @@ class Webserver:
|
|
|
434
434
|
self.send_header("Content-Type", response.content_type or "text/html", headers)
|
|
435
435
|
await self.send_basic_headers(headers)
|
|
436
436
|
|
|
437
|
-
# Preserve session cookie
|
|
437
|
+
# Preserve session cookie (only if session was used)
|
|
438
438
|
session_name = os.getenv("TINA4_SESSION", "PY_SESS")
|
|
439
|
-
|
|
439
|
+
session_activated = not hasattr(self.session, 'activated') or self.session.activated
|
|
440
|
+
if session_activated and session_name in self.cookies:
|
|
440
441
|
self.send_header("Set-Cookie", f"{session_name}={self.cookies[session_name]}", headers)
|
|
441
442
|
|
|
442
443
|
# Custom headers from route
|
|
@@ -585,14 +586,11 @@ class Webserver:
|
|
|
585
586
|
name, val = part.strip().split("=", 1)
|
|
586
587
|
self.cookies[name] = val
|
|
587
588
|
|
|
589
|
+
from tina4_python.Session import LazySession
|
|
588
590
|
session_name = os.getenv("TINA4_SESSION", "PY_SESS")
|
|
589
591
|
session_folder = os.getenv("TINA4_SESSION_FOLDER", os.path.join(tina4_python.root_path, "sessions"))
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
if session_name in self.cookies:
|
|
593
|
-
self.session.load(self.cookies[session_name])
|
|
594
|
-
else:
|
|
595
|
-
self.cookies[session_name] = self.session.start()
|
|
592
|
+
session_handler = os.getenv("TINA4_SESSION_HANDLER", "SessionFileHandler")
|
|
593
|
+
self.session = LazySession(session_name, session_folder, session_handler, self.cookies)
|
|
596
594
|
|
|
597
595
|
# ------------------------------------------------------------------
|
|
598
596
|
# Route or WebSocket
|
|
@@ -21,8 +21,6 @@ Just `pip install tina4-python` and run your project – everything just works.
|
|
|
21
21
|
"""
|
|
22
22
|
import asyncio
|
|
23
23
|
import os
|
|
24
|
-
if os.getenv("TINA4_DEBUG_LEVEL", "") == "":
|
|
25
|
-
os.environ["TINA4_DEBUG_LEVEL"] = "DEBUG"
|
|
26
24
|
|
|
27
25
|
import shutil
|
|
28
26
|
import importlib
|
|
@@ -46,7 +44,7 @@ from tina4_python.Auth import Auth
|
|
|
46
44
|
from tina4_python.Debug import Debug
|
|
47
45
|
from tina4_python.Debug import setup_logging
|
|
48
46
|
from tina4_python.ShellColors import ShellColors
|
|
49
|
-
from tina4_python.Session import Session
|
|
47
|
+
from tina4_python.Session import Session, LazySession
|
|
50
48
|
from tina4_python.HtmlElement import add_html_helpers
|
|
51
49
|
from tina4_python import ShellColors
|
|
52
50
|
from tina4_python.Constant import TINA4_LOG_INFO, TINA4_LOG_ALL, TINA4_LOG_DEBUG
|
|
@@ -428,15 +426,13 @@ async def app(scope, receive, send):
|
|
|
428
426
|
|
|
429
427
|
webserver.cookies = cookie_list
|
|
430
428
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
else:
|
|
439
|
-
webserver.cookies[os.getenv("TINA4_SESSION", "PY_SESS")] = webserver.session.start()
|
|
429
|
+
session_name = os.getenv("TINA4_SESSION", "PY_SESS")
|
|
430
|
+
webserver.session = LazySession(
|
|
431
|
+
session_name,
|
|
432
|
+
os.getenv("TINA4_SESSION_FOLDER", root_path + os.sep + "sessions"),
|
|
433
|
+
os.getenv("TINA4_SESSION_HANDLER", "SessionFileHandler"),
|
|
434
|
+
webserver.cookies,
|
|
435
|
+
)
|
|
440
436
|
|
|
441
437
|
tina4_response, tina4_headers = await webserver.get_response(webserver.method, scope=scope, reader=receive, writer=send, asgi_response=True)
|
|
442
438
|
|
|
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-0.2.193 → tina4_python-0.2.194}/tina4_python/public/js/reconnecting-websocket.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-0.2.193 → tina4_python-0.2.194}/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-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/af/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/af/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/es/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/es/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/fr/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/fr/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/ja/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/ja/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/zh/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.193 → tina4_python-0.2.194}/tina4_python/translations/zh/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|