fastapi-augment 0.1.0__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.
Files changed (50) hide show
  1. fastapi_augment/__init__.py +24 -0
  2. fastapi_augment/common/__init__.py +61 -0
  3. fastapi_augment/common/constants.py +26 -0
  4. fastapi_augment/common/exception_handlers.py +178 -0
  5. fastapi_augment/common/exceptions.py +162 -0
  6. fastapi_augment/common/utils/__init__.py +5 -0
  7. fastapi_augment/common/utils/strings.py +175 -0
  8. fastapi_augment/config/__init__.py +8 -0
  9. fastapi_augment/config/settings.py +104 -0
  10. fastapi_augment/db/__init__.py +5 -0
  11. fastapi_augment/db/sqlalchemy/__init__.py +20 -0
  12. fastapi_augment/db/sqlalchemy/alembic/__init__.py +5 -0
  13. fastapi_augment/db/sqlalchemy/alembic/env.py +141 -0
  14. fastapi_augment/db/sqlalchemy/base.py +9 -0
  15. fastapi_augment/db/sqlalchemy/crud_base.py +426 -0
  16. fastapi_augment/db/sqlalchemy/engine.py +238 -0
  17. fastapi_augment/db/sqlalchemy/migrate.py +356 -0
  18. fastapi_augment/db/sqlalchemy/mixins/__init__.py +18 -0
  19. fastapi_augment/db/sqlalchemy/mixins/audit.py +61 -0
  20. fastapi_augment/db/sqlalchemy/mixins/soft_delete.py +80 -0
  21. fastapi_augment/db/sqlalchemy/mixins/timestamp.py +48 -0
  22. fastapi_augment/db/sqlalchemy/model_base.py +47 -0
  23. fastapi_augment/db/sqlalchemy/session.py +160 -0
  24. fastapi_augment/factory.py +238 -0
  25. fastapi_augment/health/__init__.py +34 -0
  26. fastapi_augment/health/checker.py +101 -0
  27. fastapi_augment/health/checkers.py +109 -0
  28. fastapi_augment/health/router.py +87 -0
  29. fastapi_augment/lifespan.py +450 -0
  30. fastapi_augment/log/__init__.py +26 -0
  31. fastapi_augment/log/config.py +201 -0
  32. fastapi_augment/log/factory.py +32 -0
  33. fastapi_augment/log/filters.py +23 -0
  34. fastapi_augment/log/handlers.py +81 -0
  35. fastapi_augment/middlewares/__init__.py +20 -0
  36. fastapi_augment/middlewares/base.py +79 -0
  37. fastapi_augment/middlewares/request_id.py +82 -0
  38. fastapi_augment/openapi.py +110 -0
  39. fastapi_augment/py.typed +0 -0
  40. fastapi_augment/schemas/__init__.py +29 -0
  41. fastapi_augment/schemas/base.py +32 -0
  42. fastapi_augment/schemas/pagination.py +46 -0
  43. fastapi_augment/schemas/request.py +28 -0
  44. fastapi_augment/schemas/response.py +139 -0
  45. fastapi_augment/schemas/types.py +11 -0
  46. fastapi_augment-0.1.0.dist-info/METADATA +654 -0
  47. fastapi_augment-0.1.0.dist-info/RECORD +50 -0
  48. fastapi_augment-0.1.0.dist-info/WHEEL +5 -0
  49. fastapi_augment-0.1.0.dist-info/entry_points.txt +2 -0
  50. fastapi_augment-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,104 @@
