fastlib-py 0.2.3__tar.gz → 0.2.4__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.
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/PKG-INFO +1 -1
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/cache/__init__.py +1 -1
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/enums/__init__.py +0 -2
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/middleware/__init__.py +1 -2
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib_py.egg-info/PKG-INFO +1 -1
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib_py.egg-info/SOURCES.txt +0 -2
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/pyproject.toml +1 -1
- fastlib_py-0.2.3/fastlib/decorator/base.py +0 -237
- fastlib_py-0.2.3/fastlib/service/impl/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/LICENSE +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/README.md +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/cache/base.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/cache/disk_cache.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/cache/manager.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/cache/redis_cache.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/config/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/config/base.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/config/database_config.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/config/loader.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/config/manager.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/config/registry.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/config/security_config.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/config/server_config.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/config/utils.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/constant/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/constant/constant.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/context/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/context/contextvars.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/enums/base.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/exception/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/exception/base.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/exception/exception_handler.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/exception/exception_manager.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/i18n/manager.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/i18n/messages.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/i18n/types.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/i18n/utils.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/logging/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/logging/config.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/logging/handlers.py +0 -0
- {fastlib_py-0.2.3/fastlib/decorator → fastlib_py-0.2.4/fastlib/mapper}/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/mapper/base_mapper.py +0 -0
- {fastlib_py-0.2.3/fastlib/mapper → fastlib_py-0.2.4/fastlib/mapper/impl}/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/mapper/impl/base_mapper_impl.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/middleware/db_session.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/middleware/i18n.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/middleware/jwt.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/migrate/migrate.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/openapi/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/openapi/offline.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/router/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/router/base.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/schema/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/schema/base.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/schema/response.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/security/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/security/security.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/security/utils.py +0 -0
- {fastlib_py-0.2.3/fastlib/mapper/impl → fastlib_py-0.2.4/fastlib/service}/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/service/base_service.py +0 -0
- {fastlib_py-0.2.3/fastlib/service → fastlib_py-0.2.4/fastlib/service/impl}/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/service/impl/base_service_impl.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/session/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/session/db_engine.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/session/db_session.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/utils/__init__.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/utils/excel_util.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/utils/file_util.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/utils/http_util.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/utils/ip_util.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/utils/snowflake_util.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib/utils/str_util.py +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib_py.egg-info/dependency_links.txt +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib_py.egg-info/requires.txt +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/fastlib_py.egg-info/top_level.txt +0 -0
- {fastlib_py-0.2.3 → fastlib_py-0.2.4}/setup.cfg +0 -0
|
@@ -20,8 +20,6 @@ fastlib/constant/__init__.py
|
|
|
20
20
|
fastlib/constant/constant.py
|
|
21
21
|
fastlib/context/__init__.py
|
|
22
22
|
fastlib/context/contextvars.py
|
|
23
|
-
fastlib/decorator/__init__.py
|
|
24
|
-
fastlib/decorator/base.py
|
|
25
23
|
fastlib/enums/__init__.py
|
|
26
24
|
fastlib/enums/base.py
|
|
27
25
|
fastlib/exception/__init__.py
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
# SPDX-License-Identifier: MIT
|
|
2
|
-
"""Commonly used decorator modules"""
|
|
3
|
-
import asyncio
|
|
4
|
-
from functools import wraps
|
|
5
|
-
import functools
|
|
6
|
-
import hashlib
|
|
7
|
-
import json
|
|
8
|
-
import time
|
|
9
|
-
from typing import Any, Callable, Optional
|
|
10
|
-
from loguru import Logger
|
|
11
|
-
|
|
12
|
-
from fastlib.cache.manager import get_cache_client
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def log_request_time(logger_name: Optional[str] = None):
|
|
16
|
-
"""
|
|
17
|
-
Decorator to log API request execution time.
|
|
18
|
-
|
|
19
|
-
Args:
|
|
20
|
-
logger_name: Name of the logger instance to use. If None, uses default logger.
|
|
21
|
-
|
|
22
|
-
Returns:
|
|
23
|
-
Decorator function
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
def decorator(func: Callable):
|
|
27
|
-
@wraps(func)
|
|
28
|
-
async def async_wrapper(*args, **kwargs):
|
|
29
|
-
return await _time_request_async(func, logger_name, *args, **kwargs)
|
|
30
|
-
|
|
31
|
-
@wraps(func)
|
|
32
|
-
def sync_wrapper(*args, **kwargs):
|
|
33
|
-
return _time_request_sync(func, logger_name, *args, **kwargs)
|
|
34
|
-
|
|
35
|
-
# Return appropriate wrapper based on function type
|
|
36
|
-
if asyncio.iscoroutinefunction(func):
|
|
37
|
-
return async_wrapper
|
|
38
|
-
return sync_wrapper
|
|
39
|
-
|
|
40
|
-
return decorator
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
async def _time_request_async(func: Callable, logger_name: str, *args, **kwargs) -> Any:
|
|
44
|
-
"""
|
|
45
|
-
Internal function to time and log async request execution.
|
|
46
|
-
|
|
47
|
-
Args:
|
|
48
|
-
func: The decorated function
|
|
49
|
-
logger_name: Logger instance name
|
|
50
|
-
*args: Function arguments
|
|
51
|
-
**kwargs: Function keyword arguments
|
|
52
|
-
|
|
53
|
-
Returns:
|
|
54
|
-
Result of the function execution
|
|
55
|
-
"""
|
|
56
|
-
# Get logger instance
|
|
57
|
-
logger_instance = Logger.get_instance(logger_name)
|
|
58
|
-
|
|
59
|
-
# Record start time
|
|
60
|
-
start_time = time.time()
|
|
61
|
-
|
|
62
|
-
try:
|
|
63
|
-
# Execute the function
|
|
64
|
-
result = await func(*args, **kwargs)
|
|
65
|
-
|
|
66
|
-
# Calculate execution time
|
|
67
|
-
execution_time = time.time() - start_time
|
|
68
|
-
|
|
69
|
-
# Log success
|
|
70
|
-
logger_instance.info(
|
|
71
|
-
f"API endpoint {func.__name__} executed successfully",
|
|
72
|
-
execution_time_ms=round(execution_time * 1000, 2),
|
|
73
|
-
status="success",
|
|
74
|
-
endpoint=func.__name__,
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
return result
|
|
78
|
-
|
|
79
|
-
except Exception as e:
|
|
80
|
-
# Calculate execution time (even if error occurred)
|
|
81
|
-
execution_time = time.time() - start_time
|
|
82
|
-
|
|
83
|
-
# Log error
|
|
84
|
-
logger_instance.error(
|
|
85
|
-
f"API endpoint {func.__name__} execution failed: {str(e)}",
|
|
86
|
-
execution_time_ms=round(execution_time * 1000, 2),
|
|
87
|
-
status="error",
|
|
88
|
-
endpoint=func.__name__,
|
|
89
|
-
exc_info=e,
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
# Re-raise the exception
|
|
93
|
-
raise
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def _time_request_sync(func: Callable, logger_name: str, *args, **kwargs) -> Any:
|
|
97
|
-
"""
|
|
98
|
-
Internal function to time and log synchronous request execution.
|
|
99
|
-
|
|
100
|
-
Args:
|
|
101
|
-
func: The decorated function
|
|
102
|
-
logger_name: Logger instance name
|
|
103
|
-
*args: Function arguments
|
|
104
|
-
**kwargs: Function keyword arguments
|
|
105
|
-
|
|
106
|
-
Returns:
|
|
107
|
-
Result of the function execution
|
|
108
|
-
"""
|
|
109
|
-
# Get logger instance
|
|
110
|
-
logger_instance = Logger.get_instance(logger_name)
|
|
111
|
-
|
|
112
|
-
# Record start time
|
|
113
|
-
start_time = time.time()
|
|
114
|
-
|
|
115
|
-
try:
|
|
116
|
-
# Execute the function
|
|
117
|
-
result = func(*args, **kwargs)
|
|
118
|
-
|
|
119
|
-
# Calculate execution time
|
|
120
|
-
execution_time = time.time() - start_time
|
|
121
|
-
|
|
122
|
-
# Log success
|
|
123
|
-
logger_instance.info(
|
|
124
|
-
f"API endpoint {func.__name__} executed successfully",
|
|
125
|
-
execution_time_ms=round(execution_time * 1000, 2),
|
|
126
|
-
status="success",
|
|
127
|
-
endpoint=func.__name__,
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
return result
|
|
131
|
-
|
|
132
|
-
except Exception as e:
|
|
133
|
-
# Calculate execution time (even if error occurred)
|
|
134
|
-
execution_time = time.time() - start_time
|
|
135
|
-
|
|
136
|
-
# Log error
|
|
137
|
-
logger_instance.error(
|
|
138
|
-
f"API endpoint {func.__name__} execution failed: {str(e)}",
|
|
139
|
-
execution_time_ms=round(execution_time * 1000, 2),
|
|
140
|
-
status="error",
|
|
141
|
-
endpoint=func.__name__,
|
|
142
|
-
exc_info=e,
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
# Re-raise the exception
|
|
146
|
-
raise
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def prevent_duplicate_submission(
|
|
150
|
-
key_prefix: str = "duplicate_submission",
|
|
151
|
-
ttl: int = 60,
|
|
152
|
-
include_args: bool = True,
|
|
153
|
-
include_kwargs: bool = True,
|
|
154
|
-
custom_key_func: Optional[Callable] = None,
|
|
155
|
-
):
|
|
156
|
-
"""Decorator to prevent duplicate submissions within a specified time window.
|
|
157
|
-
|
|
158
|
-
Args:
|
|
159
|
-
key_prefix: Prefix for the cache key
|
|
160
|
-
ttl: Time to live in seconds for the duplicate check
|
|
161
|
-
include_args: Whether to include function args in the cache key
|
|
162
|
-
include_kwargs: Whether to include function kwargs in the cache key
|
|
163
|
-
custom_key_func: Optional custom function to generate cache key
|
|
164
|
-
|
|
165
|
-
Returns:
|
|
166
|
-
Decorated function that prevents duplicate submissions
|
|
167
|
-
"""
|
|
168
|
-
|
|
169
|
-
def decorator(func: Callable) -> Callable:
|
|
170
|
-
@functools.wraps(func)
|
|
171
|
-
async def async_wrapper(*args, **kwargs):
|
|
172
|
-
# Generate cache key
|
|
173
|
-
if custom_key_func:
|
|
174
|
-
cache_key = custom_key_func(*args, **kwargs)
|
|
175
|
-
else:
|
|
176
|
-
key_parts = [key_prefix, func.__name__]
|
|
177
|
-
|
|
178
|
-
if include_args and args:
|
|
179
|
-
# Skip 'self' for instance methods
|
|
180
|
-
args_to_hash = (
|
|
181
|
-
args[1:] if args and hasattr(args[0], "__class__") else args
|
|
182
|
-
)
|
|
183
|
-
key_parts.append(str(args_to_hash))
|
|
184
|
-
|
|
185
|
-
if include_kwargs and kwargs:
|
|
186
|
-
# Sort kwargs for consistent key generation
|
|
187
|
-
sorted_kwargs = json.dumps(kwargs, sort_keys=True)
|
|
188
|
-
key_parts.append(sorted_kwargs)
|
|
189
|
-
|
|
190
|
-
# Create hash of the key parts for a shorter key
|
|
191
|
-
key_content = ":".join(key_parts)
|
|
192
|
-
cache_key = (
|
|
193
|
-
f"{key_prefix}:{hashlib.md5(key_content.encode()).hexdigest()}"
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
# Get cache client
|
|
197
|
-
cache = await get_cache_client()
|
|
198
|
-
|
|
199
|
-
# Check if request is duplicate
|
|
200
|
-
existing = await cache.get(cache_key)
|
|
201
|
-
if existing is not None:
|
|
202
|
-
raise DuplicateSubmissionError(
|
|
203
|
-
f"Duplicate submission detected. Please wait {ttl} seconds before retrying."
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
# Set lock in cache
|
|
207
|
-
await cache.set(cache_key, "locked", ttl=ttl)
|
|
208
|
-
|
|
209
|
-
try:
|
|
210
|
-
# Execute the original function
|
|
211
|
-
result = await func(*args, **kwargs)
|
|
212
|
-
return result
|
|
213
|
-
except Exception as e:
|
|
214
|
-
# On error, remove the lock to allow retry
|
|
215
|
-
await cache.delete(cache_key)
|
|
216
|
-
raise e
|
|
217
|
-
|
|
218
|
-
@functools.wraps(func)
|
|
219
|
-
def sync_wrapper(*args, **kwargs):
|
|
220
|
-
raise NotImplementedError(
|
|
221
|
-
"Synchronous functions are not supported. "
|
|
222
|
-
"Please use async functions with prevent_duplicate_submission decorator."
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
# Return appropriate wrapper based on function type
|
|
226
|
-
if asyncio.iscoroutinefunction(func):
|
|
227
|
-
return async_wrapper
|
|
228
|
-
else:
|
|
229
|
-
return sync_wrapper
|
|
230
|
-
|
|
231
|
-
return decorator
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
class DuplicateSubmissionError(Exception):
|
|
235
|
-
"""Exception raised when a duplicate submission is detected."""
|
|
236
|
-
|
|
237
|
-
pass
|
|
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
|
|
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
|