GNServer 0.0.0.0.21__tar.gz → 0.0.0.0.22__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.
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/GNServer/GNServer/__init__.py +1 -0
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/GNServer/GNServer/_app.py +1 -1
- gnserver-0.0.0.0.22/GNServer/GNServer/tools/__init__.py +1 -0
- gnserver-0.0.0.0.22/GNServer/GNServer/tools/_models.py +77 -0
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/GNServer/GNServer.egg-info/PKG-INFO +1 -1
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/GNServer/GNServer.egg-info/SOURCES.txt +3 -1
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/PKG-INFO +1 -1
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/setup.py +1 -1
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/GNServer/GNServer/___client.py +0 -0
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/GNServer/GNServer/_client.py +0 -0
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/GNServer/GNServer.egg-info/dependency_links.txt +0 -0
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/GNServer/GNServer.egg-info/requires.txt +0 -0
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/GNServer/GNServer.egg-info/top_level.txt +0 -0
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/GNServer/LICENSE +0 -0
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/GNServer/mmbConfig.json +0 -0
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/LICENSE +0 -0
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/MANIFEST.in +0 -0
- {gnserver-0.0.0.0.21 → gnserver-0.0.0.0.22}/setup.cfg +0 -0
@@ -770,7 +770,7 @@ class App:
|
|
770
770
|
|
771
771
|
async def _handle_request(self, request: gn.GNRequest, mode: int):
|
772
772
|
|
773
|
-
request.client._data['remote_addr'] = self._quic._network_paths[0].addr
|
773
|
+
request.client._data['remote_addr'] = self._quic._network_paths[0].addr
|
774
774
|
|
775
775
|
try:
|
776
776
|
|
@@ -0,0 +1 @@
|
|
1
|
+
from ._models import TTLDict
|
@@ -0,0 +1,77 @@
|
|
1
|
+
import time
|
2
|
+
import threading
|
3
|
+
|
4
|
+
class TTLDict:
|
5
|
+
def __init__(self, default_ttl: int = 60, cleanup_interval: int = 300):
|
6
|
+
"""
|
7
|
+
:param default_ttl: TTL по умолчанию (сек), если при записи не указан
|
8
|
+
:param cleanup_interval: периодическая очистка от просроченных ключей (сек), по умолчанию 5 мин
|
9
|
+
"""
|
10
|
+
self._store = {}
|
11
|
+
self._lock = threading.Lock()
|
12
|
+
self._default_ttl = default_ttl
|
13
|
+
self._cleanup_interval = cleanup_interval
|
14
|
+
self._stop_event = threading.Event()
|
15
|
+
|
16
|
+
self._timer = threading.Thread(target=self._cleanup_worker, daemon=True)
|
17
|
+
self._timer.start()
|
18
|
+
|
19
|
+
def set(self, key, value, ttl: int = None):
|
20
|
+
"""Установить значение с временем жизни (в секундах)."""
|
21
|
+
if ttl is None:
|
22
|
+
ttl = self._default_ttl
|
23
|
+
expire_at = time.monotonic() + ttl
|
24
|
+
with self._lock:
|
25
|
+
self._store[key] = (value, expire_at)
|
26
|
+
|
27
|
+
def get(self, key):
|
28
|
+
"""Получить значение или None, если ключ отсутствует/просрочен."""
|
29
|
+
now = time.monotonic()
|
30
|
+
with self._lock:
|
31
|
+
item = self._store.get(key)
|
32
|
+
if not item:
|
33
|
+
return None
|
34
|
+
|
35
|
+
value, expire_at = item
|
36
|
+
if expire_at < now:
|
37
|
+
del self._store[key]
|
38
|
+
return None
|
39
|
+
|
40
|
+
return value
|
41
|
+
|
42
|
+
def __setitem__(self, key, value):
|
43
|
+
"""
|
44
|
+
cache[key] = val → TTL по умолчанию
|
45
|
+
"""
|
46
|
+
self.set(key, value, self._default_ttl)
|
47
|
+
|
48
|
+
def __getitem__(self, key):
|
49
|
+
"""cache[key] -> value | None"""
|
50
|
+
return self.get(key)
|
51
|
+
|
52
|
+
def __contains__(self, key):
|
53
|
+
return self.get(key) is not None
|
54
|
+
|
55
|
+
def __len__(self):
|
56
|
+
return len(self._store)
|
57
|
+
|
58
|
+
def __repr__(self):
|
59
|
+
return f"<TTLDict size={len(self._store)}>"
|
60
|
+
|
61
|
+
def _cleanup_worker(self):
|
62
|
+
"""Фоновый поток для периодической очистки."""
|
63
|
+
while not self._stop_event.wait(self._cleanup_interval):
|
64
|
+
self.cleanup()
|
65
|
+
|
66
|
+
def cleanup(self):
|
67
|
+
"""Удалить все просроченные ключи."""
|
68
|
+
now = time.monotonic()
|
69
|
+
with self._lock:
|
70
|
+
expired = [k for k, (_, exp) in self._store.items() if exp < now]
|
71
|
+
for k in expired:
|
72
|
+
self._store.pop(k, None)
|
73
|
+
|
74
|
+
def stop(self):
|
75
|
+
"""Остановить фон очистки"""
|
76
|
+
self._stop_event.set()
|
77
|
+
self._timer.join(timeout=1)
|
@@ -11,4 +11,6 @@ GNServer/GNServer.egg-info/PKG-INFO
|
|
11
11
|
GNServer/GNServer.egg-info/SOURCES.txt
|
12
12
|
GNServer/GNServer.egg-info/dependency_links.txt
|
13
13
|
GNServer/GNServer.egg-info/requires.txt
|
14
|
-
GNServer/GNServer.egg-info/top_level.txt
|
14
|
+
GNServer/GNServer.egg-info/top_level.txt
|
15
|
+
GNServer/GNServer/tools/__init__.py
|
16
|
+
GNServer/GNServer/tools/_models.py
|
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
|