fastlib-py 0.3.4__tar.gz → 0.3.6__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.
Files changed (79) hide show
  1. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/PKG-INFO +2 -5
  2. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/_logging/config.py +2 -2
  3. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/_logging/handlers.py +3 -4
  4. fastlib_py-0.3.6/fastlib/cache/_memony_cache.py +267 -0
  5. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/cache/_redis_cache.py +5 -5
  6. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/cache/base.py +7 -7
  7. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/cache/manager.py +3 -5
  8. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/config/loader.py +3 -3
  9. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/config/manager.py +7 -7
  10. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/config/registry.py +3 -3
  11. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/config/utils.py +3 -3
  12. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/contextvars.py +2 -2
  13. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/exception/base.py +5 -5
  14. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/exception/exception_handler.py +2 -2
  15. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/i18n/manager.py +2 -2
  16. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/i18n/messages.py +2 -6
  17. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/i18n/types.py +0 -20
  18. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/i18n/utils.py +3 -5
  19. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/logging.py +2 -1
  20. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/mapper/base_mapper.py +15 -15
  21. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/mapper/impl/base_mapper_impl.py +46 -48
  22. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/middleware/db_session.py +4 -5
  23. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/migrate.py +3 -4
  24. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/request.py +1 -3
  25. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/response.py +6 -6
  26. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/security/security.py +5 -4
  27. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/security/utils.py +17 -22
  28. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/service/base_service.py +35 -34
  29. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/service/impl/base_service_impl.py +38 -36
  30. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/stream/event.py +9 -9
  31. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/stream/handler.py +12 -6
  32. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/stream/schema.py +39 -34
  33. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/stream/sse.py +27 -25
  34. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/utils/excel_util.py +3 -4
  35. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/utils/file_util.py +5 -9
  36. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/utils/http_util.py +8 -8
  37. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/utils/snowflake_util.py +1 -2
  38. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/utils/validate_util.py +7 -5
  39. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib_py.egg-info/PKG-INFO +2 -5
  40. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib_py.egg-info/SOURCES.txt +1 -1
  41. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib_py.egg-info/requires.txt +0 -2
  42. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/pyproject.toml +2 -5
  43. fastlib_py-0.3.4/fastlib/cache/_disk_cache.py +0 -219
  44. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/LICENSE +0 -0
  45. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/README.md +0 -0
  46. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/__init__.py +0 -0
  47. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/_logging/__init__.py +0 -0
  48. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/cache/__init__.py +0 -0
  49. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/config/__init__.py +0 -0
  50. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/config/_database_config.py +0 -0
  51. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/config/_security_config.py +0 -0
  52. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/config/_server_config.py +0 -0
  53. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/config/base.py +0 -0
  54. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/constants.py +0 -0
  55. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/db_engine.py +0 -0
  56. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/enums.py +0 -0
  57. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/exception/__init__.py +0 -0
  58. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/exception/exception_manager.py +0 -0
  59. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/i18n/__init__.py +0 -0
  60. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/mapper/__init__.py +0 -0
  61. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/mapper/impl/__init__.py +0 -0
  62. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/middleware/__init__.py +0 -0
  63. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/middleware/i18n.py +0 -0
  64. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/middleware/jwt.py +0 -0
  65. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/openapi.py +0 -0
  66. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/py.typed +0 -0
  67. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/router.py +0 -0
  68. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/schema.py +0 -0
  69. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/security/__init__.py +0 -0
  70. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/service/__init__.py +0 -0
  71. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/service/impl/__init__.py +0 -0
  72. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/stream/__init__.py +0 -0
  73. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/stream/enums.py +0 -0
  74. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/utils/__init__.py +0 -0
  75. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/utils/ip_util.py +0 -0
  76. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib/utils/str_util.py +0 -0
  77. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib_py.egg-info/dependency_links.txt +0 -0
  78. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/fastlib_py.egg-info/top_level.txt +0 -0
  79. {fastlib_py-0.3.4 → fastlib_py-0.3.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastlib-py
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: Fastlib, high performance, easy to use, fast to code, ready for production
5
5
  Author-email: tyvekZhang <tyvekzhang@gmail.com>
6
6
  Maintainer-email: tyvekZhang <tyvekzhang@gmail.com>
@@ -10,21 +10,18 @@ Keywords: fastapi,library,sqlmodel,web
10
10
  Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Programming Language :: Python
12
12
  Classifier: Programming Language :: Python :: 3 :: Only
13
- Classifier: Programming Language :: Python :: 3.9
14
13
  Classifier: Programming Language :: Python :: 3.10
15
14
  Classifier: Programming Language :: Python :: 3.11
16
15
  Classifier: Programming Language :: Python :: 3.12
17
16
  Classifier: Programming Language :: Python :: 3.13
18
17
  Classifier: Programming Language :: Python :: Implementation :: CPython
19
18
  Classifier: Programming Language :: Python :: Implementation :: PyPy
20
- Requires-Python: >=3.9
19
+ Requires-Python: >=3.10
21
20
  Description-Content-Type: text/markdown
22
21
  License-File: LICENSE
23
22
  Requires-Dist: aiosqlite>=0.21
24
23
  Requires-Dist: alembic>=1.15.2
25
- Requires-Dist: asgi-correlation-id>=4.3.4
26
24
  Requires-Dist: cryptography>=45.0.6
27
- Requires-Dist: diskcache>=5.6.3
28
25
  Requires-Dist: fastapi[standard]>=0.115.12
29
26
  Requires-Dist: loguru>=0.7.3
30
27
  Requires-Dist: openpyxl>=3.1.5
@@ -4,7 +4,7 @@ Log configuration and logging utility for the application.
4
4
  """
5
5
 
6
6
  from pathlib import Path
7
- from typing import Literal, Optional
7
+ from typing import Literal
8
8
 
9
9
  from fastlib.config.base import BaseConfig
10
10
  from fastlib.config.manager import config_class
@@ -29,7 +29,7 @@ class LogConfig(BaseConfig):
29
29
  error_log_rotation: str = "1 day",
30
30
  error_log_retention: str = "90 days",
31
31
  enable_async: bool = True,
32
- max_file_size: Optional[str] = "10 MB",
32
+ max_file_size: str | None = "10 MB",
33
33
  time_format: str = "YYYY-MM-DD",
34
34
  **kwargs,
35
35
  ):
@@ -5,12 +5,11 @@ A comprehensive logging tool for the application.
5
5
 
6
6
  import sys
7
7
  from pathlib import Path
8
- from typing import Optional
9
8
 
10
9
  from loguru import logger
11
10
 
12
- from fastlib.config.manager import ConfigManager
13
11
  from fastlib._logging.config import LogConfig
12
+ from fastlib.config.manager import ConfigManager
14
13
 
15
14
 
16
15
  class Logger:
@@ -19,7 +18,7 @@ class Logger:
19
18
  """
20
19
 
21
20
  _instances: dict[str, "Logger"] = {}
22
- _config: Optional[LogConfig] = None
21
+ _config: LogConfig | None = None
23
22
  _is_configured: bool = False
24
23
 
25
24
  @classmethod
@@ -49,7 +48,7 @@ class Logger:
49
48
  @classmethod
50
49
  def get_instance(
51
50
  cls,
52
- name: Optional[str] = None,
51
+ name: str | None = None,
53
52
  ) -> "Logger":
54
53
  """
55
54
  Get or create a named logger instance.
@@ -0,0 +1,267 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """Memory-based cache implementation compatible with Cache interface."""
3
+
4
+ import asyncio
5
+ import time
6
+ from contextlib import asynccontextmanager
7
+ from typing import Any, Dict, Optional, Set
8
+
9
+ from fastlib.cache.base import Cache, CacheError
10
+
11
+
12
+ class MemoryCache(Cache):
13
+ """
14
+ Memory-based cache implementation
15
+ """
16
+
17
+ def __init__(self, namespace: str = "default"):
18
+ self.namespace = namespace
19
+ self._data: Dict[str, Any] = {}
20
+ self._expiry: Dict[str, float] = {}
21
+ self._lists: Dict[str, list] = {}
22
+ self._sets: Dict[str, Set] = {}
23
+ self._hashes: Dict[str, Dict[str, Any]] = {}
24
+ self._locks: Dict[str, asyncio.Lock] = {}
25
+ self._global_lock = asyncio.Lock()
26
+
27
+ def _is_expired(self, key: str) -> bool:
28
+ """Check if a key has expired"""
29
+ if key not in self._expiry:
30
+ return False
31
+ return time.time() > self._expiry[key]
32
+
33
+ def _clean_expired(self):
34
+ """Clean up expired keys"""
35
+ expired_keys = [k for k in self._expiry if self._is_expired(k)]
36
+ for key in expired_keys:
37
+ self._data.pop(key, None)
38
+ self._expiry.pop(key, None)
39
+ self._lists.pop(key, None)
40
+ self._sets.pop(key, None)
41
+ self._hashes.pop(key, None)
42
+
43
+ async def ping(self) -> bool:
44
+ try:
45
+ await self.set("__ping__", "pong", ex=1)
46
+ return True
47
+ except Exception as e:
48
+ raise CacheError("Ping failed", e) from e
49
+
50
+ async def set(
51
+ self,
52
+ key: str,
53
+ value: Any,
54
+ ex: Optional[int] = None,
55
+ nx: bool = False, # only set if not exists
56
+ xx: bool = False, # only set if exists
57
+ ) -> bool:
58
+ async with self._global_lock:
59
+ self._clean_expired()
60
+
61
+ if nx and key in self._data:
62
+ return False
63
+ if xx and key not in self._data:
64
+ return False
65
+
66
+ self._data[key] = value
67
+ if ex is not None:
68
+ self._expiry[key] = time.time() + ex
69
+ elif key in self._expiry:
70
+ del self._expiry[key] # Remove expiration time
71
+
72
+ return True
73
+
74
+ async def get(self, key: str) -> Any | None:
75
+ async with self._global_lock:
76
+ self._clean_expired()
77
+
78
+ if key in self._data and not self._is_expired(key):
79
+ return self._data[key]
80
+ return None
81
+
82
+ async def delete(self, *keys: str) -> int:
83
+ async with self._global_lock:
84
+ count = 0
85
+ for key in keys:
86
+ if key in self._data:
87
+ del self._data[key]
88
+ self._expiry.pop(key, None)
89
+ self._lists.pop(key, None)
90
+ self._sets.pop(key, None)
91
+ self._hashes.pop(key, None)
92
+ count += 1
93
+ return count
94
+
95
+ async def exists(self, *keys: str) -> int:
96
+ async with self._global_lock:
97
+ self._clean_expired()
98
+ return sum(1 for k in keys if k in self._data and not self._is_expired(k))
99
+
100
+ async def expire(self, key: str, seconds: int) -> bool:
101
+ async with self._global_lock:
102
+ if key not in self._data or self._is_expired(key):
103
+ return False
104
+ self._expiry[key] = time.time() + seconds
105
+ return True
106
+
107
+ async def ttl(self, key: str) -> int:
108
+ async with self._global_lock:
109
+ if key not in self._data or key not in self._expiry:
110
+ return -1 # No expiration time set
111
+ if self._is_expired(key):
112
+ return -2 # Key does not exist (expired)
113
+ return int(self._expiry[key] - time.time())
114
+
115
+ async def incr(self, key: str, amount: int = 1) -> int:
116
+ async with self._global_lock:
117
+ self._clean_expired()
118
+
119
+ current = self._data.get(key, 0)
120
+ if not isinstance(current, int):
121
+ raise CacheError("Value is not integer")
122
+
123
+ new_value = current + amount
124
+ self._data[key] = new_value
125
+ return new_value
126
+
127
+ async def decr(self, key: str, amount: int = 1) -> int:
128
+ return await self.incr(key, -amount)
129
+
130
+ # ---------------- Hash (dict) ----------------
131
+ async def hset(self, name: str, key: str, value: Any) -> int:
132
+ async with self._global_lock:
133
+ if name not in self._hashes:
134
+ self._hashes[name] = {}
135
+ self._hashes[name][key] = value
136
+ return 1
137
+
138
+ async def hget(self, name: str, key: str) -> Any | None:
139
+ async with self._global_lock:
140
+ if name in self._hashes:
141
+ return self._hashes[name].get(key)
142
+ return None
143
+
144
+ async def hgetall(self, name: str) -> dict[str, Any]:
145
+ async with self._global_lock:
146
+ return self._hashes.get(name, {}).copy()
147
+
148
+ async def hdel(self, name: str, *keys: str) -> int:
149
+ async with self._global_lock:
150
+ if name not in self._hashes:
151
+ return 0
152
+ removed = 0
153
+ for k in keys:
154
+ if k in self._hashes[name]:
155
+ del self._hashes[name][k]
156
+ removed += 1
157
+ return removed
158
+
159
+ # ---------------- List ----------------
160
+ async def lpush(self, name: str, *values: Any) -> int:
161
+ async with self._global_lock:
162
+ if name not in self._lists:
163
+ self._lists[name] = []
164
+ self._lists[name][:0] = values # Insert at the beginning
165
+ return len(self._lists[name])
166
+
167
+ async def rpush(self, name: str, *values: Any) -> int:
168
+ async with self._global_lock:
169
+ if name not in self._lists:
170
+ self._lists[name] = []
171
+ self._lists[name].extend(values)
172
+ return len(self._lists[name])
173
+
174
+ async def lpop(self, name: str) -> Any | None:
175
+ async with self._global_lock:
176
+ if name not in self._lists or not self._lists[name]:
177
+ return None
178
+ return self._lists[name].pop(0)
179
+
180
+ async def rpop(self, name: str) -> Any | None:
181
+ async with self._global_lock:
182
+ if name not in self._lists or not self._lists[name]:
183
+ return None
184
+ return self._lists[name].pop()
185
+
186
+ async def lrange(self, name: str, start: int = 0, end: int = -1) -> list[Any]:
187
+ async with self._global_lock:
188
+ if name not in self._lists:
189
+ return []
190
+ lst = self._lists[name]
191
+ if end == -1:
192
+ end = len(lst)
193
+ return lst[start:end]
194
+
195
+ # ---------------- Set ----------------
196
+ async def sadd(self, name: str, *values: Any) -> int:
197
+ async with self._global_lock:
198
+ if name not in self._sets:
199
+ self._sets[name] = set()
200
+ before = len(self._sets[name])
201
+ self._sets[name].update(values)
202
+ return len(self._sets[name]) - before
203
+
204
+ async def smembers(self, name: str) -> list[Any]:
205
+ async with self._global_lock:
206
+ if name not in self._sets:
207
+ return []
208
+ return list(self._sets[name])
209
+
210
+ async def srem(self, name: str, *values: Any) -> int:
211
+ async with self._global_lock:
212
+ if name not in self._sets:
213
+ return 0
214
+ before = len(self._sets[name])
215
+ for v in values:
216
+ self._sets[name].discard(v)
217
+ return before - len(self._sets[name])
218
+
219
+ # ---------------- Lock (async-based) ----------------
220
+ async def acquire_lock(
221
+ self,
222
+ lock_name: str,
223
+ timeout: int = 10,
224
+ blocking_timeout: int = 5,
225
+ thread_local: bool = True,
226
+ ) -> bool:
227
+ if lock_name not in self._locks:
228
+ self._locks[lock_name] = asyncio.Lock()
229
+
230
+ try:
231
+ await asyncio.wait_for(self._locks[lock_name].acquire(), timeout=blocking_timeout)
232
+ return True
233
+ except asyncio.TimeoutError:
234
+ return False
235
+
236
+ async def release_lock(self, lock_name: str) -> bool:
237
+ if lock_name in self._locks and self._locks[lock_name].locked():
238
+ self._locks[lock_name].release()
239
+ return True
240
+ return False
241
+
242
+ @asynccontextmanager
243
+ async def lock(self, lock_name: str, timeout: int = 10, blocking_timeout: int = 5):
244
+ acquired = await self.acquire_lock(lock_name, timeout, blocking_timeout)
245
+ if not acquired:
246
+ raise CacheError(f"Failed to acquire lock: {lock_name}")
247
+ try:
248
+ yield
249
+ finally:
250
+ await self.release_lock(lock_name)
251
+
252
+ async def close(self) -> None:
253
+ """Clean up resources"""
254
+ self._data.clear()
255
+ self._expiry.clear()
256
+ self._lists.clear()
257
+ self._sets.clear()
258
+ self._hashes.clear()
259
+ self._locks.clear()
260
+
261
+ def clear(self) -> None:
262
+ """Clear all cached data"""
263
+ self._data.clear()
264
+ self._expiry.clear()
265
+ self._lists.clear()
266
+ self._sets.clear()
267
+ self._hashes.clear()
@@ -47,7 +47,7 @@ class RedisCache(Cache):
47
47
  self,
48
48
  key: str,
49
49
  value: Any,
50
- ex: Optional[int] = None,
50
+ ex: int | None = None,
51
51
  nx: bool = False,
52
52
  xx: bool = False,
53
53
  ) -> bool:
@@ -65,7 +65,7 @@ class RedisCache(Cache):
65
65
  except RedisError as e:
66
66
  raise CacheError(f"Failed to set key {key}", e) from e
67
67
 
68
- async def get(self, key: str) -> Optional[Any]:
68
+ async def get(self, key: str) -> Any | None:
69
69
  """Get a value from cache."""
70
70
  try:
71
71
  return await self.redis_client.get(key)
@@ -127,7 +127,7 @@ class RedisCache(Cache):
127
127
  except RedisError as e:
128
128
  raise CacheError(f"Failed to set hash field {key} in {name}", e) from e
129
129
 
130
- async def hget(self, name: str, key: str) -> Optional[Any]:
130
+ async def hget(self, name: str, key: str) -> Any | None:
131
131
  """Get a field from a hash."""
132
132
  try:
133
133
  return await self.redis_client.hget(name, key)
@@ -164,14 +164,14 @@ class RedisCache(Cache):
164
164
  except RedisError as e:
165
165
  raise CacheError(f"Failed to rpush values to list {name}", e) from e
166
166
 
167
- async def lpop(self, name: str) -> Optional[Any]:
167
+ async def lpop(self, name: str) -> Any | None:
168
168
  """Pop a value from the left of a list."""
169
169
  try:
170
170
  return await self.redis_client.lpop(name)
171
171
  except RedisError as e:
172
172
  raise CacheError(f"Failed to lpop from list {name}", e) from e
173
173
 
174
- async def rpop(self, name: str) -> Optional[Any]:
174
+ async def rpop(self, name: str) -> Any | None:
175
175
  """Pop a value from the right of a list."""
176
176
  try:
177
177
  return await self.redis_client.rpop(name)
@@ -7,13 +7,13 @@ for different caching implementations like Redis, memory cache, etc.
7
7
 
8
8
  from abc import ABC, abstractmethod
9
9
  from contextlib import asynccontextmanager
10
- from typing import Any, Optional
10
+ from typing import Any
11
11
 
12
12
 
13
13
  class CacheError(Exception):
14
14
  """Custom exception for cache-related errors."""
15
15
 
16
- def __init__(self, message: str, original_error: Optional[Exception] = None):
16
+ def __init__(self, message: str, original_error: Exception | None = None):
17
17
  self.message = message
18
18
  self.original_error = original_error
19
19
  super().__init__(f"Cache Error: {message}")
@@ -37,7 +37,7 @@ class Cache(ABC):
37
37
  self,
38
38
  key: str,
39
39
  value: Any,
40
- ex: Optional[int] = None,
40
+ ex: int | None = None,
41
41
  nx: bool = False,
42
42
  xx: bool = False,
43
43
  ) -> bool:
@@ -45,7 +45,7 @@ class Cache(ABC):
45
45
  pass
46
46
 
47
47
  @abstractmethod
48
- async def get(self, key: str) -> Optional[Any]:
48
+ async def get(self, key: str) -> Any | None:
49
49
  """Get a value from cache."""
50
50
  pass
51
51
 
@@ -85,7 +85,7 @@ class Cache(ABC):
85
85
  pass
86
86
 
87
87
  @abstractmethod
88
- async def hget(self, name: str, key: str) -> Optional[Any]:
88
+ async def hget(self, name: str, key: str) -> Any | None:
89
89
  """Get a field from a hash."""
90
90
  pass
91
91
 
@@ -110,12 +110,12 @@ class Cache(ABC):
110
110
  pass
111
111
 
112
112
  @abstractmethod
113
- async def lpop(self, name: str) -> Optional[Any]:
113
+ async def lpop(self, name: str) -> Any | None:
114
114
  """Pop a value from the left of a list."""
115
115
  pass
116
116
 
117
117
  @abstractmethod
118
- async def rpop(self, name: str) -> Optional[Any]:
118
+ async def rpop(self, name: str) -> Any | None:
119
119
  """Pop a value from the right of a list."""
120
120
  pass
121
121
 
@@ -11,15 +11,13 @@ async def get_cache_client() -> Cache:
11
11
  Returns:
12
12
  Cache: Redis client if Redis is enabled in config, otherwise returns page cache.
13
13
  """
14
- from fastlib.cache._redis_cache import RedisManager
15
-
16
14
  config = ConfigManager.get_database_config()
17
15
  if config.enable_redis:
18
- from fastlib.cache._redis_cache import RedisCache
16
+ from fastlib.cache._redis_cache import RedisCache, RedisManager
19
17
 
20
18
  redis_client = await RedisManager.get_instance()
21
19
  return RedisCache(redis_client)
22
20
  else:
23
- from fastlib.cache._disk_cache import PageCache
21
+ from fastlib.cache._memony_cache import MemoryCache
24
22
 
25
- return PageCache()
23
+ return MemoryCache()
@@ -2,7 +2,7 @@
2
2
  """ConfigLoader class for loading and managing application configurations from YAML files."""
3
3
 
4
4
  import os
5
- from typing import Any, Optional
5
+ from typing import Any
6
6
 
7
7
  import yaml
8
8
  from loguru import logger
@@ -12,7 +12,7 @@ from fastlib.config import utils as config_util
12
12
 
13
13
 
14
14
  class ConfigLoader:
15
- def __init__(self, env: str, base_config_file: Optional[str] = None) -> None:
15
+ def __init__(self, env: str, base_config_file: str | None = None) -> None:
16
16
  """
17
17
  Initializes a new instance of the ConfigLoader class
18
18
 
@@ -59,7 +59,7 @@ class ConfigLoader:
59
59
  except yaml.YAMLError as e:
60
60
  raise yaml.YAMLError(f"Error parsing YAML file {file_path}: {e}") from e
61
61
 
62
- def load_config(self, environment: Optional[str] = None) -> dict[str, Any]:
62
+ def load_config(self, environment: str | None = None) -> dict[str, Any]:
63
63
  """
64
64
  Load the base configuration and merge with environment-specific settings.
65
65
  """
@@ -2,7 +2,7 @@
2
2
  """Configuration loading and management with dynamic registry support."""
3
3
 
4
4
  import os
5
- from typing import Any, Optional
5
+ from typing import Any
6
6
 
7
7
  from fastlib import constants as constant
8
8
  from fastlib.config import utils as config_util
@@ -38,7 +38,7 @@ def config_class(name: str):
38
38
  class ConfigManager:
39
39
  """Global configuration manager with static methods and dynamic registry support."""
40
40
 
41
- _global_config_dict: Optional[dict[str, Any]] = None
41
+ _global_config_dict: dict[str, Any] | None = None
42
42
  _config_instances: dict[str, BaseConfig] = {}
43
43
  _initialized: bool = False
44
44
 
@@ -63,9 +63,9 @@ class ConfigManager:
63
63
 
64
64
  @staticmethod
65
65
  def initialize_global_config(
66
- env: Optional[str] = None,
67
- config_file: Optional[str] = None,
68
- custom_configs: Optional[dict[str, Any]] = None,
66
+ env: str | None = None,
67
+ config_file: str | None = None,
68
+ custom_configs: dict[str, Any] | None = None,
69
69
  ) -> None:
70
70
  """
71
71
  Initialize global configuration.
@@ -136,7 +136,7 @@ class ConfigManager:
136
136
  ) from e
