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.
@@ -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(),
@@ -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
+ )