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.
Files changed (175) hide show
  1. spring/__init__.py +66 -0
  2. spring/ai/__init__.py +78 -0
  3. spring/ai/advisors.py +139 -0
  4. spring/ai/annotations.py +74 -0
  5. spring/ai/autoconfig.py +481 -0
  6. spring/ai/core.py +391 -0
  7. spring/ai/etl.py +188 -0
  8. spring/ai/memory.py +109 -0
  9. spring/ai/observability.py +129 -0
  10. spring/ai/providers.py +789 -0
  11. spring/ai/resilience.py +258 -0
  12. spring/ai/tools.py +106 -0
  13. spring/ai/vectorstore.py +303 -0
  14. spring/annotations/__init__.py +188 -0
  15. spring/annotations/cache.py +126 -0
  16. spring/annotations/cloud.py +207 -0
  17. spring/annotations/conditional.py +272 -0
  18. spring/annotations/core.py +864 -0
  19. spring/annotations/messaging.py +107 -0
  20. spring/aop/__init__.py +4 -0
  21. spring/aop/cloud_aop.py +404 -0
  22. spring/aop/comprehensive_aop.py +1015 -0
  23. spring/aop/method_interceptor.py +19 -0
  24. spring/aop/proxy_factory.py +55 -0
  25. spring/cloud/__init__.py +76 -0
  26. spring/cloud/discovery.py +364 -0
  27. spring/cloud/feign.py +469 -0
  28. spring/cloud/gateway.py +452 -0
  29. spring/cloud/load_balancer.py +149 -0
  30. spring/cloud/seata.py +557 -0
  31. spring/cloud/sentinel.py +525 -0
  32. spring/cloud/tracer.py +337 -0
  33. spring/config/__init__.py +21 -0
  34. spring/config/binding.py +206 -0
  35. spring/config/config_loader.py +405 -0
  36. spring/context/__init__.py +13 -0
  37. spring/context/application_context.py +589 -0
  38. spring/context/bean_definition.py +70 -0
  39. spring/context/bean_factory.py +1052 -0
  40. spring/context/registry.py +58 -0
  41. spring/context/scanner.py +106 -0
  42. spring/core/__init__.py +3 -0
  43. spring/core/graceful_shutdown.py +196 -0
  44. spring/core/typing_utils.py +50 -0
  45. spring/csv/__init__.py +52 -0
  46. spring/csv/annotations.py +402 -0
  47. spring/csv/converters.py +69 -0
  48. spring/csv/easy_csv.py +95 -0
  49. spring/csv/exceptions.py +27 -0
  50. spring/csv/reader.py +195 -0
  51. spring/csv/writer.py +155 -0
  52. spring/data/__init__.py +54 -0
  53. spring/data/page.py +181 -0
  54. spring/data/repository.py +274 -0
  55. spring/data/specification.py +228 -0
  56. spring/datasource/__init__.py +66 -0
  57. spring/datasource/annotations.py +133 -0
  58. spring/datasource/context.py +69 -0
  59. spring/datasource/dynamic.py +148 -0
  60. spring/event/__init__.py +7 -0
  61. spring/event/publisher.py +69 -0
  62. spring/excel/__init__.py +51 -0
  63. spring/excel/annotations.py +405 -0
  64. spring/excel/converters.py +231 -0
  65. spring/excel/easy_excel.py +94 -0
  66. spring/excel/exceptions.py +31 -0
  67. spring/excel/reader.py +254 -0
  68. spring/excel/style.py +95 -0
  69. spring/excel/writer.py +197 -0
  70. spring/i18n/__init__.py +97 -0
  71. spring/i18n/accessor.py +94 -0
  72. spring/i18n/auto_config.py +177 -0
  73. spring/i18n/holder.py +106 -0
  74. spring/i18n/locale.py +152 -0
  75. spring/i18n/locale_resolver.py +367 -0
  76. spring/i18n/message_source.py +250 -0
  77. spring/i18n/middleware.py +79 -0
  78. spring/i18n/properties.py +168 -0
  79. spring/i18n/sources.py +255 -0
  80. spring/logging/__init__.py +1 -0
  81. spring/logging/loguru_logger.py +228 -0
  82. spring/main.py +378 -0
  83. spring/messaging/__init__.py +1 -0
  84. spring/messaging/rabbitmq.py +302 -0
  85. spring/monitoring/__init__.py +1 -0
  86. spring/monitoring/prometheus.py +199 -0
  87. spring/orm/__init__.py +258 -0
  88. spring/orm/database.py +222 -0
  89. spring/orm/ddl_auto.py +1217 -0
  90. spring/orm/migration.py +419 -0
  91. spring/orm/mybatis_integration.py +400 -0
  92. spring/orm/pymybatis/__init__.py +86 -0
  93. spring/orm/pymybatis/annotations/__init__.py +30 -0
  94. spring/orm/pymybatis/annotations/annotations.py +332 -0
  95. spring/orm/pymybatis/cache/__init__.py +47 -0
  96. spring/orm/pymybatis/cache/cache.py +371 -0
  97. spring/orm/pymybatis/cache/redis_cache.py +434 -0
  98. spring/orm/pymybatis/circuit_breaker/__init__.py +21 -0
  99. spring/orm/pymybatis/circuit_breaker/circuit_breaker.py +424 -0
  100. spring/orm/pymybatis/configuration.py +525 -0
  101. spring/orm/pymybatis/core/__init__.py +10 -0
  102. spring/orm/pymybatis/core/sql_session.py +1382 -0
  103. spring/orm/pymybatis/core/sql_session_factory.py +76 -0
  104. spring/orm/pymybatis/dialect/__init__.py +9 -0
  105. spring/orm/pymybatis/dialect/dialect.py +445 -0
  106. spring/orm/pymybatis/dynamic_sql/__init__.py +9 -0
  107. spring/orm/pymybatis/dynamic_sql/dynamic_sql.py +900 -0
  108. spring/orm/pymybatis/interceptor/__init__.py +31 -0
  109. spring/orm/pymybatis/interceptor/interceptor.py +427 -0
  110. spring/orm/pymybatis/mapper/__init__.py +9 -0
  111. spring/orm/pymybatis/mapper/mapper.py +540 -0
  112. spring/orm/pymybatis/metrics/__init__.py +41 -0
  113. spring/orm/pymybatis/metrics/metrics.py +595 -0
  114. spring/orm/pymybatis/pool/__init__.py +9 -0
  115. spring/orm/pymybatis/pool/connection_pool.py +711 -0
  116. spring/orm/pymybatis/security/__init__.py +19 -0
  117. spring/orm/pymybatis/security/access_control.py +415 -0
  118. spring/orm/pymybatis/security/password_encoder.py +293 -0
  119. spring/orm/pymybatis/security/sensitive_data_masker.py +326 -0
  120. spring/orm/pymybatis/security/sql_injection_detector.py +675 -0
  121. spring/orm/pymybatis/transaction/__init__.py +9 -0
  122. spring/orm/pymybatis/transaction/transaction.py +288 -0
  123. spring/orm/pymybatis/type_handler/__init__.py +37 -0
  124. spring/orm/pymybatis/type_handler/type_handler.py +473 -0
  125. spring/orm/pymybatis/version.py +9 -0
  126. spring/orm/pymybatis/xml_parser/__init__.py +9 -0
  127. spring/orm/pymybatis/xml_parser/xml_parser.py +761 -0
  128. spring/retry/__init__.py +12 -0
  129. spring/retry/retry_annotations.py +71 -0
  130. spring/retry/retry_decorator.py +155 -0
  131. spring/scheduling/__init__.py +3 -0
  132. spring/scheduling/scheduler.py +389 -0
  133. spring/security/__init__.py +39 -0
  134. spring/security/jwt_utils.py +281 -0
  135. spring/security/replay_protection.py +206 -0
  136. spring/security/secret_manager.py +226 -0
  137. spring/security/security_aop.py +248 -0
  138. spring/security/security_context.py +172 -0
  139. spring/test/__init__.py +45 -0
  140. spring/test/slicing.py +341 -0
  141. spring/tracing/__init__.py +11 -0
  142. spring/tracing/skywalking.py +229 -0
  143. spring/tx/__init__.py +52 -0
  144. spring/tx/events.py +172 -0
  145. spring/tx/synchronization.py +143 -0
  146. spring/utils/__init__.py +5 -0
  147. spring/utils/banner.py +32 -0
  148. spring/utils/logger.py +73 -0
  149. spring/utils/redis_client.py +526 -0
  150. spring/validation/__init__.py +55 -0
  151. spring/validation/aop.py +141 -0
  152. spring/validation/constraints.py +357 -0
  153. spring/validation/exceptions.py +55 -0
  154. spring/validation/validator.py +139 -0
  155. spring/web/__init__.py +12 -0
  156. spring/web/actuator.py +319 -0
  157. spring/web/exception_handler.py +61 -0
  158. spring/web/health.py +399 -0
  159. spring/web/interceptor.py +91 -0
  160. spring/web/result.py +44 -0
  161. spring/web/swagger.py +601 -0
  162. spring/web/web_context.py +755 -0
  163. spring/websocket/__init__.py +86 -0
  164. spring/websocket/annotations.py +169 -0
  165. spring/websocket/broker.py +238 -0
  166. spring/websocket/exceptions.py +26 -0
  167. spring/websocket/handler.py +243 -0
  168. spring/websocket/router.py +526 -0
  169. spring/websocket/session.py +216 -0
  170. springbootai-1.8.0.dist-info/METADATA +2796 -0
  171. springbootai-1.8.0.dist-info/RECORD +175 -0
  172. springbootai-1.8.0.dist-info/WHEEL +5 -0
  173. springbootai-1.8.0.dist-info/entry_points.txt +2 -0
  174. springbootai-1.8.0.dist-info/licenses/LICENSE +7 -0
  175. springbootai-1.8.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,540 @@