137
137
 
138
138
  @staticmethod
139
- def get_config_instance(config_name: str) -> Optional[BaseConfig]:
139
+ def get_config_instance(config_name: str) -> BaseConfig | None:
140
140
  """
141
141
  Get a specific configuration instance.
142
142
 
@@ -205,7 +205,7 @@ class ConfigManager:
205
205
  return ConfigManager._initialized
206
206
 
207
207
  @staticmethod
208
- def get_config_dict() -> Optional[dict[str, Any]]:
208
+ def get_config_dict() -> dict[str, Any] | None:
209
209
  """
210
210
  Get the raw configuration dictionary.
211
211
 
@@ -2,7 +2,7 @@
2
2
  """Configuration registry for dynamic configuration class management."""
3
3
 
4
4
  # Import BaseConfig from config module to avoid circular imports
5
- from typing import Any, Optional
5
+ from typing import Any
6
6
 
7
7
 
8
8
  class ConfigRegistry:
@@ -36,7 +36,7 @@ class ConfigRegistry:
36
36
  cls._registry[name] = config_class
37
37
 
38
38
  @classmethod
39
- def get_config_class(cls, name: str) -> Optional[type]:
39
+ def get_config_class(cls, name: str) -> type | None:
40
40
  """
41
41
  Get a registered configuration class.
42
42
 
@@ -49,7 +49,7 @@ class ConfigRegistry:
49
49
  return cls._registry.get(name)
50
50
 
51
51
  @classmethod
52
- def create_instance(cls, name: str, config_dict: dict[str, Any]) -> Optional[Any]:
52
+ def create_instance(cls, name: str, config_dict: dict[str, Any]) -> Any | None:
53
53
  """