1
+ """
2
+ @Author : zarkhan
3
+ @CreateDate : 2026/9/6
4
+ @Description: 基于 pydantic-settings 的环境配置管理
5
+ - 支持 .env 文件加载(路径由使用者显式指定)
6
+ - 支持环境变量前缀
7
+ - 支持嵌套配置(通过分隔符)
8
+ """
9
+ from __future__ import annotations
10
+
11
+ from typing import Any, TypeVar
12
+
13
+ from pydantic_settings import BaseSettings, SettingsConfigDict
14
+
15
+ # SettingsConfigDict 所有可用键,用于区分配置参数和模型字段值
16
+ _CONFIG_KEYS = frozenset(SettingsConfigDict.__annotations__.keys())
17
+
18
+ _S = TypeVar('_S', bound='EnvSettings')
19
+
20
+
21
+ class EnvSettings(BaseSettings):
22
+ """环境配置基类,继承 pydantic_settings.BaseSettings。
23
+
24
+ 自动从环境变量和 ``.env`` 文件加载配置,通过 ``from_env()`` 直接传参::
25
+
26
+ from fastapi_augment.config import EnvSettings
27
+
28
+ class Settings(EnvSettings):
29
+ database_url: str
30
+ redis_url: str = ''
31
+ debug: bool = False
32
+
33
+ # 直接传入 .env 路径、前缀等,无需导入 SettingsConfigDict
34
+ settings = Settings.from_env(
35
+ env_file='config/.env',
36
+ env_prefix='APP_',
37
+ )
38
+
39
+ 特性:
40
+ - ``.env`` 文件路径由使用者显式指定,库不猜测项目根目录
41
+ - 支持 ``env_prefix`` 前缀过滤(如 ``APP_`` → ``APP_DATABASE_URL``)
42
+ - 支持 ``env_nested_delimiter`` 嵌套配置(如 ``DB__URL=xxx``)
43
+ - 环境变量优先级高于 .env 文件
44
+ - 默认 ``extra='ignore'``,未声明的环境变量被安全忽略
45
+ """
46
+
47
+ model_config = SettingsConfigDict(
48
+ env_prefix='',
49
+ env_file=None,
50
+ env_nested_delimiter=None,
51
+ case_sensitive=False,
52
+ extra='ignore',
53
+ )
54
+
55
+ @classmethod
56
+ def from_env(cls: type[_S], **kwargs: Any) -> _S:
57
+ """从环境加载配置,支持 ``SettingsConfigDict`` 所有参数。
58
+
59
+ 内部自动区分 ``SettingsConfigDict`` 配置参数和模型字段值:
60
+ - 属于 ``SettingsConfigDict`` 的键 → 覆盖 ``model_config``
61
+ - 其余键 → 直接覆盖模型字段值(优先级最高)
62
+
63
+ 常用配置参数:
64
+ env_file: .env 文件路径
65
+ env_prefix: 环境变量前缀(如 ``'APP_'``)
66
+ env_nested_delimiter: 嵌套配置分隔符(如 ``'__'``)
67
+ case_sensitive: 环境变量大小写敏感
68
+ env_file_encoding: .env 文件编码
69
+ secrets_dir: secrets 目录路径
70
+ json_file: JSON 配置文件路径
71
+ yaml_file: YAML 配置文件路径
72
+ toml_file: TOML 配置文件路径
73
+ cli_parse_args: 是否解析命令行参数
74
+
75
+ Returns:
76
+ 配置实例
77
+
78
+ Example::
79
+
80
+ settings = Settings.from_env(
81
+ env_file='config/.env',
82
+ env_prefix='APP_',
83
+ env_nested_delimiter='__',
84
+ debug=True, # 模型字段值覆盖
85
+ )
86
+ """
87
+ config_overrides: dict[str, Any] = {}
88
+ field_overrides: dict[str, Any] = {}
89
+
90
+ for key, value in kwargs.items():
91
+ if key in _CONFIG_KEYS:
92
+ config_overrides[key] = str(value) if key == 'env_file' and value is not None else value
93
+ else:
94
+ field_overrides[key] = value
95
+
96
+ if config_overrides:
97
+ sub_cls = type(
98
+ f'{cls.__name__}__env',
99
+ (cls,),
100
+ {'model_config': SettingsConfigDict(**{**dict(cls.model_config), **config_overrides})},
101
+ )
102
+ return sub_cls(**field_overrides)
103
+
104
+ return cls(**field_overrides)
@@ -0,0 +1,5 @@
1
+ """
2
+ @Author : hangu
3
+ @CreateDate : 2026/8/31
4
+ @Description :
5
+ """
@@ -0,0 +1,20 @@
1
+ """
2
+ @Author : hangu
3
+ @CreateDate : 2026/8/31
4
+ @Description : SQLAlchemy integration — engine, session, model base, mixins, and CRUD base.
5
+ """
6
+ from .base import Base
7
+ from .crud_base import CrudBase
8
+ from .engine import ClusterTopology, EngineManager, NodeConfig
9
+ from .model_base import ModelBase
10
+ from .session import SessionFactory
11
+
12
+ __all__ = [
13
+ 'Base',
14
+ 'CrudBase',
15
+ 'ClusterTopology',
16
+ 'EngineManager',
17
+ 'NodeConfig',
18
+ 'ModelBase',
19
+ 'SessionFactory'
20
+ ]
@@ -0,0 +1,5 @@
1
+ """
2
+ @Author : zarkhan
3
+ @CreateDate : 2026/9/5
4
+ @Description:
5
+ """
@@ -0,0 +1,141 @@
1
+ import asyncio
2
+ import importlib
3
+ from logging import getLogger
4
+ from logging.config import fileConfig
5
+ from os import environ
6
+ from urllib.parse import urlparse
7
+
8
+ from alembic import context
9
+ from sqlalchemy import Connection, pool, engine_from_config
10
+ from sqlalchemy.ext.asyncio import async_engine_from_config
11
+
12
+ from fastapi_augment.db.sqlalchemy import Base
13
+
14
+ _logger = getLogger(__name__)
15
+
16
+ # this is the Alembic Config object, which provides
17
+ # access to the values within the .ini file in use.
18
+ config = context.config
19
+
20
+ # 从 alembic.ini 读取自定义版本表名称,如果没有则使用默认名
21
+ version_table = config.get_main_option('version_table', 'migration_version')
22
+
23
+ # Interpret the config file for Python logging.
24
+ # This line sets up loggers basically.
25
+ if config.config_file_name is not None:
26
+ fileConfig(config.config_file_name)
27
+
28
+ # add your model's MetaData object here
29
+ # for 'autogenerate' support
30
+ # from myapp import mymodel
31
+ # target_metadata = mymodel.Base.metadata
32
+
33
+ # 从环境变量 FASTAPI_AUGMENT_MODELS 动态导入用户模型模块,
34
+ # 使模型注册到 Base.metadata,autogenerate 才能检测到变更。
35
+ _models_env = environ.get('FASTAPI_AUGMENT_MODELS', '')
36
+ if _models_env:
37
+ for _mod in _models_env.split(','):
38
+ _mod = _mod.strip()
39
+ if _mod:
40
+ try:
41
+ importlib.import_module(_mod)
42
+ except ImportError as _e:
43
+ _logger.warning('无法导入模型模块 %s: %s', _mod, _e)
44
+
45
+ target_metadata = Base.metadata
46
+
47
+
48
+ # other values from the config, defined by the needs of env.py,
49
+ # can be acquired:
50
+ # my_important_option = config.get_main_option("my_important_option")
51
+ # ... etc.
52
+
53
+
54
+ def run_migrations_offline() -> None:
55
+ """Run alembic in 'offline' mode.
56
+
57
+ This configures the context with just a URL
58
+ and not an Engine, though an Engine is acceptable
59
+ here as well. By skipping the Engine creation
60
+ we don't even need a DBAPI to be available.
61
+
62
+ Calls to context.execute() here emit the given string to the
63
+ script output.
64
+
65
+ """
66
+ url = config.get_main_option('sqlalchemy.url')
67
+ context.configure(
68
+ url=url,
69
+ target_metadata=target_metadata,
70
+ literal_binds=True,
71
+ dialect_opts={'paramstyle': 'named'},
72
+ version_table=version_table,
73
+ )
74
+
75
+ with context.begin_transaction():
76
+ context.run_migrations()
77
+
78
+
79
+ def do_run_migrations(connection: Connection) -> None:
80
+ """内部实际执行迁移的函数
81
+
82
+ Args:
83
+ connection: SQLAlchemy 数据库连接对象
84
+ """
85
+ context.configure(
86
+ connection=connection,
87
+ target_metadata=target_metadata,
88
+ version_table=version_table
89
+ )
90
+
91
+ with context.begin_transaction():
92
+ context.run_migrations()
93
+
94
+
95
+ async def run_async_migrations() -> None:
96
+ """异步模式运行迁移
97
+
98
+ 创建异步引擎并关联连接至上下文
99
+ """
100
+ connectable = async_engine_from_config(
101
+ config.get_section(config.config_ini_section, {}),
102
+ prefix='sqlalchemy.',
103
+ poolclass=pool.NullPool,
104
+ )
105
+
106
+ async with connectable.connect() as connection:
107
+ await connection.run_sync(do_run_migrations)
108
+
109
+ await connectable.dispose()
110
+
111
+
112
+ def run_migrations_online() -> None:
113
+ """Run alembic in 'online' mode.
114
+
115
+ In this scenario we need to create an Engine
116
+ and associate a connection with the context.
117
+
118
+ """
119
+ url = config.get_main_option('sqlalchemy.url')
120
+ if not url:
121
+ raise ValueError('SQLAlchemy URL 未设置')
122
+
123
+ async_drivers = {'asyncpg', 'asyncmy', 'aiomysql', 'aiosqlite', 'aioodbc'}
124
+ is_async = urlparse(url).scheme.split('+')[-1] in async_drivers
125
+
126
+ if is_async:
127
+ asyncio.run(run_async_migrations())
128
+ else:
129
+ connectable = engine_from_config(
130
+ config.get_section(config.config_ini_section, {}),
131
+ prefix='sqlalchemy.',
132
+ poolclass=pool.NullPool,
133
+ )
134
+ with connectable.connect() as connection:
135
+ do_run_migrations(connection)
136
+
137
+
138
+ if context.is_offline_mode():
139
+ run_migrations_offline()
140
+ else:
141
+ run_migrations_online()
@@ -0,0 +1,9 @@
1
+ """
2
+ @Author : hangu
3
+ @CreateDate : 2026/8/31
4
+ @Description : SQLAlchemy Base
5
+ """
6
+ from sqlalchemy.orm import DeclarativeBase
7
+
8
+
9
+ class Base(DeclarativeBase): ...