sdpy-kit 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.
kit/common_kit.py ADDED
@@ -0,0 +1,221 @@
1
+ """
2
+ Copyright (c) 2021-2026 Clark Chang. All Rights Reserved.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+
16
+ Project: sdpy
17
+ Author: Clark Chang
18
+ """
19
+
20
+ # date: 2026-08-22
21
+ """公共工具类,提供雪花算法 ID 生成和系统时间格式化功能
22
+
23
+ 重构说明(破坏性内部、外部零风险):
24
+ - 将原先 `_initialize` / `configure` / `_INITIALIZED` 三套冗余的初始化
25
+ 逻辑收敛为唯一的 `configure` 入口 + 惰性 `_ensure_initialized`。
26
+ - 用 `_DATACENTER_ID is None` 语义替代 `_INITIALIZED` 布尔标记,状态更清晰。
27
+ - 公开方法名与签名不变。
28
+ """
29
+
30
+ import threading
31
+ import time
32
+ from datetime import datetime
33
+ from random import randint
34
+
35
+
36
+ class CommonKit:
37
+ """公共工具类,提供分布式唯一 ID 生成和系统时间格式化功能
38
+
39
+ 雪花算法 ID 结构(共 63 位,符号位为 0):
40
+ - timestamp(41位):毫秒级时间戳,约可用 69 年
41
+ - datacenter_id(5位):数据中心 ID,有效范围 1-31
42
+ - machine_id(5位):机器 ID,有效范围 1-31
43
+ - sequence(12位):每毫秒内序列号,最大值 4095
44
+
45
+ 支持多线程并发调用,保证 ID 唯一性。
46
+ """
47
+
48
+ _TIMESTAMP_SHIFT: int = 22
49
+ _DATACENTER_ID_SHIFT: int = 17
50
+ _MACHINE_ID_SHIFT: int = 12
51
+
52
+ _MAX_DATACENTER_ID: int = 31
53
+ _MAX_MACHINE_ID: int = 31
54
+ _MAX_SEQUENCE: int = 4095
55
+
56
+ _EPOCH: int = 1577836800000
57
+
58
+ # None 表示尚未初始化(未调用 configure)
59
+ _DATACENTER_ID: int | None = None
60
+ _MACHINE_ID: int | None = None
61
+
62
+ _lock: threading.Lock = threading.Lock()
63
+
64
+ _last_timestamp: int = -1
65
+ _sequence: int = 0
66
+
67
+ @classmethod
68
+ def configure(cls, datacenter_id: int, machine_id: int) -> None:
69
+ """配置雪花算法的数据中心 ID 和机器 ID
70
+
71
+ Args:
72
+ datacenter_id: 数据中心 ID,有效范围 1-31
73
+ machine_id: 机器 ID,有效范围 1-31
74
+
75
+ Raises:
76
+ ValueError: 参数超出有效范围时抛出
77
+ """
78
+ if not (1 <= datacenter_id <= cls._MAX_DATACENTER_ID):
79
+ raise ValueError(
80
+ f"数据中心 ID 必须在 1-{cls._MAX_DATACENTER_ID} 之间,"
81
+ f"当前值: {datacenter_id}"
82
+ )
83
+ if not (1 <= machine_id <= cls._MAX_MACHINE_ID):
84
+ raise ValueError(
85
+ f"机器 ID 必须在 1-{cls._MAX_MACHINE_ID} 之间,当前值: {machine_id}"
86
+ )
87
+ with cls._lock:
88
+ cls._DATACENTER_ID = datacenter_id
89
+ cls._MACHINE_ID = machine_id
90
+
91
+ @classmethod
92
+ def _ensure_initialized(cls) -> None:
93
+ """惰性初始化:首次使用时采用默认配置 (1, 1)"""
94
+ if cls._DATACENTER_ID is None:
95
+ cls.configure(1, 1)
96
+
97
+ @classmethod
98
+ def _get_current_timestamp(cls) -> int:
99
+ """获取当前时间戳(毫秒)"""
100
+ return int(time.time() * 1000)
101
+
102
+ @classmethod
103
+ def _wait_for_next_millis(cls, last_timestamp: int) -> int:
104
+ """等待直到进入下一毫秒"""
105
+ timestamp = cls._get_current_timestamp()
106
+ while timestamp <= last_timestamp:
107
+ timestamp = cls._get_current_timestamp()
108
+ return timestamp
109
+
110
+ @classmethod
111
+ def generate_snowflake_id(
112
+ cls, datacenter_id: int | None = None, machine_id: int | None = None
113
+ ) -> int:
114
+ """生成雪花算法 ID(数值类型)
115
+
116
+ Args:
117
+ datacenter_id: 数据中心 ID(可选),有效范围 1-31,默认使用配置值
118
+ machine_id: 机器 ID(可选),有效范围 1-31,默认使用配置值
119
+
120
+ Returns:
121
+ int: 63 位雪花 ID 数值
122
+
123
+ Raises:
124
+ ValueError: 参数超出有效范围或时钟回拨时抛出
125
+ """
126
+ if datacenter_id is None or machine_id is None:
127
+ cls._ensure_initialized()
128
+ assert cls._DATACENTER_ID is not None
129
+ assert cls._MACHINE_ID is not None
130
+ if datacenter_id is None:
131
+ datacenter_id = cls._DATACENTER_ID
132
+ if machine_id is None:
133
+ machine_id = cls._MACHINE_ID
134
+
135
+ if not (1 <= datacenter_id <= cls._MAX_DATACENTER_ID):
136
+ raise ValueError(f"数据中心 ID 必须在 1-{cls._MAX_DATACENTER_ID} 之间")
137
+ if not (1 <= machine_id <= cls._MAX_MACHINE_ID):
138
+ raise ValueError(f"机器 ID 必须在 1-{cls._MAX_MACHINE_ID} 之间")
139
+
140
+ with cls._lock:
141
+ timestamp = cls._get_current_timestamp()
142
+
143
+ if timestamp < cls._last_timestamp:
144
+ raise ValueError(
145
+ f"时钟回拨检测:当前时间戳({timestamp})小于上次使用时间戳"
146
+ f"({cls._last_timestamp}),无法生成 ID,请检查系统时间"
147
+ )
148
+
149
+ if timestamp == cls._last_timestamp:
150
+ cls._sequence = (cls._sequence + 1) & cls._MAX_SEQUENCE
151
+ if cls._sequence == 0:
152
+ timestamp = cls._wait_for_next_millis(cls._last_timestamp)
153
+ else:
154
+ cls._sequence = 0
155
+
156
+ cls._last_timestamp = timestamp
157
+
158
+ return (
159
+ ((timestamp - cls._EPOCH) << cls._TIMESTAMP_SHIFT)
160
+ | (datacenter_id << cls._DATACENTER_ID_SHIFT)
161
+ | (machine_id << cls._MACHINE_ID_SHIFT)
162
+ | cls._sequence
163
+ )
164
+
165
+ @classmethod
166
+ def generate_snow_id(
167
+ cls, datacenter_id: int | None = None, machine_id: int | None = None
168
+ ) -> str:
169
+ """生成雪花算法 ID(字符串类型)
170
+
171
+ Args:
172
+ datacenter_id: 数据中心 ID(可选),有效范围 1-31,默认使用配置值
173
+ machine_id: 机器 ID(可选),有效范围 1-31,默认使用配置值
174
+
175
+ Returns:
176
+ str: 雪花 ID 字符串形式
177
+ """
178
+ return str(cls.generate_snowflake_id(datacenter_id, machine_id))
179
+
180
+ @staticmethod
181
+ def generate_timestamp_id() -> str:
182
+ """生成基于当前时间的唯一 ID(20 位)
183
+
184
+ 格式: YYYYMMDDHHMMSSfffRRR,后 3 位为随机数确保同毫秒内唯一性
185
+ """
186
+ timestamp = datetime.now().strftime("%Y%m%d%H%M%S%f")[:-3]
187
+ random_suffix = randint(0, 999)
188
+ return f"{timestamp}{random_suffix:03d}"
189
+
190
+ @staticmethod
191
+ def format_current_time(format_pattern: str = "yyyyMMdd") -> str:
192
+ """格式化当前系统时间
193
+
194
+ 支持格式: yyyyMMdd, yyyyMMddhhmm, yyyyMMddhhmmss,
195
+ yyyy-MM-dd, yyyy-MM-dd hh:mm, yyyy-MM-dd hh:mm:ss
196
+ """
197
+ format_map = {
198
+ "yyyyMMdd": "%Y%m%d",
199
+ "yyyyMMddhhmm": "%Y%m%d%H%M",
200
+ "yyyyMMddhhmmss": "%Y%m%d%H%M%S",
201
+ "yyyy-MM-dd": "%Y-%m-%d",
202
+ "yyyy-MM-dd hh:mm": "%Y-%m-%d %H:%M",
203
+ "yyyy-MM-dd hh:mm:ss": "%Y-%m-%d %H:%M:%S",
204
+ }
205
+ if format_pattern not in format_map:
206
+ raise ValueError(
207
+ f"不支持的格式模式: {format_pattern},支持的格式有: "
208
+ f"{', '.join(format_map.keys())}"
209
+ )
210
+
211
+ try:
212
+ return time.strftime(format_map[format_pattern])
213
+ except Exception as e:
214
+ raise RuntimeError(f"获取系统时间失败: {e}")
215
+
216
+ @classmethod
217
+ def reset(cls) -> None:
218
+ """重置 ID 生成器状态"""
219
+ with cls._lock:
220
+ cls._last_timestamp = -1
221
+ cls._sequence = 0
kit/config/__init__.py ADDED
@@ -0,0 +1,44 @@
1
+ """
2
+ Copyright (c) 2021-2026 Clark Chang. All Rights Reserved.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+
16
+ Project: sdpy
17
+ Author: Clark Chang
18
+ """
19
+
20
+ # date: 2026-09-08
21
+ """Kit 配置子包:统一导出配置与日志入口"""
22
+
23
+ from kit.config.kit_config import Settings, get_settings
24
+ from kit.config.log_config import (
25
+ critical,
26
+ debug,
27
+ error,
28
+ get_default_logger,
29
+ info,
30
+ setup_logger,
31
+ warning,
32
+ )
33
+
34
+ __all__ = [
35
+ "Settings",
36
+ "get_settings",
37
+ "setup_logger",
38
+ "get_default_logger",
39
+ "debug",
40
+ "info",
41
+ "warning",
42
+ "error",
43
+ "critical",
44
+ ]
@@ -0,0 +1,147 @@
1
+ """
2
+ Copyright (c) 2021-2026 Clark Chang. All Rights Reserved.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+
16
+ Project: sdpy
17
+ Author: Clark Chang
18
+ """
19
+
20
+ # date: 2025-04-19
21
+ # source: 迁移自 config/sys_config.py(kit 自包含配置,修改须与源文件评估双向同步)
22
+ """Kit 统一配置模块"""
23
+
24
+ from functools import lru_cache
25
+
26
+ from pydantic import Field
27
+ from pydantic_settings import BaseSettings
28
+
29
+
30
+ class Settings(BaseSettings):
31
+ APP_NAME: str = Field(default="SDPY App")
32
+ VERSION: str = Field(default="0.1.0")
33
+ DEBUG: bool = Field(default=True)
34
+ HOST: str = Field(default="0.0.0.0")
35
+ PORT: int = Field(default=8000)
36
+
37
+ LOG_LEVEL: str = Field(default="INFO")
38
+ LOG_TO_CONSOLE: bool = Field(default=True)
39
+ LOG_TO_FILE: bool = Field(default=False)
40
+ LOG_DIR: str = Field(default="./logs")
41
+ LOG_FILE: str = Field(default="app.log")
42
+
43
+ REDIS_HOST: str = Field(default="localhost")
44
+ REDIS_PORT: int = Field(default=6379)
45
+ REDIS_DB: int = Field(default=0)
46
+ REDIS_PASSWORD: str | None = Field(default=None)
47
+
48
+ # 数据库统一配置(PostgreSQL / MySQL 共用)
49
+ # 切换数据库只需修改 DEFAULT_DB_TYPE,无需修改连接参数
50
+ DEFAULT_DB_TYPE: str = Field(default="postgresql")
51
+
52
+ DB_HOST: str = Field(default="localhost")
53
+ # PostgreSQL 专用
54
+ DB_PORT: int = Field(default=5432)
55
+ DB_DATABASE: str = Field(default="postgres")
56
+ DB_USER: str = Field(default="admin")
57
+ DB_PASSWORD: str = Field(default="1qaz@WSX3edc")
58
+
59
+ # MySQL 专用覆盖(当端口/用户/库名与 PG 不同时使用)
60
+ MYSQL_PORT: int = Field(default=3306)
61
+ MYSQL_DATABASE: str = Field(default="mydb")
62
+ MYSQL_USER: str = Field(default="root")
63
+ MYSQL_CHARSET: str = Field(default="utf8mb4")
64
+
65
+ # SQLite 专用(切换到 SQLite 时取消注释)
66
+ SQLITE_DB_PATH: str = Field(default="./data/sqlite.db")
67
+
68
+ # zvec 向量库配置(本地嵌入式向量库,独立于 kit.db)
69
+ ZVEC_DATA_DIR: str = Field(
70
+ default="./data/zvec", description="zvec Collection 根目录"
71
+ )
72
+ ZVEC_QUERY_THREADS: int | None = Field(
73
+ default=None, description="查询线程数,None 自适应"
74
+ )
75
+ ZVEC_OPTIMIZE_THREADS: int | None = Field(
76
+ default=None, description="索引构建线程数,None 自适应"
77
+ )
78
+ ZVEC_MEMORY_LIMIT_MB: int | None = Field(
79
+ default=None, description="内存软上限(MB),None 自适应"
80
+ )
81
+ ZVEC_DEFAULT_METRIC: str = Field(default="cosine", description="默认向量距离度量")
82
+ ZVEC_LOG_DIR: str = Field(
83
+ default="./logs/zvec", description="zvec 引擎独立日志目录"
84
+ )
85
+ ZVEC_LOG_LEVEL: str = Field(
86
+ default="warn", description="zvec 引擎日志级别(debug/info/warn/error/fatal)"
87
+ )
88
+
89
+ FILE_STORAGE_ROOT: str = Field(default="./data")
90
+
91
+ # AI 模型档案配置文件路径(chat/embedding/rerank/image 四类型多厂商档案)
92
+ AI_MODELS_CONFIG_PATH: str = Field(
93
+ default="./config/ai_models.json", description="AI 模型档案配置文件路径"
94
+ )
95
+
96
+ # AI 候选链优先级(可选,逗号分隔档案名):设置后覆盖 JSON default 顺序;
97
+ # JSON 中不存在的档案名会被丢弃并告警
98
+ AI_CHAT_PROFILES: str | None = Field(
99
+ default=None, description="chat 类型候选链优先级(逗号分隔档案名)"
100
+ )
101
+ AI_EMBEDDING_PROFILES: str | None = Field(
102
+ default=None, description="embedding 类型候选链优先级(逗号分隔档案名)"
103
+ )
104
+ AI_RERANK_PROFILES: str | None = Field(
105
+ default=None, description="rerank 类型候选链优先级(逗号分隔档案名)"
106
+ )
107
+ AI_IMAGE_PROFILES: str | None = Field(
108
+ default=None, description="image 类型候选链优先级(逗号分隔档案名)"
109
+ )
110
+
111
+ # MCP 客户端配置(通用 MCP 服务器档案,schema 见 config/mcp_servers.json)
112
+ MCP_SERVERS_CONFIG_PATH: str = Field(
113
+ default="./config/mcp_servers.json", description="MCP 服务器档案文件路径"
114
+ )
115
+ MCP_CALL_TIMEOUT: int = Field(
116
+ default=60,
117
+ ge=1,
118
+ le=600,
119
+ description="MCP 调用默认超时(秒),档案未指定 timeout 时使用",
120
+ )
121
+
122
+ # MinerU 精准解析 API 配置(https://mineru.net/apiManage/docs)
123
+ MINERU_API_KEY: str | None = Field(
124
+ default=None, description="MinerU API Key(API 管理页面自建)"
125
+ )
126
+ MINERU_BASE_API_URL: str = Field(
127
+ default="https://mineru.net", description="MinerU API 基础地址"
128
+ )
129
+ MINERU_TIMEOUT: int = Field(
130
+ default=60, ge=1, le=600, description="单次 HTTP 请求超时(秒)"
131
+ )
132
+ MINERU_POLL_INTERVAL: float = Field(
133
+ default=2.0, ge=0.5, description="任务轮询间隔(秒)"
134
+ )
135
+ MINERU_MAX_WAIT: int = Field(
136
+ default=600, ge=10, description="任务轮询总超时(秒),超时抛 MinerUError"
137
+ )
138
+
139
+ class Config:
140
+ env_file = ".env"
141
+ env_file_encoding = "utf-8"
142
+ extra = "ignore"
143
+
144
+
145
+ @lru_cache()
146
+ def get_settings() -> Settings:
147
+ return Settings()
@@ -0,0 +1,134 @@
1
+ """
2
+ Copyright (c) 2021-2026 Clark Chang. All Rights Reserved.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+
16
+ Project: sdpy
17
+ Author: Clark Chang
18
+ """
19
+
20
+ # date: 2025-04-19
21
+ # source: 迁移自 config/logger_config.py(kit 自包含日志,修改须与源文件评估双向同步)
22
+ """Kit 统一日志配置模块"""
23
+
24
+ import logging
25
+ import os
26
+ import sys
27
+ from logging.handlers import RotatingFileHandler
28
+
29
+ from kit.config.kit_config import get_settings
30
+
31
+ settings = get_settings()
32
+
33
+ DEFAULT_LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
34
+ DEFAULT_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
35
+
36
+ LOG_LEVEL_MAPPING = {
37
+ "DEBUG": logging.DEBUG,
38
+ "INFO": logging.INFO,
39
+ "WARNING": logging.WARNING,
40
+ "WARN": logging.WARNING,
41
+ "ERROR": logging.ERROR,
42
+ "CRITICAL": logging.CRITICAL,
43
+ "FATAL": logging.CRITICAL,
44
+ }
45
+
46
+
47
+ def ensure_log_dir(log_dir: str) -> None:
48
+ """确保日志目录存在"""
49
+ os.makedirs(log_dir, exist_ok=True)
50
+
51
+
52
+ def setup_logger(
53
+ name: str | None = None,
54
+ log_level: str | None = None,
55
+ log_file: str | None = None,
56
+ log_format: str = DEFAULT_LOG_FORMAT,
57
+ date_format: str = DEFAULT_DATE_FORMAT,
58
+ max_bytes: int = 5 * 1024 * 1024,
59
+ backup_count: int = 5,
60
+ ) -> logging.Logger:
61
+ """设置全局统一的日志配置"""
62
+ logger = logging.getLogger(name)
63
+
64
+ level_str = log_level or settings.LOG_LEVEL
65
+ logger.setLevel(LOG_LEVEL_MAPPING.get(level_str.upper(), logging.INFO))
66
+
67
+ if logger.handlers:
68
+ return logger
69
+
70
+ formatter = logging.Formatter(log_format, datefmt=date_format)
71
+
72
+ if settings.LOG_TO_CONSOLE:
73
+ console_handler = logging.StreamHandler(sys.stdout)
74
+ console_handler.setFormatter(formatter)
75
+ logger.addHandler(console_handler)
76
+
77
+ if settings.LOG_TO_FILE:
78
+ ensure_log_dir(settings.LOG_DIR)
79
+ log_file_path = log_file or os.path.join(settings.LOG_DIR, settings.LOG_FILE)
80
+ file_handler = RotatingFileHandler(
81
+ log_file_path,
82
+ maxBytes=max_bytes,
83
+ backupCount=backup_count,
84
+ encoding="utf-8",
85
+ )
86
+ file_handler.setFormatter(formatter)
87
+ logger.addHandler(file_handler)
88
+
89
+ return logger
90
+
91
+
92
+ def get_default_logger(
93
+ name: str | None = None,
94
+ log_level: str | None = None,
95
+ log_file: str | None = None,
96
+ ) -> logging.Logger:
97
+ """获取默认配置的日志器"""
98
+ return setup_logger(name, log_level, log_file)
99
+
100
+
101
+ def debug(logger: logging.Logger, msg: str, *args, **kwargs) -> None:
102
+ """记录调试信息"""
103
+ logger.debug(msg, *args, **kwargs)
104
+
105
+
106
+ def info(logger: logging.Logger, msg: str, *args, **kwargs) -> None:
107
+ """记录一般信息"""
108
+ logger.info(msg, *args, **kwargs)
109
+
110
+
111
+ def warning(logger: logging.Logger, msg: str, *args, **kwargs) -> None:
112
+ """记录警告信息"""
113
+ logger.warning(msg, *args, **kwargs)
114
+
115
+
116
+ def error(logger: logging.Logger, msg: str, *args, **kwargs) -> None:
117
+ """记录错误信息"""
118
+ logger.error(msg, *args, **kwargs)
119
+
120
+
121
+ def critical(logger: logging.Logger, msg: str, *args, **kwargs) -> None:
122
+ """记录严重错误信息"""
123
+ logger.critical(msg, *args, **kwargs)
124
+
125
+
126
+ __all__ = [
127
+ "setup_logger",
128
+ "get_default_logger",
129
+ "debug",
130
+ "info",
131
+ "warning",
132
+ "error",
133
+ "critical",
134
+ ]
kit/db/__init__.py ADDED
@@ -0,0 +1,70 @@
1
+ """
2
+ Copyright (c) 2021-2026 Clark Chang. All Rights Reserved.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+
16
+ Project: sdpy
17
+ Author: Clark Chang
18
+ """
19
+
20
+ # date: 2026-08-23
21
+ """kit.db — 基于 SQLModel 的统一数据库访问层
22
+
23
+ 支持 PostgreSQL、MySQL、SQLite 三种数据库,提供:
24
+ - 线程安全的引擎管理(engine.py)
25
+ - 统一会话入口 session_scope(session.py)
26
+ - 不提交事务的泛型 CRUDBase(crud.py)
27
+ - @transactional 装饰器(transaction.py)
28
+ - 统一异常体系 DBError(exceptions.py)
29
+ - SQLModel 混入类(models/base.py)
30
+ """
31
+
32
+ from kit.db.crud import CRUDBase
33
+ from kit.db.engine import (
34
+ MYSQL,
35
+ POSTGRESQL,
36
+ SQLITE,
37
+ close_all_engines,
38
+ close_engine,
39
+ create_tables,
40
+ get_engine,
41
+ list_tables,
42
+ table_exists,
43
+ )
44
+ from kit.db.exceptions import DBError, NotFoundError
45
+ from kit.db.session import get_session, session_scope
46
+ from kit.db.transaction import transactional
47
+
48
+ __all__ = [
49
+ # 引擎管理
50
+ "get_engine",
51
+ "create_tables",
52
+ "list_tables",
53
+ "table_exists",
54
+ "close_all_engines",
55
+ "close_engine",
56
+ # 常量
57
+ "POSTGRESQL",
58
+ "MYSQL",
59
+ "SQLITE",
60
+ # 会话
61
+ "session_scope",
62
+ "get_session",
63
+ # 异常
64
+ "DBError",
65
+ "NotFoundError",
66
+ # CRUD
67
+ "CRUDBase",
68
+ # 事务
69
+ "transactional",
70
+ ]