54
54
  Create an instance of a registered configuration class.
55
55
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  import configparser
5
5
  from pathlib import Path
6
- from typing import Any, NamedTuple, Optional
6
+ from typing import Any, NamedTuple
7
7
  from urllib.parse import urlparse
8
8
 
9
9
  from loguru import logger
@@ -36,8 +36,8 @@ class ProjectInfo:
36
36
  self,
37
37
  name: str,
38
38
  version: str,
39
- description: Optional[str] = None,
40
- authors: Optional[list[str]] = None,
39
+ description: str | None = None,
40
+ authors: list[str] | None = None,
41
41
  ):
42
42
  self.name = name
43
43
  self.version = version
@@ -6,12 +6,12 @@ the current authenticated user's ID throughout the request lifecycle.
6
6
  """
7
7
 
8
8
  from contextvars import ContextVar
9
- from typing import Any, Optional
9
+ from typing import Any
10
10
 
11
11
  from loguru import logger
12
12
 
13
13
  # Type aliases for better readability
14
- UserID = Optional[int]
14
+ UserID = int | None
15
15
 
16
16
  # Context variable declaration
17
17
  _current_user_id: ContextVar[UserID] = ContextVar("current_user_id", default=None)
@@ -2,7 +2,7 @@
2
2
  """Base exception class for the application."""
3
3
 
4
4
  from http import HTTPStatus
5
- from typing import Any, Optional
5
+ from typing import Any
6
6
 
7
7
  from pydantic import BaseModel
8
8
 
@@ -27,8 +27,8 @@ class BaseException(Exception):
27
27
  def __init__(
28
28
  self,
29
29
  code: ErrorDetail,
30
- message: Optional[str] = None,
31
- details: Optional[Any] = None,
30
+ message: str | None = None,
31
+ details: Any | None = None,
32
32
  ):
33
33
  """
