fastlib-py 0.3.3__tar.gz → 0.3.5__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.3.3 → fastlib_py-0.3.5}/PKG-INFO +2 -3
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/_logging/config.py +2 -2
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/_logging/handlers.py +3 -4
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/cache/_disk_cache.py +6 -6
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/cache/_redis_cache.py +5 -5
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/cache/base.py +7 -7
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/config/loader.py +3 -3
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/config/manager.py +7 -7
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/config/registry.py +3 -3
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/config/utils.py +3 -3
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/contextvars.py +2 -2
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/exception/base.py +5 -5
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/exception/exception_handler.py +2 -2
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/i18n/manager.py +2 -2
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/i18n/messages.py +2 -6
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/i18n/types.py +0 -20
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/i18n/utils.py +3 -5
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/logging.py +2 -1
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/mapper/base_mapper.py +15 -15
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/mapper/impl/base_mapper_impl.py +46 -48
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/middleware/db_session.py +4 -5
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/migrate.py +3 -4
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/request.py +1 -3
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/response.py +6 -6
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/security/security.py +5 -4
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/security/utils.py +17 -22
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/service/base_service.py +35 -34
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/service/impl/base_service_impl.py +38 -36
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/stream/event.py +9 -9
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/stream/handler.py +3 -5
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/stream/schema.py +5 -32
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/stream/sse.py +27 -25
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/utils/excel_util.py +3 -4
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/utils/file_util.py +5 -9
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/utils/http_util.py +8 -8
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/utils/snowflake_util.py +1 -2
- fastlib_py-0.3.5/fastlib/utils/validate_util.py +53 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib_py.egg-info/PKG-INFO +2 -3
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib_py.egg-info/SOURCES.txt +1 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/pyproject.toml +126 -127
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/LICENSE +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/README.md +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/_logging/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/cache/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/cache/manager.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/config/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/config/_database_config.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/config/_security_config.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/config/_server_config.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/config/base.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/constants.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/db_engine.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/enums.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/exception/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/exception/exception_manager.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/i18n/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/mapper/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/mapper/impl/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/middleware/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/middleware/i18n.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/middleware/jwt.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/openapi.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/py.typed +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/router.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/schema.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/security/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/service/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/service/impl/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/stream/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/stream/enums.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/utils/__init__.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/utils/ip_util.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib/utils/str_util.py +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib_py.egg-info/dependency_links.txt +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib_py.egg-info/requires.txt +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/fastlib_py.egg-info/top_level.txt +0 -0
- {fastlib_py-0.3.3 → fastlib_py-0.3.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastlib-py
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
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,14 +10,13 @@ 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.
|
|
19
|
+
Requires-Python: >=3.10
|
|
21
20
|
Description-Content-Type: text/markdown
|
|
22
21
|
License-File: LICENSE
|
|
23
22
|
Requires-Dist: aiosqlite>=0.21
|
|
@@ -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
|
|
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:
|
|
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:
|
|
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:
|
|
51
|
+
name: str | None = None,
|
|
53
52
|
) -> "Logger":
|
|
54
53
|
"""
|
|
55
54
|
Get or create a named logger instance.
|
|
@@ -5,7 +5,7 @@ import json
|
|
|
5
5
|
import threading
|
|
6
6
|
import time
|
|
7
7
|
from contextlib import asynccontextmanager
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import Any
|
|
9
9
|
|
|
10
10
|
import diskcache
|
|
11
11
|
|
|
@@ -28,7 +28,7 @@ class PageCache(Cache):
|
|
|
28
28
|
self,
|
|
29
29
|
key: str,
|
|
30
30
|
value: Any,
|
|
31
|
-
ex:
|
|
31
|
+
ex: int | None = None,
|
|
32
32
|
nx: bool = False, # only set if not exists
|
|
33
33
|
xx: bool = False, # only set if exists
|
|
34
34
|
) -> bool:
|
|
@@ -38,7 +38,7 @@ class PageCache(Cache):
|
|
|
38
38
|
return False
|
|
39
39
|
return self.cache.set(key, value, expire=ex)
|
|
40
40
|
|
|
41
|
-
async def get(self, key: str) ->
|
|
41
|
+
async def get(self, key: str) -> Any | None:
|
|
42
42
|
return self.cache.get(key, None)
|
|
43
43
|
|
|
44
44
|
async def delete(self, *keys: str) -> int:
|
|
@@ -86,7 +86,7 @@ class PageCache(Cache):
|
|
|
86
86
|
self.cache.set(name, data)
|
|
87
87
|
return 1
|
|
88
88
|
|
|
89
|
-
async def hget(self, name: str, key: str) ->
|
|
89
|
+
async def hget(self, name: str, key: str) -> Any | None:
|
|
90
90
|
data = self.cache.get(name, {})
|
|
91
91
|
return data.get(key) if isinstance(data, dict) else None
|
|
92
92
|
|
|
@@ -136,7 +136,7 @@ class PageCache(Cache):
|
|
|
136
136
|
self._set_list(name, lst)
|
|
137
137
|
return len(lst)
|
|
138
138
|
|
|
139
|
-
async def lpop(self, name: str) ->
|
|
139
|
+
async def lpop(self, name: str) -> Any | None:
|
|
140
140
|
lst = self._get_list(name)
|
|
141
141
|
if not lst:
|
|
142
142
|
return None
|
|
@@ -144,7 +144,7 @@ class PageCache(Cache):
|
|
|
144
144
|
self._set_list(name, lst)
|
|
145
145
|
return val
|
|
146
146
|
|
|
147
|
-
async def rpop(self, name: str) ->
|
|
147
|
+
async def rpop(self, name: str) -> Any | None:
|
|
148
148
|
lst = self._get_list(name)
|
|
149
149
|
if not lst:
|
|
150
150
|
return None
|
|
@@ -47,7 +47,7 @@ class RedisCache(Cache):
|
|
|
47
47
|
self,
|
|
48
48
|
key: str,
|
|
49
49
|
value: Any,
|
|
50
|
-
ex:
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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
|
|
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:
|
|
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:
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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
|
|
|
@@ -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
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
67
|
-
config_file:
|
|
68
|
-
custom_configs:
|
|
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) ->
|
|
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() ->
|
|
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
|
|
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) ->
|
|
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]) ->
|
|
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
|
|
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:
|
|
40
|
-
authors:
|
|
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
|
|
9
|
+
from typing import Any
|
|
10
10
|
|
|
11
11
|
from loguru import logger
|
|
12
12
|
|
|
13
13
|
# Type aliases for better readability
|
|
14
|
-
UserID =
|
|
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
|
|
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:
|
|
31
|
-
details:
|
|
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:
|
|
62
|
-
details:
|
|
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
|
|
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:
|
|
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,
|
|
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
|
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Optional, Union
|
|
7
6
|
|
|
8
7
|
from fastlib.constants import RESOURCE_DIR
|
|
9
8
|
from fastlib.i18n.types import Language
|
|
@@ -17,7 +16,7 @@ TRANSLATIONS = {
|
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
|
|
20
|
-
def load_translations(base_dirs:
|
|
19
|
+
def load_translations(base_dirs: Path | list[Path] | None = None):
|
|
21
20
|
"""Load translation files from specified directories.
|
|
22
21
|
|
|
23
22
|
Args:
|
|
@@ -61,9 +60,7 @@ def load_translations(base_dirs: Union[Path, list[Path], None] = None):
|
|
|
61
60
|
TRANSLATIONS[lang.value][namespace] = translations_data
|
|
62
61
|
|
|
63
62
|
|
|
64
|
-
def get_message(
|
|
65
|
-
message_type: str, key: str, language: Optional[Language] = None
|
|
66
|
-
) -> str:
|
|
63
|
+
def get_message(message_type: str, key: str, language: Language | None = None) -> str:
|
|
67
64
|
"""Get translated message for given type, key and language.
|
|
68
65
|
|
|
69
66
|
Args:
|
|
@@ -80,7 +77,6 @@ def get_message(
|
|
|
80
77
|
# Try to get message from requested language
|
|
81
78
|
lang_dict = TRANSLATIONS.get(language.value, {})
|
|
82
79
|
msg = lang_dict.get(message_type, {}).get(key)
|
|
83
|
-
print(f"lang_dict: {lang_dict}")
|
|
84
80
|
if msg:
|
|
85
81
|
return msg
|
|
86
82
|
|
|
@@ -84,23 +84,3 @@ class Language(Enum):
|
|
|
84
84
|
def is_supported(cls, code: str) -> bool:
|
|
85
85
|
"""Check if language code is supported"""
|
|
86
86
|
return code in cls.supported_codes()
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
# if __name__ == "__main__":
|
|
90
|
-
# # Register new languages dynamically
|
|
91
|
-
# Language.register("es", "Spanish", "Español")
|
|
92
|
-
# Language.register("ja", "Japanese", "日本語")
|
|
93
|
-
# Language.register("ar", "Arabic", "العربية", "rtl")
|
|
94
|
-
|
|
95
|
-
# # Access new languages
|
|
96
|
-
# spanish = Language.ES
|
|
97
|
-
# print(f"Spanish code: {spanish.value}") # es
|
|
98
|
-
# print(f"Spanish native: {spanish.native_name}") # Español
|
|
99
|
-
|
|
100
|
-
# # Check support
|
|
101
|
-
# print(Language.is_supported("fr")) # False
|
|
102
|
-
# print(Language.is_supported("ja")) # True
|
|
103
|
-
|
|
104
|
-
# # Get from code
|
|
105
|
-
# japanese = Language.from_code("ja")
|
|
106
|
-
# print(japanese.display_name) # Japanese
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
Internationalization utilities
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from typing import Optional
|
|
7
|
-
|
|
8
6
|
from .manager import I18nContext
|
|
9
7
|
from .messages import get_message
|
|
10
8
|
from .types import Language
|
|
@@ -21,12 +19,12 @@ LANGUAGE_MAPPING = {
|
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
|
|
24
|
-
def _resolve_language(language:
|
|
22
|
+
def _resolve_language(language: Language | None) -> Language:
|
|
25
23
|
"""Return given language or current context default"""
|
|
26
24
|
return language or I18nContext.get_language()
|
|
27
25
|
|
|
28
26
|
|
|
29
|
-
def parse_language(language_str:
|
|
27
|
+
def parse_language(language_str: str | None) -> Language:
|
|
30
28
|
"""Parse string like 'zh-CN', 'en-US' to Language enum"""
|
|
31
29
|
if not language_str:
|
|
32
30
|
return DEFAULT_LANGUAGE
|
|
@@ -36,7 +34,7 @@ def parse_language(language_str: Optional[str]) -> Language:
|
|
|
36
34
|
|
|
37
35
|
|
|
38
36
|
def get_localized_message(
|
|
39
|
-
category: str, key: str, language:
|
|
37
|
+
category: str, key: str, language: Language | None = None
|
|
40
38
|
) -> str:
|
|
41
39
|
"""Get localized message by category ('error', 'success', 'business')"""
|
|
42
40
|
lang = _resolve_language(language)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
5
|
from collections.abc import Sequence
|
|
6
|
-
from typing import Any, Generic,
|
|
6
|
+
from typing import Any, Generic, TypeVar
|
|
7
7
|
|
|
8
8
|
from pydantic import BaseModel
|
|
9
9
|
from sqlmodel import SQLModel
|
|
@@ -19,7 +19,7 @@ IDType = TypeVar("IDType", int, str)
|
|
|
19
19
|
class BaseMapper(ABC, Generic[ModelType]):
|
|
20
20
|
@abstractmethod
|
|
21
21
|
async def insert(
|
|
22
|
-
self, *, data: ModelType, db_session:
|
|
22
|
+
self, *, data: ModelType, db_session: AsyncSession | None = None
|
|
23
23
|
) -> ModelType:
|
|
24
24
|
"""Insert a single data into the database.
|
|
25
25
|
|
|
@@ -37,7 +37,7 @@ class BaseMapper(ABC, Generic[ModelType]):
|
|
|
37
37
|
self,
|
|
38
38
|
*,
|
|
39
39
|
data_list: list[ModelType],
|
|
40
|
-
db_session:
|
|
40
|
+
db_session: AsyncSession | None = None,
|
|
41
41
|
) -> int:
|
|
42
42
|
"""Insert data list into the database in a single operation.
|
|
43
43
|
|
|
@@ -52,8 +52,8 @@ class BaseMapper(ABC, Generic[ModelType]):
|
|
|
52
52
|
|
|
53
53
|
@abstractmethod
|
|
54
54
|
async def select_by_id(
|
|
55
|
-
self, *, id: IDType, db_session:
|
|
56
|
-
) ->
|
|
55
|
+
self, *, id: IDType, db_session: AsyncSession | None = None
|
|
56
|
+
) -> ModelType | None:
|
|
57
57
|
"""Select a single record by its ID.
|
|
58
58
|
|
|
59
59
|
Args:
|
|
@@ -67,7 +67,7 @@ class BaseMapper(ABC, Generic[ModelType]):
|
|
|
67
67
|
|
|
68
68
|
@abstractmethod
|
|
69
69
|
async def select_by_ids(
|
|
70
|
-
self, *, ids: list[IDType], db_session:
|
|
70
|
+
self, *, ids: list[IDType], db_session: AsyncSession | None = None
|
|
71
71
|
) -> list[ModelType]:
|
|
72
72
|
"""Select record list by their IDs.
|
|
73
73
|
|
|
@@ -86,7 +86,7 @@ class BaseMapper(ABC, Generic[ModelType]):
|
|
|
86
86
|
*,
|
|
87
87
|
current: IDType,
|
|
88
88
|
page_size: int,
|
|
89
|
-
db_session:
|
|
89
|
+
db_session: AsyncSession | None = None,
|
|
90
90
|
**kwargs,
|
|
91
91
|
) -> tuple[list[ModelType], int]:
|
|
92
92
|
"""Select record list with pagination.
|
|
@@ -109,7 +109,7 @@ class BaseMapper(ABC, Generic[ModelType]):
|
|
|
109
109
|
current: IDType,
|
|
110
110
|
page_size: int,
|
|
111
111
|
sort: list[SortItem] = None,
|
|
112
|
-
db_session:
|
|
112
|
+
db_session: AsyncSession | None = None,
|
|
113
113
|
**kwargs,
|
|
114
114
|
) -> tuple[list[ModelType], int]:
|
|
115
115
|
"""Select record list with pagination and sorting.
|
|
@@ -133,7 +133,7 @@ class BaseMapper(ABC, Generic[ModelType]):
|
|
|
133
133
|
current: IDType,
|
|
134
134
|
page_size: int,
|
|
135
135
|
sort: list[SortItem] = None,
|
|
136
|
-
db_session:
|
|
136
|
+
db_session: AsyncSession | None = None,
|
|
137
137
|
**kwargs,
|
|
138
138
|
) -> tuple[list[ModelType], int]:
|
|
139
139
|
"""Select record list with pagination and sorting by parent ID.
|
|
@@ -152,7 +152,7 @@ class BaseMapper(ABC, Generic[ModelType]):
|
|
|
152
152
|
|
|
153
153
|
@abstractmethod
|
|
154
154
|
async def update_by_id(
|
|
155
|
-
self, *, data: ModelType, db_session:
|
|
155
|
+
self, *, data: ModelType, db_session: AsyncSession | None = None
|
|
156
156
|
) -> int:
|
|
157
157
|
"""Update a record by its ID.
|
|
158
158
|
|
|
@@ -170,7 +170,7 @@ class BaseMapper(ABC, Generic[ModelType]):
|
|
|
170
170
|
self,
|
|
171
171
|
*,
|
|
172
172
|
data: Sequence[dict[str, Any]],
|
|
173
|
-
db_session:
|
|
173
|
+
db_session: AsyncSession | None = None,
|
|
174
174
|
) -> int:
|
|
175
175
|
"""
|
|
176
176
|
Update multiple records.
|
|
@@ -190,7 +190,7 @@ class BaseMapper(ABC, Generic[ModelType]):
|
|
|
190
190
|
*,
|
|
191
191
|
ids: list[IDType],
|
|
192
192
|
data: dict[str, Any],
|
|
193
|
-
db_session:
|
|
193
|
+
db_session: AsyncSession | None = None,
|
|
194
194
|
) -> int:
|
|
195
195
|
"""Update record list by their IDs with the same values.
|
|
196
196
|
|
|
@@ -206,7 +206,7 @@ class BaseMapper(ABC, Generic[ModelType]):
|
|
|
206
206
|
|
|
207
207
|
@abstractmethod
|
|
208
208
|
async def delete_by_id(
|
|
209
|
-
self, *, id: IDType, db_session:
|
|
209
|
+
self, *, id: IDType, db_session: AsyncSession | None = None
|
|
210
210
|
) -> int:
|
|
211
211
|
"""Delete a data by its ID.
|
|
212
212
|
|
|
@@ -221,7 +221,7 @@ class BaseMapper(ABC, Generic[ModelType]):
|
|
|
221
221
|
|
|
222
222
|
@abstractmethod
|
|
223
223
|
async def batch_delete_by_ids(
|
|
224
|
-
self, *, ids: list[IDType], db_session:
|
|
224
|
+
self, *, ids: list[IDType], db_session: AsyncSession | None = None
|
|
225
225
|
) -> int:
|
|
226
226
|
"""Delete multiple data_list by their IDs.
|
|
227
227
|
|
|
@@ -242,7 +242,7 @@ class BaseMapper(ABC, Generic[ModelType]):
|
|
|
242
242
|
schema_class: SchemaType,
|
|
243
243
|
level: int = 1,
|
|
244
244
|
max_level: int = 5,
|
|
245
|
-
db_session:
|
|
245
|
+
db_session: AsyncSession | None = None,
|
|
246
246
|
) -> list[SchemaType]:
|
|
247
247
|
"""
|
|
248
248
|
Recursively fetch children of given parent items up to 5 levels.
|