springbootAI 1.8.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.
- spring/__init__.py +66 -0
- spring/ai/__init__.py +78 -0
- spring/ai/advisors.py +139 -0
- spring/ai/annotations.py +74 -0
- spring/ai/autoconfig.py +481 -0
- spring/ai/core.py +391 -0
- spring/ai/etl.py +188 -0
- spring/ai/memory.py +109 -0
- spring/ai/observability.py +129 -0
- spring/ai/providers.py +789 -0
- spring/ai/resilience.py +258 -0
- spring/ai/tools.py +106 -0
- spring/ai/vectorstore.py +303 -0
- spring/annotations/__init__.py +188 -0
- spring/annotations/cache.py +126 -0
- spring/annotations/cloud.py +207 -0
- spring/annotations/conditional.py +272 -0
- spring/annotations/core.py +864 -0
- spring/annotations/messaging.py +107 -0
- spring/aop/__init__.py +4 -0
- spring/aop/cloud_aop.py +404 -0
- spring/aop/comprehensive_aop.py +1015 -0
- spring/aop/method_interceptor.py +19 -0
- spring/aop/proxy_factory.py +55 -0
- spring/cloud/__init__.py +76 -0
- spring/cloud/discovery.py +364 -0
- spring/cloud/feign.py +469 -0
- spring/cloud/gateway.py +452 -0
- spring/cloud/load_balancer.py +149 -0
- spring/cloud/seata.py +557 -0
- spring/cloud/sentinel.py +525 -0
- spring/cloud/tracer.py +337 -0
- spring/config/__init__.py +21 -0
- spring/config/binding.py +206 -0
- spring/config/config_loader.py +405 -0
- spring/context/__init__.py +13 -0
- spring/context/application_context.py +589 -0
- spring/context/bean_definition.py +70 -0
- spring/context/bean_factory.py +1052 -0
- spring/context/registry.py +58 -0
- spring/context/scanner.py +106 -0
- spring/core/__init__.py +3 -0
- spring/core/graceful_shutdown.py +196 -0
- spring/core/typing_utils.py +50 -0
- spring/csv/__init__.py +52 -0
- spring/csv/annotations.py +402 -0
- spring/csv/converters.py +69 -0
- spring/csv/easy_csv.py +95 -0
- spring/csv/exceptions.py +27 -0
- spring/csv/reader.py +195 -0
- spring/csv/writer.py +155 -0
- spring/data/__init__.py +54 -0
- spring/data/page.py +181 -0
- spring/data/repository.py +274 -0
- spring/data/specification.py +228 -0
- spring/datasource/__init__.py +66 -0
- spring/datasource/annotations.py +133 -0
- spring/datasource/context.py +69 -0
- spring/datasource/dynamic.py +148 -0
- spring/event/__init__.py +7 -0
- spring/event/publisher.py +69 -0
- spring/excel/__init__.py +51 -0
- spring/excel/annotations.py +405 -0
- spring/excel/converters.py +231 -0
- spring/excel/easy_excel.py +94 -0
- spring/excel/exceptions.py +31 -0
- spring/excel/reader.py +254 -0
- spring/excel/style.py +95 -0
- spring/excel/writer.py +197 -0
- spring/i18n/__init__.py +97 -0
- spring/i18n/accessor.py +94 -0
- spring/i18n/auto_config.py +177 -0
- spring/i18n/holder.py +106 -0
- spring/i18n/locale.py +152 -0
- spring/i18n/locale_resolver.py +367 -0
- spring/i18n/message_source.py +250 -0
- spring/i18n/middleware.py +79 -0
- spring/i18n/properties.py +168 -0
- spring/i18n/sources.py +255 -0
- spring/logging/__init__.py +1 -0
- spring/logging/loguru_logger.py +228 -0
- spring/main.py +378 -0
- spring/messaging/__init__.py +1 -0
- spring/messaging/rabbitmq.py +302 -0
- spring/monitoring/__init__.py +1 -0
- spring/monitoring/prometheus.py +199 -0
- spring/orm/__init__.py +258 -0
- spring/orm/database.py +222 -0
- spring/orm/ddl_auto.py +1217 -0
- spring/orm/migration.py +419 -0
- spring/orm/mybatis_integration.py +400 -0
- spring/orm/pymybatis/__init__.py +86 -0
- spring/orm/pymybatis/annotations/__init__.py +30 -0
- spring/orm/pymybatis/annotations/annotations.py +332 -0
- spring/orm/pymybatis/cache/__init__.py +47 -0
- spring/orm/pymybatis/cache/cache.py +371 -0
- spring/orm/pymybatis/cache/redis_cache.py +434 -0
- spring/orm/pymybatis/circuit_breaker/__init__.py +21 -0
- spring/orm/pymybatis/circuit_breaker/circuit_breaker.py +424 -0
- spring/orm/pymybatis/configuration.py +525 -0
- spring/orm/pymybatis/core/__init__.py +10 -0
- spring/orm/pymybatis/core/sql_session.py +1382 -0
- spring/orm/pymybatis/core/sql_session_factory.py +76 -0
- spring/orm/pymybatis/dialect/__init__.py +9 -0
- spring/orm/pymybatis/dialect/dialect.py +445 -0
- spring/orm/pymybatis/dynamic_sql/__init__.py +9 -0
- spring/orm/pymybatis/dynamic_sql/dynamic_sql.py +900 -0
- spring/orm/pymybatis/interceptor/__init__.py +31 -0
- spring/orm/pymybatis/interceptor/interceptor.py +427 -0
- spring/orm/pymybatis/mapper/__init__.py +9 -0
- spring/orm/pymybatis/mapper/mapper.py +540 -0
- spring/orm/pymybatis/metrics/__init__.py +41 -0
- spring/orm/pymybatis/metrics/metrics.py +595 -0
- spring/orm/pymybatis/pool/__init__.py +9 -0
- spring/orm/pymybatis/pool/connection_pool.py +711 -0
- spring/orm/pymybatis/security/__init__.py +19 -0
- spring/orm/pymybatis/security/access_control.py +415 -0
- spring/orm/pymybatis/security/password_encoder.py +293 -0
- spring/orm/pymybatis/security/sensitive_data_masker.py +326 -0
- spring/orm/pymybatis/security/sql_injection_detector.py +675 -0
- spring/orm/pymybatis/transaction/__init__.py +9 -0
- spring/orm/pymybatis/transaction/transaction.py +288 -0
- spring/orm/pymybatis/type_handler/__init__.py +37 -0
- spring/orm/pymybatis/type_handler/type_handler.py +473 -0
- spring/orm/pymybatis/version.py +9 -0
- spring/orm/pymybatis/xml_parser/__init__.py +9 -0
- spring/orm/pymybatis/xml_parser/xml_parser.py +761 -0
- spring/retry/__init__.py +12 -0
- spring/retry/retry_annotations.py +71 -0
- spring/retry/retry_decorator.py +155 -0
- spring/scheduling/__init__.py +3 -0
- spring/scheduling/scheduler.py +389 -0
- spring/security/__init__.py +39 -0
- spring/security/jwt_utils.py +281 -0
- spring/security/replay_protection.py +206 -0
- spring/security/secret_manager.py +226 -0
- spring/security/security_aop.py +248 -0
- spring/security/security_context.py +172 -0
- spring/test/__init__.py +45 -0
- spring/test/slicing.py +341 -0
- spring/tracing/__init__.py +11 -0
- spring/tracing/skywalking.py +229 -0
- spring/tx/__init__.py +52 -0
- spring/tx/events.py +172 -0
- spring/tx/synchronization.py +143 -0
- spring/utils/__init__.py +5 -0
- spring/utils/banner.py +32 -0
- spring/utils/logger.py +73 -0
- spring/utils/redis_client.py +526 -0
- spring/validation/__init__.py +55 -0
- spring/validation/aop.py +141 -0
- spring/validation/constraints.py +357 -0
- spring/validation/exceptions.py +55 -0
- spring/validation/validator.py +139 -0
- spring/web/__init__.py +12 -0
- spring/web/actuator.py +319 -0
- spring/web/exception_handler.py +61 -0
- spring/web/health.py +399 -0
- spring/web/interceptor.py +91 -0
- spring/web/result.py +44 -0
- spring/web/swagger.py +601 -0
- spring/web/web_context.py +755 -0
- spring/websocket/__init__.py +86 -0
- spring/websocket/annotations.py +169 -0
- spring/websocket/broker.py +238 -0
- spring/websocket/exceptions.py +26 -0
- spring/websocket/handler.py +243 -0
- spring/websocket/router.py +526 -0
- spring/websocket/session.py +216 -0
- springbootai-1.8.0.dist-info/METADATA +2796 -0
- springbootai-1.8.0.dist-info/RECORD +175 -0
- springbootai-1.8.0.dist-info/WHEEL +5 -0
- springbootai-1.8.0.dist-info/entry_points.txt +2 -0
- springbootai-1.8.0.dist-info/licenses/LICENSE +7 -0
- springbootai-1.8.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
"""SpringBootAI CSV 注解 —— 字段/类级映射元数据。
|
|
2
|
+
|
|
3
|
+
设计原则:**复用项目既有范式,不重复造轮子**。本模块的字段级注解完全镜像 Excel 模块
|
|
4
|
+
``spring/excel/annotations.py`` 的 ``@ExcelProperty`` / ``@ExcelIgnore`` / ``@excel_sheet``
|
|
5
|
+
元数据描述符范式(该范式又源自 ORM ``Column``/``@entity``):
|
|
6
|
+
|
|
7
|
+
- 字段级:``CsvProperty`` / ``CsvIgnore`` 作为类属性标记(描述符实例)或函数装饰器,
|
|
8
|
+
元数据通过 ``cls.__dict__`` + MRO 反射读取(与 ``Column``/``__column__`` 一致)。
|
|
9
|
+
- 类级:``@csv_file`` 装饰器在类上设置 ``__csv_file__``(与 ``@entity`` 设置
|
|
10
|
+
``__entity__``/``__table__``、``@excel_sheet`` 设置 ``__excel_sheet__`` 一致)。
|
|
11
|
+
|
|
12
|
+
注解本身不依赖任何第三方库(CSV 使用 Python 标准库 ``csv``)。
|
|
13
|
+
|
|
14
|
+
对齐常见 CSV 注解库(如 Python ``csv`` + 注解映射、Java ``commons-csv`` + 注解)的核心注解:
|
|
15
|
+
``@CsvProperty`` / ``@CsvIgnore`` / ``@CsvFile``。
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from typing import Any, Callable, List, Optional, Type, Union
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ==================== 字段级注解 ====================
|
|
23
|
+
|
|
24
|
+
class CsvProperty:
|
|
25
|
+
"""字段级注解:声明实体字段与 CSV 列的映射关系(镜像 ORM ``Column`` / Excel ``ExcelProperty``)。
|
|
26
|
+
|
|
27
|
+
两种使用方式(与 ``Column`` / ``ExcelProperty`` 一致):
|
|
28
|
+
|
|
29
|
+
1. 类属性描述符(推荐)::
|
|
30
|
+
|
|
31
|
+
@csv_file("用户列表")
|
|
32
|
+
class DemoData:
|
|
33
|
+
id = CsvProperty("ID", order=1)
|
|
34
|
+
name = CsvProperty("姓名", order=2)
|
|
35
|
+
age = CsvProperty("年龄", order=3)
|
|
36
|
+
|
|
37
|
+
def __init__(self, id=None, name=None, age=None):
|
|
38
|
+
self.id = id; self.name = name; self.age = age
|
|
39
|
+
|
|
40
|
+
2. 函数装饰器(镜像 ``@column`` / ``@ExcelProperty``)::
|
|
41
|
+
|
|
42
|
+
@CsvProperty("姓名", order=2)
|
|
43
|
+
def name(self): ...
|
|
44
|
+
|
|
45
|
+
属性说明(对齐 CSV 注解映射 + 复用 Excel 语义):
|
|
46
|
+
value: 列标题(表头文案)。为空时用字段名转标题。
|
|
47
|
+
order: 列顺序,越小越靠前;同 order 按 MRO 声明顺序。默认 0。
|
|
48
|
+
index: 绝对列索引(从 0 起),设置后覆盖 order。默认 None。
|
|
49
|
+
converter: 自定义转换器(``Converter`` 子类或实例)。默认 None(按类型自动选)。
|
|
50
|
+
format: 通用格式占位(同时作 date_format 默认)。
|
|
51
|
+
date_format: 日期格式串,如 ``%Y-%m-%d``。读时按此解析,写时按此格式化。
|
|
52
|
+
big_number: 是否按字符串写入(CSV 本身即字符串,此标记用于强制把数值原样保留,
|
|
53
|
+
避免 long ID 被解析回 int 后再写时丢精度)。默认 False。
|
|
54
|
+
ignore: 内部等价 @CsvIgnore 的快捷开关。默认 False。
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
value: str = "",
|
|
60
|
+
order: int = 0,
|
|
61
|
+
index: Optional[int] = None,
|
|
62
|
+
converter: Optional[Union[Type, Any]] = None,
|
|
63
|
+
format: Optional[str] = None,
|
|
64
|
+
date_format: Optional[str] = None,
|
|
65
|
+
big_number: bool = False,
|
|
66
|
+
ignore: bool = False,
|
|
67
|
+
):
|
|
68
|
+
self.value = value
|
|
69
|
+
self.order = order
|
|
70
|
+
self.index = index
|
|
71
|
+
self.converter = converter
|
|
72
|
+
self.format = format
|
|
73
|
+
self.date_format = date_format or format
|
|
74
|
+
self.big_number = big_number
|
|
75
|
+
self.ignore = ignore
|
|
76
|
+
# 反射时回填
|
|
77
|
+
self.attr_name: str = ""
|
|
78
|
+
|
|
79
|
+
def __set_name__(self, owner: type, name: str) -> None:
|
|
80
|
+
"""类属性描述符形式时,Python 自动回填字段名(镜像 ``ExcelProperty``)。"""
|
|
81
|
+
self.attr_name = name
|
|
82
|
+
|
|
83
|
+
def __call__(self, target: Callable) -> Callable:
|
|
84
|
+
"""函数装饰器形式:``@CsvProperty(...)``,把元数据挂到 ``__csv_property__``。
|
|
85
|
+
|
|
86
|
+
镜像 ORM ``column()`` 的 ``setattr(f, '__column__', col)`` 与
|
|
87
|
+
``ExcelProperty.__call__`` 的 ``setattr(target, '__excel_property__', self)``。
|
|
88
|
+
"""
|
|
89
|
+
setattr(target, "__csv_property__", self)
|
|
90
|
+
self.attr_name = getattr(target, "__name__", "")
|
|
91
|
+
return target
|
|
92
|
+
|
|
93
|
+
def resolve_header(self, attr_name: str) -> str:
|
|
94
|
+
"""计算最终表头文案。"""
|
|
95
|
+
return self.value or _field_to_header(attr_name)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class CsvIgnore:
|
|
99
|
+
"""字段级注解:标记字段在读写时跳过(镜像 ORM 中跳过未标注字段、Excel ``ExcelIgnore``)。
|
|
100
|
+
|
|
101
|
+
用法与 ``CsvProperty`` 一致,支持类属性描述符与函数装饰器两种形式::
|
|
102
|
+
|
|
103
|
+
remark = CsvIgnore()
|
|
104
|
+
# 或
|
|
105
|
+
@CsvIgnore()
|
|
106
|
+
def remark(self): ...
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
def __init__(self):
|
|
110
|
+
self.attr_name: str = ""
|
|
111
|
+
|
|
112
|
+
def __set_name__(self, owner: type, name: str) -> None:
|
|
113
|
+
self.attr_name = name
|
|
114
|
+
|
|
115
|
+
def __call__(self, target: Callable) -> Callable:
|
|
116
|
+
setattr(target, "__csv_ignore__", True)
|
|
117
|
+
self.attr_name = getattr(target, "__name__", "")
|
|
118
|
+
return target
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# ==================== 类级注解 ====================
|
|
122
|
+
|
|
123
|
+
class CsvFile:
|
|
124
|
+
"""类级注解元数据:CSV 文件配置(镜像 ORM ``Table`` / Excel ``ExcelSheet``)。
|
|
125
|
+
|
|
126
|
+
属性说明:
|
|
127
|
+
file_name: 文件名(仅元数据,读写时由调用方传路径)。
|
|
128
|
+
has_header: 是否包含表头行。默认 True(读时第一行作表头,写时先写表头)。
|
|
129
|
+
delimiter: 字段分隔符。默认 ``,``。
|
|
130
|
+
encoding: 文件编码。默认 ``utf-8-sig``(带 BOM,兼容 Excel 打开中文 CSV)。
|
|
131
|
+
quote_char: 引用字符。默认 ``"``。
|
|
132
|
+
line_terminator: 行终止符。默认 ``\\r\\n``(CSV 标准,复用 Excel 兼容)。
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
def __init__(
|
|
136
|
+
self,
|
|
137
|
+
file_name: str = "",
|
|
138
|
+
has_header: bool = True,
|
|
139
|
+
delimiter: str = ",",
|
|
140
|
+
encoding: str = "utf-8-sig",
|
|
141
|
+
quote_char: str = '"',
|
|
142
|
+
line_terminator: str = "\r\n",
|
|
143
|
+
):
|
|
144
|
+
self.file_name = file_name
|
|
145
|
+
self.has_header = has_header
|
|
146
|
+
self.delimiter = delimiter
|
|
147
|
+
self.encoding = encoding
|
|
148
|
+
self.quote_char = quote_char
|
|
149
|
+
self.line_terminator = line_terminator
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def csv_file(
|
|
153
|
+
file_name: str = "",
|
|
154
|
+
has_header: bool = True,
|
|
155
|
+
delimiter: str = ",",
|
|
156
|
+
encoding: str = "utf-8-sig",
|
|
157
|
+
quote_char: str = '"',
|
|
158
|
+
line_terminator: str = "\r\n",
|
|
159
|
+
) -> Callable[[type], type]:
|
|
160
|
+
"""类级装饰器:标注实体类对应的 CSV 文件配置(镜像 ORM ``@entity`` / Excel ``@excel_sheet``)。
|
|
161
|
+
|
|
162
|
+
用法::
|
|
163
|
+
|
|
164
|
+
@csv_file("用户列表", delimiter=",", encoding="utf-8-sig")
|
|
165
|
+
class DemoData:
|
|
166
|
+
id = CsvProperty("ID", order=1)
|
|
167
|
+
...
|
|
168
|
+
|
|
169
|
+
不使用本装饰器时,读写引擎使用默认配置(has_header=True,delimiter=",",utf-8-sig)。
|
|
170
|
+
"""
|
|
171
|
+
meta = CsvFile(
|
|
172
|
+
file_name=file_name,
|
|
173
|
+
has_header=has_header,
|
|
174
|
+
delimiter=delimiter,
|
|
175
|
+
encoding=encoding,
|
|
176
|
+
quote_char=quote_char,
|
|
177
|
+
line_terminator=line_terminator,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
def decorator(cls: type) -> type:
|
|
181
|
+
setattr(cls, "__csv_file__", meta)
|
|
182
|
+
return cls
|
|
183
|
+
|
|
184
|
+
return decorator
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# ==================== 元数据解析(复用 ORM/Excel 反射范式) ====================
|
|
188
|
+
|
|
189
|
+
class CsvColumnModel:
|
|
190
|
+
"""解析后的列模型,供 reader/writer 统一消费(镜像 ``ExcelColumnModel``)。"""
|
|
191
|
+
|
|
192
|
+
__slots__ = (
|
|
193
|
+
"attr_name", "header", "order", "index", "converter", "date_format",
|
|
194
|
+
"big_number", "py_type",
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def __init__(
|
|
198
|
+
self,
|
|
199
|
+
attr_name: str,
|
|
200
|
+
header: str,
|
|
201
|
+
order: int,
|
|
202
|
+
index: Optional[int],
|
|
203
|
+
converter: Optional[Union[Type, Any]],
|
|
204
|
+
date_format: Optional[str],
|
|
205
|
+
big_number: bool,
|
|
206
|
+
py_type: Any,
|
|
207
|
+
):
|
|
208
|
+
self.attr_name = attr_name
|
|
209
|
+
self.header = header
|
|
210
|
+
self.order = order
|
|
211
|
+
self.index = index
|
|
212
|
+
self.converter = converter
|
|
213
|
+
self.date_format = date_format
|
|
214
|
+
self.big_number = big_number
|
|
215
|
+
self.py_type = py_type
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def sort_key(self):
|
|
219
|
+
"""排序键:index 优先(None 视作大值靠后),其次 order,最后 attr_name 稳定。"""
|
|
220
|
+
return (self.index if self.index is not None else float("inf"),
|
|
221
|
+
self.order, self.attr_name)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _field_to_header(name: str) -> str:
|
|
225
|
+
"""字段名转表头:snake_case/camelCase -> 友好标题(镜像 Excel ``_field_to_header``)。
|
|
226
|
+
|
|
227
|
+
例:``user_name`` -> ``User Name``;``userName`` -> ``User Name``;``id`` -> ``Id``。
|
|
228
|
+
"""
|
|
229
|
+
import re
|
|
230
|
+
s1 = re.sub(r"(.)([A-Z][a-z]+)", r"\1 \2", name)
|
|
231
|
+
s2 = re.sub(r"([a-z0-9])([A-Z])", r"\1 \2", s1)
|
|
232
|
+
return " ".join(part.capitalize() for part in s2.replace("_", " ").split())
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _get_class_file_meta(cls: type) -> CsvFile:
|
|
236
|
+
"""读取类上的 ``__csv_file__``,缺失则返回默认。镜像 ORM ``_parse_entity`` 读 ``__table__``。"""
|
|
237
|
+
meta = getattr(cls, "__csv_file__", None)
|
|
238
|
+
if isinstance(meta, CsvFile):
|
|
239
|
+
return meta
|
|
240
|
+
return CsvFile()
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _resolve_init_hints(cls: type) -> dict:
|
|
244
|
+
"""获取 ``__init__`` 参数的类型注解(用于无类属性注解时的类型推断)。
|
|
245
|
+
|
|
246
|
+
返回的承载类型已解包 ``Optional[X]``:Python 3.10 的 ``get_type_hints`` 会把带
|
|
247
|
+
``None`` 默认值的参数注解自动包装为 ``Optional[X]``,3.11+ 不再包装。统一解包
|
|
248
|
+
为承载类型,使转换器选择/类型推断与 Python 版本无关(可空性不由类型承载)。
|
|
249
|
+
"""
|
|
250
|
+
import inspect
|
|
251
|
+
try:
|
|
252
|
+
from typing import get_type_hints
|
|
253
|
+
from spring.core.typing_utils import unwrap_optional_type
|
|
254
|
+
hints = get_type_hints(cls.__init__)
|
|
255
|
+
return {k: unwrap_optional_type(v) for k, v in hints.items()}
|
|
256
|
+
except Exception:
|
|
257
|
+
try:
|
|
258
|
+
return dict(inspect.signature(cls).parameters)
|
|
259
|
+
except Exception:
|
|
260
|
+
return {}
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _get_init_param_names(cls: type) -> List[str]:
|
|
264
|
+
"""提取 ``__init__`` 中 ``self.xxx`` 以外的参数名,作为字段名回退顺序。"""
|
|
265
|
+
import inspect
|
|
266
|
+
try:
|
|
267
|
+
sig = inspect.signature(cls.__init__)
|
|
268
|
+
except (TypeError, ValueError):
|
|
269
|
+
return []
|
|
270
|
+
names = []
|
|
271
|
+
for pname, param in list(sig.parameters.items())[1:]: # 跳过 self
|
|
272
|
+
if param.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD):
|
|
273
|
+
continue
|
|
274
|
+
names.append(pname)
|
|
275
|
+
return names
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def parse_csv_columns(cls: type) -> List[CsvColumnModel]:
|
|
279
|
+
"""解析实体类的 CSV 列模型(镜像 Excel ``parse_excel_columns``)。
|
|
280
|
+
|
|
281
|
+
解析顺序(镜像 ORM ``_parse_entity`` 对 ``Column`` 的处理):
|
|
282
|
+
1. 遍历 ``cls.__mro__`` 的 ``__dict__``,收集 ``CsvProperty`` 实例或带
|
|
283
|
+
``__csv_property__`` 的成员;遇到 ``CsvIgnore`` / ``__csv_ignore__`` 则跳过。
|
|
284
|
+
2. 若类上没有任何 ``CsvProperty`` 标记,回退到 ``__init__`` 参数列表,按字段名自动
|
|
285
|
+
生成表头(让未改造的纯 ``__init__`` 模型也能导入导出)。
|
|
286
|
+
3. 按 ``index`` -> ``order`` -> 声明顺序排序。
|
|
287
|
+
"""
|
|
288
|
+
from .exceptions import CsvPropertyError
|
|
289
|
+
|
|
290
|
+
seen: dict = {} # attr_name -> CsvProperty
|
|
291
|
+
ignored: set = set()
|
|
292
|
+
declaration_order: dict = {}
|
|
293
|
+
counter = 0
|
|
294
|
+
|
|
295
|
+
for base in reversed(cls.__mro__): # 自底向上,子类覆盖父类
|
|
296
|
+
for attr_name, value in vars(base).items():
|
|
297
|
+
if attr_name.startswith("__"):
|
|
298
|
+
continue
|
|
299
|
+
prop = None
|
|
300
|
+
if isinstance(value, CsvProperty):
|
|
301
|
+
prop = value
|
|
302
|
+
if not prop.attr_name:
|
|
303
|
+
prop.attr_name = attr_name
|
|
304
|
+
elif hasattr(value, "__csv_property__"):
|
|
305
|
+
prop = getattr(value, "__csv_property__")
|
|
306
|
+
if not isinstance(prop, CsvProperty):
|
|
307
|
+
continue
|
|
308
|
+
if not prop.attr_name:
|
|
309
|
+
prop.attr_name = attr_name
|
|
310
|
+
else:
|
|
311
|
+
if isinstance(value, CsvIgnore):
|
|
312
|
+
ignored.add(attr_name)
|
|
313
|
+
continue
|
|
314
|
+
if getattr(value, "__csv_ignore__", False) is True:
|
|
315
|
+
ignored.add(attr_name)
|
|
316
|
+
continue
|
|
317
|
+
continue
|
|
318
|
+
if attr_name in ignored:
|
|
319
|
+
continue
|
|
320
|
+
if prop.ignore:
|
|
321
|
+
ignored.add(attr_name)
|
|
322
|
+
continue
|
|
323
|
+
if attr_name not in declaration_order:
|
|
324
|
+
declaration_order[attr_name] = counter
|
|
325
|
+
counter += 1
|
|
326
|
+
# 子类覆盖父类
|
|
327
|
+
seen[attr_name] = (prop, declaration_order[attr_name])
|
|
328
|
+
|
|
329
|
+
init_hints = _resolve_init_hints(cls)
|
|
330
|
+
|
|
331
|
+
columns = []
|
|
332
|
+
|
|
333
|
+
if seen:
|
|
334
|
+
# 有显式 CsvProperty 标记
|
|
335
|
+
for attr_name, (prop, decl_order) in seen.items():
|
|
336
|
+
columns.append(_to_column_model(
|
|
337
|
+
attr_name=attr_name,
|
|
338
|
+
prop=prop,
|
|
339
|
+
decl_order=decl_order,
|
|
340
|
+
py_type=init_hints.get(attr_name, None),
|
|
341
|
+
))
|
|
342
|
+
# 排序:index 优先,其次 order,最后声明顺序
|
|
343
|
+
columns.sort(key=lambda c: (
|
|
344
|
+
c.index if c.index is not None else float("inf"),
|
|
345
|
+
c.order,
|
|
346
|
+
declaration_order.get(c.attr_name, 0),
|
|
347
|
+
))
|
|
348
|
+
else:
|
|
349
|
+
# 回退:没有任何 CsvProperty 标记 -> 用 __init__ 参数自动建列
|
|
350
|
+
for pname in _get_init_param_names(cls):
|
|
351
|
+
if pname in ignored:
|
|
352
|
+
continue
|
|
353
|
+
columns.append(_to_column_model(
|
|
354
|
+
attr_name=pname,
|
|
355
|
+
prop=CsvProperty(), # 默认元数据
|
|
356
|
+
decl_order=0,
|
|
357
|
+
py_type=init_hints.get(pname, None),
|
|
358
|
+
))
|
|
359
|
+
|
|
360
|
+
# 全部被 @CsvIgnore 或无可导出字段时,统一抛错
|
|
361
|
+
if not columns:
|
|
362
|
+
raise CsvPropertyError(
|
|
363
|
+
f"类 {cls.__name__} 没有可导出字段(全部被 @CsvIgnore,或无 __init__ 字段?)"
|
|
364
|
+
)
|
|
365
|
+
return columns
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _to_column_model(attr_name: str, prop: CsvProperty, decl_order: int, py_type: Any) -> CsvColumnModel:
|
|
369
|
+
"""把 CsvProperty + 类型注解组装为 CsvColumnModel。"""
|
|
370
|
+
return CsvColumnModel(
|
|
371
|
+
attr_name=attr_name,
|
|
372
|
+
header=prop.resolve_header(attr_name),
|
|
373
|
+
order=prop.order if prop.order else decl_order,
|
|
374
|
+
index=prop.index,
|
|
375
|
+
converter=prop.converter,
|
|
376
|
+
date_format=prop.date_format,
|
|
377
|
+
big_number=prop.big_number,
|
|
378
|
+
py_type=py_type,
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def has_explicit_properties(cls: type) -> bool:
|
|
383
|
+
"""类上是否声明了至少一个 CsvProperty(用于决定按表头还是按位置匹配)。"""
|
|
384
|
+
for base in cls.__mro__:
|
|
385
|
+
for value in vars(base).values():
|
|
386
|
+
if isinstance(value, CsvProperty):
|
|
387
|
+
return True
|
|
388
|
+
if hasattr(value, "__csv_property__"):
|
|
389
|
+
return True
|
|
390
|
+
return False
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
__all__ = [
|
|
394
|
+
"CsvProperty",
|
|
395
|
+
"CsvIgnore",
|
|
396
|
+
"CsvFile",
|
|
397
|
+
"csv_file",
|
|
398
|
+
"CsvColumnModel",
|
|
399
|
+
"parse_csv_columns",
|
|
400
|
+
"_get_class_file_meta",
|
|
401
|
+
"has_explicit_properties",
|
|
402
|
+
]
|
spring/csv/converters.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""SpringBootAI CSV 转换器 —— 复用 Excel 转换器,避免重复造轮子。
|
|
2
|
+
|
|
3
|
+
设计原则:**复用项目既有范式,不重复造轮子**。CSV 单元格与 Excel 单元格在 Python 值双向
|
|
4
|
+
转换上的语义完全一致(Python 值 ↔ 单元格字符串值),因此本模块直接复用
|
|
5
|
+
``spring.excel.converters`` 的 ``Converter`` 接口与内置转换器(int/float/bool/str/
|
|
6
|
+
datetime/Decimal),仅提供 CSV 友好的别名与解析入口。
|
|
7
|
+
|
|
8
|
+
转换器接口方法名仍为 ``to_excel`` / ``from_excel``(与 Excel 模块共享同一实现,避免分叉),
|
|
9
|
+
本模块在 reader/writer 中以这两个方法驱动转换;如需 CSV 语义别名,可使用下方
|
|
10
|
+
``CsvConverter`` 适配基类。
|
|
11
|
+
|
|
12
|
+
与 Excel 模块的区别:CSV 无单元格样式/数字格式,所有值最终都是字符串;转换器负责把字符串
|
|
13
|
+
解析回 Python 类型(读取)或把 Python 值格式化为字符串(写入)。
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import Any, Optional
|
|
18
|
+
|
|
19
|
+
# 复用 Excel 转换器(spring.excel.converters 不依赖 openpyxl,可安全导入)
|
|
20
|
+
from spring.excel.converters import (
|
|
21
|
+
Converter,
|
|
22
|
+
StringConverter,
|
|
23
|
+
IntegerConverter,
|
|
24
|
+
FloatConverter,
|
|
25
|
+
BooleanConverter,
|
|
26
|
+
DateStringConverter,
|
|
27
|
+
BigDecimalConverter,
|
|
28
|
+
resolve_converter,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CsvConverter(Converter):
|
|
33
|
+
"""CSV 转换器适配基类:提供 ``to_csv`` / ``from_csv`` 语义别名。
|
|
34
|
+
|
|
35
|
+
子类继承 Excel 转换器的 ``to_excel`` / ``from_excel`` 实现,``to_csv`` / ``from_csv``
|
|
36
|
+
直接委托,保持单一实现源(DRY)。用户自定义 CSV 转换器可继承本类,实现任一对方法即可。
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def to_csv(self, value: Any) -> Any:
|
|
40
|
+
return self.to_excel(value)
|
|
41
|
+
|
|
42
|
+
def from_csv(self, cell_value: Any) -> Any:
|
|
43
|
+
return self.from_excel(cell_value)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def resolve_csv_converter(
|
|
47
|
+
py_type: Any,
|
|
48
|
+
declared: Any = None,
|
|
49
|
+
date_format: Optional[str] = None,
|
|
50
|
+
) -> Optional[Converter]:
|
|
51
|
+
"""CSV 转换器解析入口(委托 ``spring.excel.converters.resolve_converter``)。
|
|
52
|
+
|
|
53
|
+
优先级与 Excel 一致:显式声明的 converter > 按类型注解自动选择 > None。
|
|
54
|
+
"""
|
|
55
|
+
return resolve_converter(py_type, declared=declared, date_format=date_format)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"Converter",
|
|
60
|
+
"CsvConverter",
|
|
61
|
+
"StringConverter",
|
|
62
|
+
"IntegerConverter",
|
|
63
|
+
"FloatConverter",
|
|
64
|
+
"BooleanConverter",
|
|
65
|
+
"DateStringConverter",
|
|
66
|
+
"BigDecimalConverter",
|
|
67
|
+
"resolve_converter",
|
|
68
|
+
"resolve_csv_converter",
|
|
69
|
+
]
|
spring/csv/easy_csv.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""SpringBootAI ``EasyCsv`` —— 流式构建入口(对齐 ``EasyExcel`` API)。
|
|
2
|
+
|
|
3
|
+
用法::
|
|
4
|
+
|
|
5
|
+
# 读
|
|
6
|
+
rows = (EasyCsv.read("/tmp/users.csv", head=DemoData)
|
|
7
|
+
.has_header(True)
|
|
8
|
+
.delimiter(",")
|
|
9
|
+
.doRead())
|
|
10
|
+
|
|
11
|
+
# 写
|
|
12
|
+
EasyCsv.write("/tmp/users.csv", head=DemoData).has_header(True).doWrite(data_list)
|
|
13
|
+
|
|
14
|
+
# 一步到位
|
|
15
|
+
rows = read_csv("/tmp/users.csv", DemoData)
|
|
16
|
+
write_csv("/tmp/users.csv", DemoData, rows)
|
|
17
|
+
|
|
18
|
+
CSV 使用 Python 标准库 ``csv``,**无可选依赖**,注解声明与读写均开箱即用。
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import Any, Optional, Type
|
|
23
|
+
|
|
24
|
+
from .reader import CsvReader
|
|
25
|
+
from .writer import CsvWriter
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class EasyCsv:
|
|
29
|
+
"""EasyCsv 流式入口(静态工厂方法风格,对齐 ``EasyExcel``)。"""
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def read(
|
|
33
|
+
source: Any,
|
|
34
|
+
head: Optional[Type] = None,
|
|
35
|
+
has_header: Optional[bool] = None,
|
|
36
|
+
delimiter: Optional[str] = None,
|
|
37
|
+
encoding: Optional[str] = None,
|
|
38
|
+
) -> CsvReader:
|
|
39
|
+
"""构建读取器。
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
source: 文件路径或类文件对象。
|
|
43
|
+
head: 实体类(带 ``@CsvProperty`` 注解)。
|
|
44
|
+
has_header: 是否含表头。默认取类 ``@csv_file`` 配置或 True。
|
|
45
|
+
delimiter: 字段分隔符。默认取类配置或 ``,``。
|
|
46
|
+
encoding: 文件编码。默认取类配置或 ``utf-8-sig``。
|
|
47
|
+
"""
|
|
48
|
+
return CsvReader(
|
|
49
|
+
source=source, head=head, has_header=has_header,
|
|
50
|
+
delimiter=delimiter, encoding=encoding,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def write(
|
|
55
|
+
target: Any,
|
|
56
|
+
head: Optional[Type] = None,
|
|
57
|
+
delimiter: Optional[str] = None,
|
|
58
|
+
encoding: Optional[str] = None,
|
|
59
|
+
) -> CsvWriter:
|
|
60
|
+
"""构建写入器。
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
target: 文件路径或类文件对象。
|
|
64
|
+
head: 实体类(带 ``@CsvProperty`` 注解)。
|
|
65
|
+
delimiter: 字段分隔符。默认取类配置或 ``,``。
|
|
66
|
+
encoding: 文件编码。默认取类配置或 ``utf-8-sig``。
|
|
67
|
+
"""
|
|
68
|
+
return CsvWriter(target=target, head=head, delimiter=delimiter, encoding=encoding)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# 便捷函数(非流式,一步到位)
|
|
72
|
+
def read_csv(
|
|
73
|
+
source: Any,
|
|
74
|
+
head: Type,
|
|
75
|
+
has_header: Optional[bool] = None,
|
|
76
|
+
delimiter: Optional[str] = None,
|
|
77
|
+
encoding: Optional[str] = None,
|
|
78
|
+
) -> list:
|
|
79
|
+
"""一步读取:``read_csv(path, DemoData)``。"""
|
|
80
|
+
return EasyCsv.read(source, head=head, has_header=has_header,
|
|
81
|
+
delimiter=delimiter, encoding=encoding).doRead()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def write_csv(
|
|
85
|
+
target: Any,
|
|
86
|
+
head: Type,
|
|
87
|
+
data: list,
|
|
88
|
+
delimiter: Optional[str] = None,
|
|
89
|
+
encoding: Optional[str] = None,
|
|
90
|
+
) -> Any:
|
|
91
|
+
"""一步写入:``write_csv(path, DemoData, rows)``。"""
|
|
92
|
+
return EasyCsv.write(target, head=head, delimiter=delimiter, encoding=encoding).doWrite(data)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
__all__ = ["EasyCsv", "read_csv", "write_csv"]
|
spring/csv/exceptions.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""SpringBootAI CSV 模块异常定义。
|
|
2
|
+
|
|
3
|
+
设计对齐 Excel 模块(``spring.excel.exceptions``)的错误语义:注解配置错误、读写过程错误
|
|
4
|
+
均通过本模块的异常抛出,便于上层统一捕获。
|
|
5
|
+
|
|
6
|
+
与 Excel 模块的区别:CSV 使用 Python 标准库 ``csv``,**无可选依赖**,因此没有
|
|
7
|
+
``CsvDependencyError``(对应 Excel 的 ``ExcelDependencyError``)。
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CsvError(Exception):
|
|
12
|
+
"""CSV 模块所有异常的基类。"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CsvPropertyError(CsvError):
|
|
16
|
+
"""实体类字段上的 @CsvProperty / @CsvIgnore 配置不合法时抛出。"""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CsvReadError(CsvError):
|
|
20
|
+
"""读取 CSV 过程中发生的错误(表头缺失、行数据无法转换等)。"""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CsvWriteError(CsvError):
|
|
24
|
+
"""写入 CSV 过程中发生的错误。"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
__all__ = ["CsvError", "CsvPropertyError", "CsvReadError", "CsvWriteError"]
|