34
34
  Initialize the HTTPException.
@@ -58,5 +58,5 @@ class SystemErrorCode:
58
58
 
59
59
  class SystemException(BaseException):
60
60
  code: SystemErrorCode
61
- message: Optional[str] = None
62
- details: Optional[Any] = None
61
+ message: str | None = None
62
+ details: Any | None = None
@@ -4,7 +4,7 @@
4
4
  import textwrap
5
5
  import traceback
6
6
  from http import HTTPStatus
7
- from typing import Any, Optional
7
+ from typing import Any
8
8
 
9
9
  from fastapi import Request
10
10
  from fastapi.exception_handlers import http_exception_handler
@@ -95,7 +95,7 @@ def build_error_response(
95
95
  exc: Exception,
96
96
  request: Request,
97
97
  status_code: int,
98
- headers: Optional[dict[str, str]] = None,
98
+ headers: dict[str, str] | None = None,
99
99
  ) -> Response:
100
100
  """
101
101
  Build standardized error response.
@@ -5,8 +5,8 @@ Internationalization context manager
5
5
 
6
6
  import contextvars
7
7
  import functools
8
- from collections.abc import Awaitable
9
- from typing import Any, Callable, TypeVar
8
+ from collections.abc import Awaitable, Callable
9
+ from typing import Any, TypeVar
10
10
 
11
11
  from fastlib.i18n.types import Language
12
12