1
+ """
2
+ PyMyBatis映射器模块
3
+
4
+ 实现Mapper接口的动态代理,支持XML和注解两种SQL定义方式
5
+ """
6
+
7
+ import importlib
8
+ import inspect
9
+ import types
10
+ from dataclasses import asdict, is_dataclass
11
+ from collections.abc import Mapping
12
+ from typing import Any, Dict, Type, Optional, Annotated, Union, get_args, get_origin, get_type_hints
13
+
14
+ from ..annotations import Param
15
+
16
+ _UNION_ORIGINS = {Union}
17
+ if getattr(types, 'UnionType', None) is not None:
18
+ _UNION_ORIGINS.add(types.UnionType)
19
+
20
+
21
+ class Mapper:
22
+ """
23
+ Mapper基类
24
+
25
+ 所有Mapper接口都应继承此类
26
+ """
27
+ pass
28
+
29
+
30
+ class MapperProxy:
31
+ """
32
+ Mapper代理类
33
+
34
+ 实现动态代理,将Mapper接口方法调用转换为SQL执行
35
+ """
36
+
37
+ def __init__(self, mapper_class: Type, sql_session: Any):
38
+ """
39
+ 初始化Mapper代理
40
+
41
+ Args:
42
+ mapper_class: Mapper类
43
+ sql_session: SqlSession实例
44
+ """
45
+ self.mapper_class = mapper_class
46
+ self.sql_session = sql_session
47
+ self._register_result_maps()
48
+
49
+ def _register_result_maps(self) -> None:
50
+ namespace = f"{self.mapper_class.__module__}.{self.mapper_class.__name__}"
51
+ for result_map in getattr(self.mapper_class, '__result_maps__', []):
52
+ self.sql_session.result_maps[result_map.id] = result_map
53
+ self.sql_session.result_maps[f"{namespace}.{result_map.id}"] = result_map
54
+
55
+ def __getattr__(self, name: str):
56
+ """
57
+ 获取属性
58
+
59
+ Args:
60
+ name: 属性名
61
+
62
+ Returns:
63
+ 方法执行结果
64
+ """
65
+ method = getattr(self.mapper_class, name, None)
66
+
67
+ if method is None or not callable(method):
68
+ raise AttributeError(f"Mapper {self.mapper_class.__name__} 没有方法: {name}")
69
+
70
+ def wrapper(*args, **kwargs):
71
+ return self._execute_method(method, *args, **kwargs)
72
+
73
+ return wrapper
74
+
75
+ def _execute_method(self, method, *args, **kwargs):
76
+ """
77
+ 执行方法
78
+
79
+ Args:
80
+ method: 方法对象
81
+ args: 位置参数
82
+ kwargs: 关键字参数
83
+
84
+ Returns:
85
+ 执行结果
86
+ """
87
+ # 获取被装饰的原始方法
88
+ wrapped_method = getattr(method, '__func__', method)
89
+
90
+ # 获取方法注解(通过装饰器附加的属性)
91
+ select_annotation = getattr(wrapped_method, 'select', None)
92
+ insert_annotation = getattr(wrapped_method, 'insert', None)
93
+ update_annotation = getattr(wrapped_method, 'update', None)
94
+ delete_annotation = getattr(wrapped_method, 'delete', None)
95
+ select_provider = getattr(wrapped_method, 'select_provider', None)
96
+ insert_provider = getattr(wrapped_method, 'insert_provider', None)
97
+ update_provider = getattr(wrapped_method, 'update_provider', None)
98
+ delete_provider = getattr(wrapped_method, 'delete_provider', None)
99
+ options = getattr(wrapped_method, 'options', None)
100
+ transaction = getattr(
101
+ wrapped_method,
102
+ 'transactional',
103
+ getattr(self.mapper_class, 'transactional', None),
104
+ )
105
+
106
+ # 解析参数
107
+ params = self._parse_params(method, args, kwargs)
108
+
109
+ def execute():
110
+ if options and options.flush_cache:
111
+ self.sql_session.sql_cache.clear()
112
+
113
+ if select_annotation or select_provider:
114
+ provider_options = select_provider.options if select_provider else {}
115
+ select_sql = (
116
+ self._call_sql_provider(select_provider, params)
117
+ if select_provider else select_annotation.value
118
+ )
119
+ result_map = self._resolve_result_map(
120
+ provider_options.get('result_map') if select_provider
121
+ else select_annotation.result_map
122
+ )
123
+ fetch_size = (
124
+ options.fetch_size if options and options.fetch_size is not None
125
+ else provider_options.get('fetch_size') if select_provider
126
+ else select_annotation.fetch_size
127
+ )
128
+ timeout = (
129
+ options.timeout if options and options.timeout is not None
130
+ else provider_options.get('timeout') if select_provider
131
+ else select_annotation.timeout
132
+ )
133
+ use_cache = (
134
+ options.use_cache if options else provider_options.get('cache', True)
135
+ if select_provider else select_annotation.cache
136
+ )
137
+ result_type = (
138
+ (provider_options.get('result_type') or
139
+ self._result_type_from_return_annotation(method))
140
+ if select_provider else
141
+ (select_annotation.result_type or
142
+ self._result_type_from_return_annotation(method))
143
+ )
144
+ single = self._returns_single(method)
145
+ if single:
146
+ result = self.sql_session.select_one(
147
+ select_sql,
148
+ params,
149
+ result_map=result_map,
150
+ use_cache=use_cache,
151
+ fetch_size=fetch_size,
152
+ timeout=timeout,
153
+ )
154
+ else:
155
+ result = self.sql_session.select(
156
+ select_sql,
157
+ params,
158
+ result_map=result_map,
159
+ use_cache=use_cache,
160
+ fetch_size=fetch_size,
161
+ timeout=timeout,
162
+ )
163
+ return self._apply_result_type(result, result_type)
164
+
165
+ timeout = options.timeout if options else None
166
+ if insert_annotation or insert_provider:
167
+ provider_options = insert_provider.options if insert_provider else {}
168
+ insert_sql = (
169
+ self._call_sql_provider(insert_provider, params)
170
+ if insert_provider else insert_annotation.value
171
+ )
172
+ use_generated_keys = (
173
+ insert_annotation.use_generated_keys if insert_annotation else False
174
+ ) or bool(options and options.use_generated_keys) or bool(
175
+ provider_options.get('use_generated_keys', False)
176
+ )
177
+ key_property = (
178
+ insert_annotation.key_property if insert_annotation else None
179
+ ) or (options.key_property if options else None) or provider_options.get('key_property')
180
+ result = self.sql_session.insert(
181
+ insert_sql,
182
+ params,
183
+ use_generated_keys=use_generated_keys,
184
+ timeout=timeout,
185
+ )
186
+ if use_generated_keys and key_property:
187
+ self._assign_generated_key(
188
+ args, kwargs, key_property, result
189
+ )
190
+ return result
191
+ if update_annotation or update_provider:
192
+ provider_options = update_provider.options if update_provider else {}
193
+ update_sql = (
194
+ self._call_sql_provider(update_provider, params)
195
+ if update_provider else update_annotation.value
196
+ )
197
+ return self.sql_session.update(
198
+ update_sql,
199
+ params,
200
+ timeout=timeout or (
201
+ provider_options.get('timeout') if update_provider
202
+ else update_annotation.timeout
203
+ ),
204
+ )
205
+ if delete_annotation or delete_provider:
206
+ provider_options = delete_provider.options if delete_provider else {}
207
+ delete_sql = (
208
+ self._call_sql_provider(delete_provider, params)
209
+ if delete_provider else delete_annotation.value
210
+ )
211
+ return self.sql_session.delete(
212
+ delete_sql,
213
+ params,
214
+ timeout=timeout or (
215
+ provider_options.get('timeout') if delete_provider
216
+ else delete_annotation.timeout
217
+ ),
218
+ )
219
+
220
+ namespace = self.mapper_class.__module__ + '.' + self.mapper_class.__name__
221
+ statement_id = f"{namespace}.{method.__name__}"
222
+ statement = self.sql_session.get_mapped_statement(statement_id)
223
+ if statement is not None and statement.sql_type == 'SELECT':
224
+ if self._returns_single(method):
225
+ result = self.sql_session.select_one(statement_id, params)
226
+ else:
227
+ result = self.sql_session.select(statement_id, params)
228
+ return self._apply_result_type(
229
+ result, self._result_type_from_return_annotation(method)
230
+ )
231
+
232
+ result = self.sql_session.execute(statement_id, params)
233
+ if (
234
+ statement is not None
235
+ and statement.sql_type == 'INSERT'
236
+ and statement.use_generated_keys
237
+ and statement.key_property
238
+ ):
239
+ self._assign_generated_key(args, kwargs, statement.key_property, result)
240
+ return result
241
+
242
+ if transaction is None:
243
+ return execute()
244
+ normalized_propagation = str(transaction.propagation).upper()
245
+
246
+ deferred = None
247
+ deferred_traceback = None
248
+ with self.sql_session.transaction(
249
+ isolation_level=transaction.isolation,
250
+ propagation=normalized_propagation,
251
+ ):
252
+ try:
253
+ return execute()
254
+ except Exception as exc:
255
+ if not transaction.rollback_for or any(
256
+ isinstance(exc, exc_type) for exc_type in transaction.rollback_for
257
+ ):
258
+ raise
259
+ deferred = exc
260
+ deferred_traceback = exc.__traceback__
261
+ raise deferred.with_traceback(deferred_traceback)
262
+
263
+ @staticmethod
264
+ def _call_sql_provider(provider: Any, params: Dict[str, Any]) -> str:
265
+ """Resolve and invoke a provider, accepting Java- and Python-style APIs."""
266
+ if provider is None or provider.provider_type is None:
267
+ raise ValueError("Provider 注解必须提供 provider_type/type/value")
268
+ target = provider.provider_type
269
+ if isinstance(target, str):
270
+ module_name, _, member_name = target.rpartition('.')
271
+ if not module_name:
272
+ raise ImportError(f"Provider 类型必须是可导入的全限定名称: {target}")
273
+ target = getattr(importlib.import_module(module_name), member_name)
274
+
275
+ method_name = provider.method
276
+ if method_name:
277
+ method = getattr(target, method_name, None)
278
+ if method is None and inspect.isclass(target):
279
+ method = getattr(target(), method_name, None)
280
+ elif callable(target):
281
+ method = target
282
+ else:
283
+ method = getattr(target, 'provide_sql', None) or getattr(target, 'sql', None)
284
+ if not callable(method):
285
+ raise TypeError("Provider 必须是可调用对象,或包含指定方法/provide_sql 方法")
286
+
287
+ try:
288
+ signature = inspect.signature(method)
289
+ positional = [p for p in signature.parameters.values()
290
+ if p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD)]
291
+ accepts_kwargs = any(p.kind == p.VAR_KEYWORD for p in signature.parameters.values())
292
+ if accepts_kwargs or (not positional and signature.parameters):
293
+ result = method(**params)
294
+ elif positional:
295
+ result = method(params)
296
+ else:
297
+ result = method()
298
+ except (TypeError, ValueError):
299
+ # C-extension callables may not expose signatures; the dict form
300
+ # is the least surprising provider contract.
301
+ result = method(params)
302
+ if not isinstance(result, str) or not result.strip():
303
+ raise ValueError("SQL Provider 必须返回非空 SQL 字符串")
304
+ return result
305
+
306
+ def _resolve_result_map(self, result_map: Any) -> Optional[str]:
307
+ if result_map is None:
308
+ return None
309
+ if not isinstance(result_map, str):
310
+ self.sql_session.result_maps[result_map.id] = result_map
311
+ return result_map.id
312
+ if result_map in self.sql_session.result_maps:
313
+ return result_map
314
+ namespaced = f"{self.mapper_class.__module__}.{self.mapper_class.__name__}.{result_map}"
315
+ return namespaced if namespaced in self.sql_session.result_maps else result_map
316
+
317
+ def _apply_result_type(self, result: Any, result_type: Any) -> Any:
318
+ target_type = self._resolve_result_type(result_type)
319
+ if target_type is None or result is None:
320
+ return result
321
+ if isinstance(result, list):
322
+ return [self._construct_result(target_type, item) for item in result]
323
+ return self._construct_result(target_type, result)
324
+
325
+ def _resolve_result_type(self, result_type: Any) -> Optional[Type]:
326
+ if result_type is None or result_type in ('dict', 'builtins.dict'):
327
+ return None
328
+ if isinstance(result_type, type):
329
+ return result_type
330
+ if not isinstance(result_type, str):
331
+ raise TypeError("result_type 必须是类型或可导入的类型名称")
332
+ if '.' in result_type:
333
+ module_name, type_name = result_type.rsplit('.', 1)
334
+ return getattr(importlib.import_module(module_name), type_name)
335
+ module = importlib.import_module(self.mapper_class.__module__)
336
+ return getattr(module, result_type)
337
+
338
+ @staticmethod
339
+ def _return_annotation(method) -> Any:
340
+ try:
341
+ return get_type_hints(method, include_extras=True).get('return')
342
+ except (NameError, TypeError):
343
+ return inspect.signature(method).return_annotation
344
+
345
+ def _returns_single(self, method) -> bool:
346
+ annotation = self._return_annotation(method)
347
+ if annotation is not inspect.Signature.empty:
348
+ origin = get_origin(annotation)
349
+ if origin in {list, tuple, set, dict}:
350
+ return False
351
+ if origin in _UNION_ORIGINS:
352
+ candidates = [value for value in get_args(annotation) if value is not type(None)]
353
+ return len(candidates) == 1 and not self._is_collection_type(candidates[0])
354
+ return not self._is_collection_type(annotation)
355
+
356
+ method_name = method.__name__
357
+ return (
358
+ method_name.startswith('find_by_')
359
+ or method_name.startswith('get_by_')
360
+ or method_name == 'find_one'
361
+ )
362
+
363
+ @staticmethod
364
+ def _is_collection_type(annotation: Any) -> bool:
365
+ return get_origin(annotation) in {list, tuple, set, dict} or annotation in {
366
+ list, tuple, set, dict
367
+ }
368
+
369
+ def _result_type_from_return_annotation(self, method) -> Optional[Type]:
370
+ annotation = self._return_annotation(method)
371
+ if annotation is inspect.Signature.empty:
372
+ return None
373
+ origin = get_origin(annotation)
374
+ if origin in {list, tuple, set}:
375
+ args = get_args(annotation)
376
+ return args[0] if args else None
377
+ if origin is dict:
378
+ return None
379
+ if origin in _UNION_ORIGINS:
380
+ candidates = [value for value in get_args(annotation) if value is not type(None)]
381
+ return candidates[0] if len(candidates) == 1 else None
382
+ return annotation if isinstance(annotation, type) else None
383
+
384
+ @staticmethod
385
+ def _construct_result(target_type: Type, value: Any) -> Any:
386
+ if isinstance(value, target_type):
387
+ return value
388
+ if not isinstance(value, Mapping):
389
+ return target_type(value)
390
+ try:
391
+ return target_type(**value)
392
+ except TypeError:
393
+ instance = target_type()
394
+ for key, item in value.items():
395
+ setattr(instance, key, item)
396
+ return instance
397
+
398
+ @staticmethod
399
+ def _assign_generated_key(args, kwargs, property_name: str, value: Any) -> None:
400
+ for candidate in list(args) + list(kwargs.values()):
401
+ if isinstance(candidate, dict):
402
+ candidate[property_name] = value
403
+ return
404
+ if hasattr(candidate, '__dict__'):
405
+ setattr(candidate, property_name, value)
406
+ return
407
+
408
+ def _parse_params(self, method, args, kwargs) -> Dict[str, Any]:
409
+ """
410
+ 解析方法参数
411
+
412
+ Args:
413
+ method: 方法对象
414
+ args: 位置参数
415
+ kwargs: 关键字参数
416
+
417
+ Returns:
418
+ 参数字典
419
+ """
420
+ params = {}
421
+
422
+ # 获取方法签名
423
+ sig = inspect.signature(method)
424
+ parameters = list(sig.parameters.values())
425
+ try:
426
+ type_hints = get_type_hints(method, include_extras=True)
427
+ except (NameError, TypeError):
428
+ type_hints = {}
429
+
430
+ # 处理位置参数,跳过第一个参数self(MapperProxy调用时不传递self)
431
+ for i, arg in enumerate(args):
432
+ param_index = i + 1 # 跳过self
433
+ if param_index < len(parameters):
434
+ parameter = parameters[param_index]
435
+ annotation = type_hints.get(parameter.name, parameter.annotation)
436
+ alias = self._parameter_alias(annotation, parameter.name)
437
+ self._add_param(params, alias, arg)
438
+ if alias != parameter.name:
439
+ params.setdefault(parameter.name, arg)
440
+
441
+ # 处理关键字参数
442
+ for key, value in kwargs.items():
443
+ parameter = sig.parameters.get(key)
444
+ annotation = type_hints.get(key, parameter.annotation) if parameter else None
445
+ alias = self._parameter_alias(annotation, key)
446
+ self._add_param(params, alias, value)
447
+ if alias != key:
448
+ params.setdefault(key, value)
449
+
450
+ return params
451
+
452
+ @staticmethod
453
+ def _parameter_alias(annotation: Any, default: str) -> str:
454
+ if isinstance(annotation, Param):
455
+ return annotation.value
456
+ if get_origin(annotation) is Annotated:
457
+ for metadata in get_args(annotation)[1:]:
458
+ if isinstance(metadata, Param):
459
+ return metadata.value
460
+ return default
461
+
462
+ @staticmethod
463
+ def _add_param(params: Dict[str, Any], name: str, value: Any) -> None:
464
+ """保留命名参数,同时为单对象参数提供可预测的字段展开。"""
465
+ params[name] = value
466
+
467
+ expanded = None
468
+ if isinstance(value, Mapping):
469
+ expanded = value
470
+ elif is_dataclass(value) and not isinstance(value, type):
471
+ expanded = asdict(value)
472
+ elif hasattr(value, 'to_dict') and callable(value.to_dict):
473
+ candidate = value.to_dict()
474
+ if isinstance(candidate, Mapping):
475
+ expanded = candidate
476
+ elif hasattr(value, '__dict__'):
477
+ expanded = {
478
+ key: item for key, item in vars(value).items()
479
+ if not key.startswith('_')
480
+ }
481
+
482
+ if expanded:
483
+ for key, item in expanded.items():
484
+ params.setdefault(str(key), item)
485
+
486
+
487
+ class MapperRegistry:
488
+ """
489
+ Mapper注册中心
490
+
491
+ 管理所有Mapper接口,支持懒加载
492
+ """
493
+
494
+ def __init__(self):
495
+ """初始化Mapper注册中心"""
496
+ self.mappers: Dict[str, Type] = {}
497
+
498
+ def add_mapper(self, mapper_class: Type) -> None:
499
+ """
500
+ 添加Mapper
501
+
502
+ Args:
503
+ mapper_class: Mapper类
504
+ """
505
+ key = f"{mapper_class.__module__}.{mapper_class.__name__}"
506
+ self.mappers[key] = mapper_class
507
+
508
+ def get_mapper(self, mapper_class: Type) -> Optional[Type]:
509
+ """
510
+ 获取Mapper类
511
+
512
+ Args:
513
+ mapper_class: Mapper类
514
+
515
+ Returns:
516
+ Mapper类,未找到返回None
517
+ """
518
+ key = f"{mapper_class.__module__}.{mapper_class.__name__}"
519
+ return self.mappers.get(key)
520
+
521
+ def has_mapper(self, mapper_class: Type) -> bool:
522
+ """
523
+ 检查Mapper是否已注册
524
+
525
+ Args:
526
+ mapper_class: Mapper类
527
+
528
+ Returns:
529
+ 是否已注册
530
+ """
531
+ key = f"{mapper_class.__module__}.{mapper_class.__name__}"
532
+ return key in self.mappers
533
+
534
+ def get_all_mappers(self) -> Dict[str, Type]:
535
+ """获取所有Mapper"""
536
+ return self.mappers
537
+
538
+ def clear(self) -> None:
539
+ """清空所有Mapper"""
540
+ self.mappers.clear()
@@ -0,0 +1,41 @@
1
+ """
2
+ PyMyBatis监控指标模块
3
+
4
+ 提供Prometheus兼容的指标收集和导出功能
5
+ """
6
+
7
+ from .metrics import (
8
+ MetricsCollector,
9
+ Counter,
10
+ Gauge,
11
+ Histogram,
12
+ Timer,
13
+ get_default_collector,
14
+ QUERY_COUNTER,
15
+ QUERY_TIMER,
16
+ ACTIVE_CONNECTIONS,
17
+ IDLE_CONNECTIONS,
18
+ CACHE_HIT_COUNTER,
19
+ CACHE_MISS_COUNTER,
20
+ TRANSACTION_COUNTER,
21
+ CIRCUIT_BREAKER_STATE,
22
+ CIRCUIT_BREAKER_FAILURE_RATE
23
+ )
24
+
25
+ __all__ = [
26
+ 'MetricsCollector',
27
+ 'Counter',
28
+ 'Gauge',
29
+ 'Histogram',
30
+ 'Timer',
31
+ 'get_default_collector',
32
+ 'QUERY_COUNTER',
33
+ 'QUERY_TIMER',
34
+ 'ACTIVE_CONNECTIONS',
35
+ 'IDLE_CONNECTIONS',
36
+ 'CACHE_HIT_COUNTER',
37
+ 'CACHE_MISS_COUNTER',
38
+ 'TRANSACTION_COUNTER',
39
+ 'CIRCUIT_BREAKER_STATE',
40
+ 'CIRCUIT_BREAKER_FAILURE_RATE'
41
+ ]