qena-shared-lib 0.1.18__py3-none-any.whl → 0.1.20__py3-none-any.whl
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.
- qena_shared_lib/__init__.py +18 -1
- qena_shared_lib/alias.py +27 -0
- qena_shared_lib/cache.py +61 -0
- qena_shared_lib/enums.py +8 -0
- qena_shared_lib/eventbus.py +373 -0
- qena_shared_lib/exceptions.py +2 -5
- qena_shared_lib/http/__init__.py +23 -0
- qena_shared_lib/http/_request.py +24 -0
- qena_shared_lib/http/_response.py +24 -0
- qena_shared_lib/mongodb.py +599 -0
- qena_shared_lib/rabbitmq/__init__.py +2 -0
- qena_shared_lib/rabbitmq/_listener.py +8 -32
- qena_shared_lib/rabbitmq/message/__init__.py +22 -0
- qena_shared_lib/rabbitmq/message/_inbound.py +13 -0
- qena_shared_lib/rabbitmq/message/_outbound.py +13 -0
- qena_shared_lib/redis.py +47 -0
- qena_shared_lib/remotelogging/_base.py +14 -8
- qena_shared_lib/remotelogging/logstash/_base.py +1 -0
- qena_shared_lib/sync.py +91 -0
- {qena_shared_lib-0.1.18.dist-info → qena_shared_lib-0.1.20.dist-info}/METADATA +394 -34
- {qena_shared_lib-0.1.18.dist-info → qena_shared_lib-0.1.20.dist-info}/RECORD +28 -16
- qena_shared_lib-0.1.20.dist-info/WHEEL +4 -0
- qena_shared_lib-0.1.18.dist-info/WHEEL +0 -4
qena_shared_lib/redis.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from inspect import isawaitable
|
|
3
|
+
|
|
4
|
+
from redis.asyncio import ConnectionPool, Redis
|
|
5
|
+
|
|
6
|
+
from .logging import LoggerFactory
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"RedisDependent",
|
|
10
|
+
"RedisManager",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RedisDependent(ABC):
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def attach(self, redis_client: Redis) -> None:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RedisManager:
|
|
21
|
+
def __init__(self, url: str):
|
|
22
|
+
self._logger = LoggerFactory.get_logger("redis")
|
|
23
|
+
self._redis_dependents: set[RedisDependent] = set()
|
|
24
|
+
self._url = url
|
|
25
|
+
|
|
26
|
+
async def connect(self) -> None:
|
|
27
|
+
self._redis_connection_pool = ConnectionPool.from_url(self._url)
|
|
28
|
+
self._redis_client = Redis.from_pool(self._redis_connection_pool)
|
|
29
|
+
|
|
30
|
+
response = self._redis_client.ping()
|
|
31
|
+
|
|
32
|
+
if isawaitable(response):
|
|
33
|
+
await response
|
|
34
|
+
|
|
35
|
+
for redis_dependent in self._redis_dependents:
|
|
36
|
+
redis_dependent.attach(self._redis_client)
|
|
37
|
+
|
|
38
|
+
self._logger.info("connected to redis")
|
|
39
|
+
|
|
40
|
+
async def disconnect(self) -> None:
|
|
41
|
+
await self._redis_connection_pool.aclose()
|
|
42
|
+
self._logger.info("disconnected from redis")
|
|
43
|
+
|
|
44
|
+
def add(self, redis_dependent: RedisDependent) -> "RedisManager":
|
|
45
|
+
self._redis_dependents.add(redis_dependent)
|
|
46
|
+
|
|
47
|
+
return self
|
|
@@ -6,6 +6,7 @@ from asyncio import (
|
|
|
6
6
|
gather,
|
|
7
7
|
)
|
|
8
8
|
from dataclasses import dataclass
|
|
9
|
+
from datetime import datetime
|
|
9
10
|
from enum import Enum
|
|
10
11
|
from sys import exc_info
|
|
11
12
|
from traceback import format_exception
|
|
@@ -47,6 +48,7 @@ class RemoteLogRecord:
|
|
|
47
48
|
self._error_type: str | None = None
|
|
48
49
|
self._error_message: str | None = None
|
|
49
50
|
self._error_stack_trace: str | None = None
|
|
51
|
+
self._created_time = datetime.now()
|
|
50
52
|
self._log_retries = 0
|
|
51
53
|
|
|
52
54
|
@property
|
|
@@ -85,6 +87,18 @@ class RemoteLogRecord:
|
|
|
85
87
|
def error(self) -> tuple[str | None, str | None, str | None]:
|
|
86
88
|
return self._error_type, self._error_message, self._error_stack_trace
|
|
87
89
|
|
|
90
|
+
@property
|
|
91
|
+
def log_retries(self) -> int:
|
|
92
|
+
return self._log_retries
|
|
93
|
+
|
|
94
|
+
@log_retries.setter
|
|
95
|
+
def log_retries(self, log_retries: int) -> None:
|
|
96
|
+
self._log_retries = log_retries
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def created_time(self) -> datetime:
|
|
100
|
+
return self._created_time
|
|
101
|
+
|
|
88
102
|
def error_from_exception(self, exception: BaseException) -> None:
|
|
89
103
|
self._error_type = type(exception).__name__
|
|
90
104
|
self._error_message = str(exception)
|
|
@@ -154,14 +168,6 @@ class RemoteLogRecord:
|
|
|
154
168
|
)
|
|
155
169
|
)
|
|
156
170
|
|
|
157
|
-
@property
|
|
158
|
-
def log_retries(self) -> int:
|
|
159
|
-
return self._log_retries
|
|
160
|
-
|
|
161
|
-
@log_retries.setter
|
|
162
|
-
def log_retries(self, log_retries: int) -> None:
|
|
163
|
-
self._log_retries = log_retries
|
|
164
|
-
|
|
165
171
|
|
|
166
172
|
@dataclass
|
|
167
173
|
class SenderResponse:
|
|
@@ -6,6 +6,7 @@ from .._base import BaseRemoteLogSender, RemoteLogRecord
|
|
|
6
6
|
class BaseLogstashSender(BaseRemoteLogSender):
|
|
7
7
|
def remote_log_record_to_ecs(self, log: RemoteLogRecord) -> dict[str, Any]:
|
|
8
8
|
log_dict: dict[str, Any] = {
|
|
9
|
+
"@timestamp": log.created_time.isoformat(),
|
|
9
10
|
"message": log.message,
|
|
10
11
|
"service.name": log.service_name,
|
|
11
12
|
"log.level": log.log_level.name.lower(),
|
qena_shared_lib/sync.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from inspect import Traceback
|
|
2
|
+
from typing import cast
|
|
3
|
+
|
|
4
|
+
from redis.asyncio import Redis
|
|
5
|
+
from redis.asyncio.lock import Lock
|
|
6
|
+
from typing_extensions import Self
|
|
7
|
+
|
|
8
|
+
from .redis import RedisDependent
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"DistributedLockGuard",
|
|
12
|
+
"DistributedLockManager",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DistributedLockGuard:
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
distributed_lock_manager: "DistributedLockManager",
|
|
20
|
+
key: str,
|
|
21
|
+
blocking: bool = True,
|
|
22
|
+
):
|
|
23
|
+
self._distributed_lock_manager = distributed_lock_manager
|
|
24
|
+
self._key = key
|
|
25
|
+
self._blocking = blocking
|
|
26
|
+
self._is_acquired = False
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def is_aquired(self) -> bool:
|
|
30
|
+
return self._is_acquired
|
|
31
|
+
|
|
32
|
+
async def __aenter__(self) -> Self:
|
|
33
|
+
if self._blocking:
|
|
34
|
+
await self._distributed_lock_manager.acquire(self._key)
|
|
35
|
+
else:
|
|
36
|
+
self._is_acquired = (
|
|
37
|
+
await self._distributed_lock_manager.try_acquire(self._key)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
async def __aexit__(
|
|
43
|
+
self,
|
|
44
|
+
exception_type: type[Exception],
|
|
45
|
+
exception: Exception,
|
|
46
|
+
traceback: Traceback,
|
|
47
|
+
) -> None:
|
|
48
|
+
del exception_type, exception, traceback
|
|
49
|
+
|
|
50
|
+
await self._distributed_lock_manager.release(self._key)
|
|
51
|
+
|
|
52
|
+
self._is_acquired = False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class DistributedLockManager(RedisDependent):
|
|
56
|
+
def __init__(self, lock_timeout: int = 30):
|
|
57
|
+
self._lock_timeout = lock_timeout
|
|
58
|
+
self._lockes: dict[str, Lock] = {}
|
|
59
|
+
|
|
60
|
+
def attach(self, redis_client: Redis) -> None:
|
|
61
|
+
self._redis_client = redis_client
|
|
62
|
+
|
|
63
|
+
async def acquire(self, key: str) -> None:
|
|
64
|
+
lock = self._register_lock(key)
|
|
65
|
+
|
|
66
|
+
await lock.acquire()
|
|
67
|
+
|
|
68
|
+
async def try_acquire(self, key: str) -> bool:
|
|
69
|
+
lock = self._register_lock(key=key, blocking=False)
|
|
70
|
+
is_acquired = await lock.acquire()
|
|
71
|
+
|
|
72
|
+
return cast(bool, is_acquired)
|
|
73
|
+
|
|
74
|
+
def _register_lock(self, key: str, blocking: bool = True) -> Lock:
|
|
75
|
+
if key not in self._lockes:
|
|
76
|
+
self._lockes[key] = self._redis_client.lock(
|
|
77
|
+
name=key, blocking=blocking, timeout=self._lock_timeout
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return self._lockes[key]
|
|
81
|
+
|
|
82
|
+
async def release(self, key: str) -> None:
|
|
83
|
+
if key not in self._lockes or not await self._lockes[key].owned():
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
await self._lockes[key].release()
|
|
87
|
+
|
|
88
|
+
def __call__(self, key: str, blocking: bool = True) -> DistributedLockGuard:
|
|
89
|
+
return DistributedLockGuard(
|
|
90
|
+
distributed_lock_manager=self, key=key, blocking=blocking
|
|
91
|
+
)
|