springbootAI 1.8.5__tar.gz → 1.8.7__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.
Files changed (224) hide show
  1. {springbootai-1.8.5/springbootAI.egg-info → springbootai-1.8.7}/PKG-INFO +1 -1
  2. {springbootai-1.8.5 → springbootai-1.8.7}/pyproject.toml +1 -1
  3. {springbootai-1.8.5 → springbootai-1.8.7}/spring/__init__.py +1 -1
  4. springbootai-1.8.7/spring/logging/loguru_logger.py +437 -0
  5. {springbootai-1.8.5 → springbootai-1.8.7}/spring/main.py +145 -69
  6. {springbootai-1.8.5 → springbootai-1.8.7}/spring/utils/banner.py +1 -1
  7. {springbootai-1.8.5 → springbootai-1.8.7/springbootAI.egg-info}/PKG-INFO +1 -1
  8. {springbootai-1.8.5 → springbootai-1.8.7}/springbootAI.egg-info/SOURCES.txt +1 -0
  9. springbootai-1.8.7/tests/test_component_init_error.py +171 -0
  10. springbootai-1.8.7/tests/test_logging_config.py +186 -0
  11. springbootai-1.8.5/spring/logging/loguru_logger.py +0 -272
  12. springbootai-1.8.5/tests/test_logging_config.py +0 -65
  13. {springbootai-1.8.5 → springbootai-1.8.7}/LICENSE +0 -0
  14. {springbootai-1.8.5 → springbootai-1.8.7}/MANIFEST.in +0 -0
  15. {springbootai-1.8.5 → springbootai-1.8.7}/README.md +0 -0
  16. {springbootai-1.8.5 → springbootai-1.8.7}/requirements.txt +0 -0
  17. {springbootai-1.8.5 → springbootai-1.8.7}/setup.cfg +0 -0
  18. {springbootai-1.8.5 → springbootai-1.8.7}/setup.py +0 -0
  19. {springbootai-1.8.5 → springbootai-1.8.7}/spring/ai/__init__.py +0 -0
  20. {springbootai-1.8.5 → springbootai-1.8.7}/spring/ai/advisors.py +0 -0
  21. {springbootai-1.8.5 → springbootai-1.8.7}/spring/ai/annotations.py +0 -0
  22. {springbootai-1.8.5 → springbootai-1.8.7}/spring/ai/autoconfig.py +0 -0
  23. {springbootai-1.8.5 → springbootai-1.8.7}/spring/ai/core.py +0 -0
  24. {springbootai-1.8.5 → springbootai-1.8.7}/spring/ai/etl.py +0 -0
  25. {springbootai-1.8.5 → springbootai-1.8.7}/spring/ai/memory.py +0 -0
  26. {springbootai-1.8.5 → springbootai-1.8.7}/spring/ai/observability.py +0 -0
  27. {springbootai-1.8.5 → springbootai-1.8.7}/spring/ai/providers.py +0 -0
  28. {springbootai-1.8.5 → springbootai-1.8.7}/spring/ai/resilience.py +0 -0
  29. {springbootai-1.8.5 → springbootai-1.8.7}/spring/ai/tools.py +0 -0
  30. {springbootai-1.8.5 → springbootai-1.8.7}/spring/ai/vectorstore.py +0 -0
  31. {springbootai-1.8.5 → springbootai-1.8.7}/spring/annotations/__init__.py +0 -0
  32. {springbootai-1.8.5 → springbootai-1.8.7}/spring/annotations/cache.py +0 -0
  33. {springbootai-1.8.5 → springbootai-1.8.7}/spring/annotations/cloud.py +0 -0
  34. {springbootai-1.8.5 → springbootai-1.8.7}/spring/annotations/conditional.py +0 -0
  35. {springbootai-1.8.5 → springbootai-1.8.7}/spring/annotations/core.py +0 -0
  36. {springbootai-1.8.5 → springbootai-1.8.7}/spring/annotations/messaging.py +0 -0
  37. {springbootai-1.8.5 → springbootai-1.8.7}/spring/aop/__init__.py +0 -0
  38. {springbootai-1.8.5 → springbootai-1.8.7}/spring/aop/cloud_aop.py +0 -0
  39. {springbootai-1.8.5 → springbootai-1.8.7}/spring/aop/comprehensive_aop.py +0 -0
  40. {springbootai-1.8.5 → springbootai-1.8.7}/spring/aop/method_interceptor.py +0 -0
  41. {springbootai-1.8.5 → springbootai-1.8.7}/spring/aop/proxy_factory.py +0 -0
  42. {springbootai-1.8.5 → springbootai-1.8.7}/spring/cloud/__init__.py +0 -0
  43. {springbootai-1.8.5 → springbootai-1.8.7}/spring/cloud/discovery.py +0 -0
  44. {springbootai-1.8.5 → springbootai-1.8.7}/spring/cloud/feign.py +0 -0
  45. {springbootai-1.8.5 → springbootai-1.8.7}/spring/cloud/gateway.py +0 -0
  46. {springbootai-1.8.5 → springbootai-1.8.7}/spring/cloud/load_balancer.py +0 -0
  47. {springbootai-1.8.5 → springbootai-1.8.7}/spring/cloud/seata.py +0 -0
  48. {springbootai-1.8.5 → springbootai-1.8.7}/spring/cloud/sentinel.py +0 -0
  49. {springbootai-1.8.5 → springbootai-1.8.7}/spring/cloud/tracer.py +0 -0
  50. {springbootai-1.8.5 → springbootai-1.8.7}/spring/cloud/transaction_store.py +0 -0
  51. {springbootai-1.8.5 → springbootai-1.8.7}/spring/config/__init__.py +0 -0
  52. {springbootai-1.8.5 → springbootai-1.8.7}/spring/config/binding.py +0 -0
  53. {springbootai-1.8.5 → springbootai-1.8.7}/spring/config/config_loader.py +0 -0
  54. {springbootai-1.8.5 → springbootai-1.8.7}/spring/context/__init__.py +0 -0
  55. {springbootai-1.8.5 → springbootai-1.8.7}/spring/context/application_context.py +0 -0
  56. {springbootai-1.8.5 → springbootai-1.8.7}/spring/context/bean_definition.py +0 -0
  57. {springbootai-1.8.5 → springbootai-1.8.7}/spring/context/bean_factory.py +0 -0
  58. {springbootai-1.8.5 → springbootai-1.8.7}/spring/context/registry.py +0 -0
  59. {springbootai-1.8.5 → springbootai-1.8.7}/spring/context/scanner.py +0 -0
  60. {springbootai-1.8.5 → springbootai-1.8.7}/spring/core/__init__.py +0 -0
  61. {springbootai-1.8.5 → springbootai-1.8.7}/spring/core/graceful_shutdown.py +0 -0
  62. {springbootai-1.8.5 → springbootai-1.8.7}/spring/core/typing_utils.py +0 -0
  63. {springbootai-1.8.5 → springbootai-1.8.7}/spring/csv/__init__.py +0 -0
  64. {springbootai-1.8.5 → springbootai-1.8.7}/spring/csv/annotations.py +0 -0
  65. {springbootai-1.8.5 → springbootai-1.8.7}/spring/csv/converters.py +0 -0
  66. {springbootai-1.8.5 → springbootai-1.8.7}/spring/csv/easy_csv.py +0 -0
  67. {springbootai-1.8.5 → springbootai-1.8.7}/spring/csv/exceptions.py +0 -0
  68. {springbootai-1.8.5 → springbootai-1.8.7}/spring/csv/reader.py +0 -0
  69. {springbootai-1.8.5 → springbootai-1.8.7}/spring/csv/writer.py +0 -0
  70. {springbootai-1.8.5 → springbootai-1.8.7}/spring/data/__init__.py +0 -0
  71. {springbootai-1.8.5 → springbootai-1.8.7}/spring/data/page.py +0 -0
  72. {springbootai-1.8.5 → springbootai-1.8.7}/spring/data/repository.py +0 -0
  73. {springbootai-1.8.5 → springbootai-1.8.7}/spring/data/specification.py +0 -0
  74. {springbootai-1.8.5 → springbootai-1.8.7}/spring/datasource/__init__.py +0 -0
  75. {springbootai-1.8.5 → springbootai-1.8.7}/spring/datasource/annotations.py +0 -0
  76. {springbootai-1.8.5 → springbootai-1.8.7}/spring/datasource/context.py +0 -0
  77. {springbootai-1.8.5 → springbootai-1.8.7}/spring/datasource/dynamic.py +0 -0
  78. {springbootai-1.8.5 → springbootai-1.8.7}/spring/event/__init__.py +0 -0
  79. {springbootai-1.8.5 → springbootai-1.8.7}/spring/event/publisher.py +0 -0
  80. {springbootai-1.8.5 → springbootai-1.8.7}/spring/excel/__init__.py +0 -0
  81. {springbootai-1.8.5 → springbootai-1.8.7}/spring/excel/annotations.py +0 -0
  82. {springbootai-1.8.5 → springbootai-1.8.7}/spring/excel/converters.py +0 -0
  83. {springbootai-1.8.5 → springbootai-1.8.7}/spring/excel/easy_excel.py +0 -0
  84. {springbootai-1.8.5 → springbootai-1.8.7}/spring/excel/exceptions.py +0 -0
  85. {springbootai-1.8.5 → springbootai-1.8.7}/spring/excel/reader.py +0 -0
  86. {springbootai-1.8.5 → springbootai-1.8.7}/spring/excel/style.py +0 -0
  87. {springbootai-1.8.5 → springbootai-1.8.7}/spring/excel/writer.py +0 -0
  88. {springbootai-1.8.5 → springbootai-1.8.7}/spring/i18n/__init__.py +0 -0
  89. {springbootai-1.8.5 → springbootai-1.8.7}/spring/i18n/accessor.py +0 -0
  90. {springbootai-1.8.5 → springbootai-1.8.7}/spring/i18n/auto_config.py +0 -0
  91. {springbootai-1.8.5 → springbootai-1.8.7}/spring/i18n/holder.py +0 -0
  92. {springbootai-1.8.5 → springbootai-1.8.7}/spring/i18n/locale.py +0 -0
  93. {springbootai-1.8.5 → springbootai-1.8.7}/spring/i18n/locale_resolver.py +0 -0
  94. {springbootai-1.8.5 → springbootai-1.8.7}/spring/i18n/message_source.py +0 -0
  95. {springbootai-1.8.5 → springbootai-1.8.7}/spring/i18n/middleware.py +0 -0
  96. {springbootai-1.8.5 → springbootai-1.8.7}/spring/i18n/properties.py +0 -0
  97. {springbootai-1.8.5 → springbootai-1.8.7}/spring/i18n/sources.py +0 -0
  98. {springbootai-1.8.5 → springbootai-1.8.7}/spring/logging/__init__.py +0 -0
  99. {springbootai-1.8.5 → springbootai-1.8.7}/spring/messaging/__init__.py +0 -0
  100. {springbootai-1.8.5 → springbootai-1.8.7}/spring/messaging/rabbitmq.py +0 -0
  101. {springbootai-1.8.5 → springbootai-1.8.7}/spring/monitoring/__init__.py +0 -0
  102. {springbootai-1.8.5 → springbootai-1.8.7}/spring/monitoring/prometheus.py +0 -0
  103. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/__init__.py +0 -0
  104. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/database.py +0 -0
  105. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/ddl_auto.py +0 -0
  106. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/migration.py +0 -0
  107. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/mybatis_integration.py +0 -0
  108. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/__init__.py +0 -0
  109. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/annotations/__init__.py +0 -0
  110. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/annotations/annotations.py +0 -0
  111. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/cache/__init__.py +0 -0
  112. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/cache/cache.py +0 -0
  113. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/cache/redis_cache.py +0 -0
  114. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/circuit_breaker/__init__.py +0 -0
  115. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/circuit_breaker/circuit_breaker.py +0 -0
  116. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/configuration.py +0 -0
  117. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/core/__init__.py +0 -0
  118. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/core/sql_session.py +0 -0
  119. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/core/sql_session_factory.py +0 -0
  120. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/dialect/__init__.py +0 -0
  121. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/dialect/dialect.py +0 -0
  122. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/dynamic_sql/__init__.py +0 -0
  123. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/dynamic_sql/dynamic_sql.py +0 -0
  124. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/interceptor/__init__.py +0 -0
  125. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/interceptor/interceptor.py +0 -0
  126. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/mapper/__init__.py +0 -0
  127. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/mapper/mapper.py +0 -0
  128. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/metrics/__init__.py +0 -0
  129. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/metrics/metrics.py +0 -0
  130. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/pool/__init__.py +0 -0
  131. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/pool/connection_pool.py +0 -0
  132. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/security/__init__.py +0 -0
  133. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/security/access_control.py +0 -0
  134. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/security/password_encoder.py +0 -0
  135. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/security/sensitive_data_masker.py +0 -0
  136. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/security/sql_injection_detector.py +0 -0
  137. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/transaction/__init__.py +0 -0
  138. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/transaction/transaction.py +0 -0
  139. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/type_handler/__init__.py +0 -0
  140. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/type_handler/type_handler.py +0 -0
  141. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/version.py +0 -0
  142. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/xml_parser/__init__.py +0 -0
  143. {springbootai-1.8.5 → springbootai-1.8.7}/spring/orm/pymybatis/xml_parser/xml_parser.py +0 -0
  144. {springbootai-1.8.5 → springbootai-1.8.7}/spring/retry/__init__.py +0 -0
  145. {springbootai-1.8.5 → springbootai-1.8.7}/spring/retry/retry_annotations.py +0 -0
  146. {springbootai-1.8.5 → springbootai-1.8.7}/spring/retry/retry_decorator.py +0 -0
  147. {springbootai-1.8.5 → springbootai-1.8.7}/spring/scheduling/__init__.py +0 -0
  148. {springbootai-1.8.5 → springbootai-1.8.7}/spring/scheduling/scheduler.py +0 -0
  149. {springbootai-1.8.5 → springbootai-1.8.7}/spring/security/__init__.py +0 -0
  150. {springbootai-1.8.5 → springbootai-1.8.7}/spring/security/jwt_utils.py +0 -0
  151. {springbootai-1.8.5 → springbootai-1.8.7}/spring/security/replay_protection.py +0 -0
  152. {springbootai-1.8.5 → springbootai-1.8.7}/spring/security/secret_manager.py +0 -0
  153. {springbootai-1.8.5 → springbootai-1.8.7}/spring/security/security_aop.py +0 -0
  154. {springbootai-1.8.5 → springbootai-1.8.7}/spring/security/security_context.py +0 -0
  155. {springbootai-1.8.5 → springbootai-1.8.7}/spring/test/__init__.py +0 -0
  156. {springbootai-1.8.5 → springbootai-1.8.7}/spring/test/slicing.py +0 -0
  157. {springbootai-1.8.5 → springbootai-1.8.7}/spring/tracing/__init__.py +0 -0
  158. {springbootai-1.8.5 → springbootai-1.8.7}/spring/tracing/skywalking.py +0 -0
  159. {springbootai-1.8.5 → springbootai-1.8.7}/spring/tx/__init__.py +0 -0
  160. {springbootai-1.8.5 → springbootai-1.8.7}/spring/tx/events.py +0 -0
  161. {springbootai-1.8.5 → springbootai-1.8.7}/spring/tx/synchronization.py +0 -0
  162. {springbootai-1.8.5 → springbootai-1.8.7}/spring/utils/__init__.py +0 -0
  163. {springbootai-1.8.5 → springbootai-1.8.7}/spring/utils/bean_utils.py +0 -0
  164. {springbootai-1.8.5 → springbootai-1.8.7}/spring/utils/logger.py +0 -0
  165. {springbootai-1.8.5 → springbootai-1.8.7}/spring/utils/redis_client.py +0 -0
  166. {springbootai-1.8.5 → springbootai-1.8.7}/spring/validation/__init__.py +0 -0
  167. {springbootai-1.8.5 → springbootai-1.8.7}/spring/validation/aop.py +0 -0
  168. {springbootai-1.8.5 → springbootai-1.8.7}/spring/validation/constraints.py +0 -0
  169. {springbootai-1.8.5 → springbootai-1.8.7}/spring/validation/exceptions.py +0 -0
  170. {springbootai-1.8.5 → springbootai-1.8.7}/spring/validation/validator.py +0 -0
  171. {springbootai-1.8.5 → springbootai-1.8.7}/spring/web/__init__.py +0 -0
  172. {springbootai-1.8.5 → springbootai-1.8.7}/spring/web/actuator.py +0 -0
  173. {springbootai-1.8.5 → springbootai-1.8.7}/spring/web/exception_handler.py +0 -0
  174. {springbootai-1.8.5 → springbootai-1.8.7}/spring/web/health.py +0 -0
  175. {springbootai-1.8.5 → springbootai-1.8.7}/spring/web/interceptor.py +0 -0
  176. {springbootai-1.8.5 → springbootai-1.8.7}/spring/web/result.py +0 -0
  177. {springbootai-1.8.5 → springbootai-1.8.7}/spring/web/swagger.py +0 -0
  178. {springbootai-1.8.5 → springbootai-1.8.7}/spring/web/web_context.py +0 -0
  179. {springbootai-1.8.5 → springbootai-1.8.7}/spring/websocket/__init__.py +0 -0
  180. {springbootai-1.8.5 → springbootai-1.8.7}/spring/websocket/annotations.py +0 -0
  181. {springbootai-1.8.5 → springbootai-1.8.7}/spring/websocket/broker.py +0 -0
  182. {springbootai-1.8.5 → springbootai-1.8.7}/spring/websocket/exceptions.py +0 -0
  183. {springbootai-1.8.5 → springbootai-1.8.7}/spring/websocket/handler.py +0 -0
  184. {springbootai-1.8.5 → springbootai-1.8.7}/spring/websocket/router.py +0 -0
  185. {springbootai-1.8.5 → springbootai-1.8.7}/spring/websocket/session.py +0 -0
  186. {springbootai-1.8.5 → springbootai-1.8.7}/springbootAI.egg-info/dependency_links.txt +0 -0
  187. {springbootai-1.8.5 → springbootai-1.8.7}/springbootAI.egg-info/entry_points.txt +0 -0
  188. {springbootai-1.8.5 → springbootai-1.8.7}/springbootAI.egg-info/requires.txt +0 -0
  189. {springbootai-1.8.5 → springbootai-1.8.7}/springbootAI.egg-info/top_level.txt +0 -0
  190. {springbootai-1.8.5 → springbootai-1.8.7}/sql/seata_undo_log.sql +0 -0
  191. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_actuator.py +0 -0
  192. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_ai_module.py +0 -0
  193. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_annotation_combinations.py +0 -0
  194. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_annotations_contract.py +0 -0
  195. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_aop_annotations_full.py +0 -0
  196. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_bean_utils.py +0 -0
  197. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_cache_annotations.py +0 -0
  198. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_cloud_embedded_full.py +0 -0
  199. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_conditional_annotations.py +0 -0
  200. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_config_binding.py +0 -0
  201. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_config_fixes_185.py +0 -0
  202. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_connection_resilience.py +0 -0
  203. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_core_annotations_full.py +0 -0
  204. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_csv_module.py +0 -0
  205. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_data_repository.py +0 -0
  206. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_datasource_routing.py +0 -0
  207. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_ddl_auto.py +0 -0
  208. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_di_config_event_full.py +0 -0
  209. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_excel_module.py +0 -0
  210. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_i18n_module.py +0 -0
  211. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_jpa_version_transient.py +0 -0
  212. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_new_features.py +0 -0
  213. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_orm_pymybatis_full.py +0 -0
  214. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_production_readiness.py +0 -0
  215. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_pymybatis_contract.py +0 -0
  216. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_seata_durable_store.py +0 -0
  217. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_security.py +0 -0
  218. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_security_full.py +0 -0
  219. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_swagger_module.py +0 -0
  220. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_test_slicing.py +0 -0
  221. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_transactional_events.py +0 -0
  222. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_validation_module.py +0 -0
  223. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_web_annotations_full.py +0 -0
  224. {springbootai-1.8.5 → springbootai-1.8.7}/tests/test_websocket_module.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: springbootAI
3
- Version: 1.8.5
3
+ Version: 1.8.7
4
4
  Summary: Python版Spring Boot框架,集成PyMyBatis ORM
5
5
  Author-email: YuConggen <1516933915@qq.com>
6
6
  License-Expression: MIT
@@ -12,7 +12,7 @@ build-backend = "setuptools.build_meta"
12
12
  [project]
13
13
  # 发行名 springbootAI(PyPI 包名,pip install springbootAI);导入包名仍为 spring
14
14
  name = "springbootAI"
15
- version = "1.8.5"
15
+ version = "1.8.7"
16
16
  description = "Python版Spring Boot框架,集成PyMyBatis ORM"
17
17
  readme = "README.md"
18
18
  license = "MIT"
@@ -5,7 +5,7 @@ from .config import *
5
5
  from .utils import *
6
6
  from .main import create_app, run, SpringApplication, run_cli
7
7
 
8
- __version__ = "1.8.5"
8
+ __version__ = "1.8.7"
9
9
  __author__ = "yuconggen"
10
10
  __license__ = "MIT"
11
11
 
@@ -0,0 +1,437 @@
1
+ """
2
+ Loguru结构化日志模块
3
+ 提供企业级日志功能
4
+ """
5
+ import logging
6
+ import sys
7
+ import os
8
+ import tempfile
9
+ from datetime import datetime
10
+ from typing import Optional
11
+
12
+ # 尝试导入loguru,失败则使用标准logging
13
+ try:
14
+ from loguru import logger as loguru_logger
15
+ _loguru_available = True
16
+ except ImportError:
17
+ _loguru_available = False
18
+ loguru_logger = None
19
+
20
+
21
+ def _format_config_error(config_key: str, config_value, reason: str,
22
+ suggestions: list = None) -> str:
23
+ """格式化配置错误信息为清晰可读的多行文本。
24
+
25
+ 统一所有配置校验失败的输出格式,让用户一眼定位是哪个配置项、什么值、
26
+ 为什么错、怎么修,而不是看到框架内部 traceback 不知道哪里出问题。
27
+
28
+ Args:
29
+ config_key: 配置项名(如 ``logging.log_dir``)
30
+ config_value: 用户配置的值
31
+ reason: 错误原因
32
+ suggestions: 修复建议列表
33
+ """
34
+ lines = [
35
+ "=" * 64,
36
+ f"[配置错误] {config_key} 配置无效",
37
+ "-" * 64,
38
+ f" 配置项: {config_key}",
39
+ f" 配置值: {config_value!r}",
40
+ f" 错误原因: {reason}",
41
+ ]
42
+ if suggestions:
43
+ lines.append(" 修复建议:")
44
+ for i, s in enumerate(suggestions, 1):
45
+ lines.append(f" {i}. {s}")
46
+ lines.append("=" * 64)
47
+ return "\n".join(lines)
48
+
49
+
50
+ class LoggingConfigError(RuntimeError):
51
+ """日志配置校验失败(路径不可创建/不可写入等)。
52
+
53
+ 继承 RuntimeError 而非直接用 ConfigurationError,避免与 config_loader 的
54
+ 循环导入;信息格式由 ``_format_config_error`` 统一。
55
+ """
56
+
57
+
58
+ class SpringLogger:
59
+ """Spring日志管理器"""
60
+
61
+ _instance = None
62
+ _lock = __import__('threading').Lock()
63
+
64
+ def __new__(cls, *args, **kwargs):
65
+ if cls._instance is None:
66
+ with cls._lock:
67
+ if cls._instance is None:
68
+ cls._instance = super().__new__(cls)
69
+ return cls._instance
70
+
71
+ def __init__(self, level: str = "INFO", log_format: str = None,
72
+ log_dir: str = "logs", retention: str = "30 days",
73
+ rotation: str = "100 MB"):
74
+ if hasattr(self, '_initialized'):
75
+ return
76
+ self.level = level.upper()
77
+ self.log_dir = log_dir
78
+ self.retention = retention
79
+ self.rotation = rotation
80
+ self._initialized = True
81
+ self._use_loguru = _loguru_available
82
+ self.log_format = log_format or self._default_format()
83
+
84
+ # import 时只设置控制台输出,不创建文件 handler(不创建 logs/ 目录)。
85
+ # 文件 handler 在 init_logging → reconfigure 读取用户配置后创建,
86
+ # 避免在 CWD 下提前生成 logs/ 目录与用户配置的 log_dir 产生双份日志。
87
+ if self._use_loguru:
88
+ loguru_logger.remove()
89
+ loguru_logger.add(
90
+ sys.stdout, format=self.log_format, level=self.level, colorize=True)
91
+ else:
92
+ self._setup_std_logging(strict=False, enable_file=False)
93
+
94
+ def reconfigure(self, level: Optional[str] = None, log_format: Optional[str] = None,
95
+ log_dir: Optional[str] = None, retention: Optional[str] = None,
96
+ rotation: Optional[str] = None) -> None:
97
+ """重新配置日志参数并重建处理器(读取配置后调用)。
98
+
99
+ SpringLogger 为单例,``__init__`` 的 ``_initialized`` 守卫会阻止后续 ``__init__``
100
+ 更新参数。``init_logging`` 读取 ``application.yml`` 的 ``logging.log_dir`` 后,
101
+ 必须通过本方法重新配置,否则日志目录永远停留在默认 ``'logs'``(相对当前工作目录,
102
+ 即 Application.py 所在目录/logs)。
103
+
104
+ 本方法以 **strict 模式** 校验日志目录:用户显式配置的 ``log_dir`` 如果路径
105
+ 不合法/不可创建/不可写入,将抛出 ``LoggingConfigError``(含明确错误信息),
106
+ 而不是静默降级到默认目录——配置写错必须让用户知道。
107
+
108
+ Args:
109
+ level: 日志级别(如 INFO/DEBUG),None 表示保留原值
110
+ log_format: 日志格式,None 表示保留原值
111
+ log_dir: 日志目录,None 表示保留原值
112
+ retention: 日志保留期,None 表示保留原值
113
+ rotation: 日志轮转大小,None 表示保留原值
114
+
115
+ Raises:
116
+ LoggingConfigError: 日志目录不可创建或不可写入(strict 模式)
117
+ """
118
+ if level is not None:
119
+ self.level = level.upper()
120
+ if log_format is not None:
121
+ self.log_format = log_format
122
+ # 记录是否是用户显式配置了 log_dir:只有显式配置才 strict 校验,
123
+ # 保留旧值(log_dir=None)时用非严格模式,避免默认 'logs' 在只读 CWD 报错
124
+ log_dir_explicitly_set = log_dir is not None
125
+ if log_dir_explicitly_set:
126
+ self.log_dir = log_dir
127
+ if retention is not None:
128
+ self.retention = retention
129
+ if rotation is not None:
130
+ self.rotation = rotation
131
+ # 重建日志处理器:用户显式配置 log_dir 时 strict=True(路径错必须报错),
132
+ # 否则 strict=False(保留旧值/默认值时允许降级)。
133
+ # reconfigure 始终启用文件 handler(enable_file=True),
134
+ # 因为此时用户配置已读取,应该创建日志文件。
135
+ strict_mode = log_dir_explicitly_set
136
+ if self._use_loguru:
137
+ self._setup_loguru(strict=strict_mode, enable_file=True)
138
+ else:
139
+ self._setup_std_logging(strict=strict_mode, enable_file=True)
140
+
141
+ def _validate_log_dir(self, strict: bool) -> bool:
142
+ """校验日志目录可创建且可写入。
143
+
144
+ ``strict=True``(用户配置生效路径):校验失败抛 ``LoggingConfigError``,
145
+ 错误信息含配置项名、配置值、具体 errno 原因、修复建议。
146
+ ``strict=False``(import 时默认配置):校验失败仅 warning 并返回 False,
147
+ 保证 import 不崩溃。
148
+
149
+ Args:
150
+ strict: 是否严格模式(True=报错,False=降级)
151
+
152
+ Returns:
153
+ True 表示目录可用,False 表示不可用(仅 strict=False 时返回)
154
+
155
+ Raises:
156
+ LoggingConfigError: strict=True 且目录不可用时
157
+ """
158
+ suggestions = [
159
+ "检查路径拼写是否正确",
160
+ "确认父目录存在且有写权限",
161
+ "使用绝对路径避免工作目录歧义",
162
+ "Windows 路径用双反斜杠或正斜杠: \"C:/Users/xxx/logs\"",
163
+ ]
164
+
165
+ # 1. 空值校验
166
+ if not self.log_dir or not str(self.log_dir).strip():
167
+ msg = _format_config_error(
168
+ 'logging.log_dir', self.log_dir,
169
+ '日志目录为空字符串,无法创建', suggestions)
170
+ if strict:
171
+ raise LoggingConfigError(msg)
172
+ print(f"[WARNING] {msg}", file=sys.stderr)
173
+ return False
174
+
175
+ log_dir = str(self.log_dir).strip()
176
+
177
+ # 2. 尝试创建目录(捕获所有 OSError 子类:PermissionError/FileNotFoundError 等)
178
+ try:
179
+ os.makedirs(log_dir, exist_ok=True)
180
+ except OSError as exc:
181
+ # 根据 errno 给出具体原因
182
+ import errno
183
+ if exc.errno == errno.EACCES:
184
+ reason = f"无写权限(Permission denied): {exc}"
185
+ elif exc.errno == errno.ENOENT:
186
+ reason = f"父目录不存在且无法创建: {exc}"
187
+ elif exc.errno == errno.ENOTDIR:
188
+ reason = f"路径中某一段不是目录(可能是文件): {exc}"
189
+ elif exc.errno == errno.EROFS:
190
+ reason = f"只读文件系统: {exc}"
191
+ else:
192
+ reason = f"{type(exc).__name__} (errno={exc.errno}): {exc}"
193
+ msg = _format_config_error(
194
+ 'logging.log_dir', self.log_dir, reason, suggestions)
195
+ if strict:
196
+ raise LoggingConfigError(msg) from exc
197
+ print(f"[WARNING] {msg}", file=sys.stderr)
198
+ return False
199
+
200
+ # 3. 测试可写入(创建临时文件后删除)
201
+ test_file = os.path.join(log_dir, f".spring_write_test_{os.getpid()}")
202
+ try:
203
+ with open(test_file, 'w', encoding='utf-8') as f:
204
+ f.write("write permission test")
205
+ os.remove(test_file)
206
+ except OSError as exc:
207
+ import errno
208
+ if exc.errno == errno.EACCES:
209
+ reason = f"目录已创建但无写权限: {exc}"
210
+ else:
211
+ reason = f"目录已创建但无法写入文件: {type(exc).__name__}: {exc}"
212
+ msg = _format_config_error(
213
+ 'logging.log_dir', self.log_dir, reason, suggestions)
214
+ if strict:
215
+ raise LoggingConfigError(msg) from exc
216
+ print(f"[WARNING] {msg}", file=sys.stderr)
217
+ return False
218
+
219
+ return True
220
+
221
+ def _default_format(self) -> str:
222
+ """默认日志格式"""
223
+ if self._use_loguru:
224
+ return (
225
+ "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
226
+ "<level>{level: <8}</level> | "
227
+ "<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | "
228
+ "<level>{message}</level>"
229
+ )
230
+ return "%(asctime)s | %(levelname)-8s | %(name)s:%(funcName)s:%(lineno)d | %(message)s"
231
+
232
+ def _setup_loguru(self, strict: bool = False, enable_file: bool = True):
233
+ """配置Loguru。
234
+
235
+ Args:
236
+ strict: True 时日志目录校验失败抛 ``LoggingConfigError``(用户配置路径);
237
+ False 时降级为仅控制台日志(import 时默认配置,保证不崩溃)
238
+ enable_file: True 时添加文件 handler(创建日志目录+文件);
239
+ False 时仅控制台输出(import 时避免提前创建 logs/ 目录)
240
+ """
241
+ # 清除默认处理器
242
+ loguru_logger.remove()
243
+
244
+ # 添加控制台输出(始终启用,确保错误信息至少能打印到控制台)
245
+ loguru_logger.add(
246
+ sys.stdout,
247
+ format=self.log_format,
248
+ level=self.level,
249
+ colorize=True,
250
+ )
251
+
252
+ # enable_file=False 时只输出到控制台(import 时使用,不创建 logs/ 目录)
253
+ if not enable_file:
254
+ return
255
+
256
+ # 校验日志目录:strict 模式下失败会抛异常(由 reconfigure 捕获并输出),
257
+ # 非 strict 模式下返回 False 则仅使用控制台日志
258
+ if not self._validate_log_dir(strict=strict):
259
+ return
260
+
261
+ # 添加文件输出(按日期轮转)
262
+ loguru_logger.add(
263
+ os.path.join(self.log_dir, "application_{time:YYYY-MM-DD}.log"),
264
+ format=self.log_format,
265
+ level=self.level,
266
+ rotation=self.rotation,
267
+ retention=self.retention,
268
+ compression="zip",
269
+ encoding="utf-8",
270
+ )
271
+ # 添加错误日志单独输出
272
+ loguru_logger.add(
273
+ os.path.join(self.log_dir, "error_{time:YYYY-MM-DD}.log"),
274
+ format=self.log_format,
275
+ level="ERROR",
276
+ rotation=self.rotation,
277
+ retention=self.retention,
278
+ compression="zip",
279
+ encoding="utf-8",
280
+ )
281
+
282
+ def _setup_std_logging(self, strict: bool = False, enable_file: bool = True):
283
+ """配置标准logging(fallback)。
284
+
285
+ Args:
286
+ strict: True 时日志目录校验失败抛 ``LoggingConfigError``;
287
+ False 时降级为仅控制台日志(import 时保证不崩溃)
288
+ enable_file: True 时添加文件 handler;False 时仅控制台输出
289
+ (import 时避免提前创建 logs/ 目录)
290
+ """
291
+ self._logger = logging.getLogger("Spring")
292
+ self._logger.setLevel(getattr(logging, self.level))
293
+ # 清除旧 handler(reconfigure 重建时避免重复 add 导致日志重复输出)
294
+ for handler in list(self._logger.handlers):
295
+ self._logger.removeHandler(handler)
296
+
297
+ # 添加控制台输出(始终启用)
298
+ console_handler = logging.StreamHandler(sys.stdout)
299
+ console_handler.setFormatter(logging.Formatter(self.log_format))
300
+ self._logger.addHandler(console_handler)
301
+
302
+ # enable_file=False 时只输出到控制台
303
+ if not enable_file:
304
+ return
305
+
306
+ # 校验日志目录
307
+ if not self._validate_log_dir(strict=strict):
308
+ return
309
+
310
+ # 添加文件输出
311
+ file_handler = logging.FileHandler(
312
+ os.path.join(self.log_dir, "application.log"),
313
+ encoding="utf-8"
314
+ )
315
+ file_handler.setFormatter(logging.Formatter(self.log_format))
316
+ self._logger.addHandler(file_handler)
317
+
318
+ def get_logger(self):
319
+ """获取日志实例"""
320
+ if self._use_loguru:
321
+ return loguru_logger
322
+ return self._logger
323
+
324
+ def info(self, message: str, **kwargs):
325
+ """记录INFO级别日志"""
326
+ if self._use_loguru:
327
+ loguru_logger.info(message, **kwargs)
328
+ else:
329
+ self._logger.info(message)
330
+
331
+ def debug(self, message: str, **kwargs):
332
+ """记录DEBUG级别日志"""
333
+ if self._use_loguru:
334
+ loguru_logger.debug(message, **kwargs)
335
+ else:
336
+ self._logger.debug(message)
337
+
338
+ def warning(self, message: str, **kwargs):
339
+ """记录WARNING级别日志"""
340
+ if self._use_loguru:
341
+ loguru_logger.warning(message, **kwargs)
342
+ else:
343
+ self._logger.warning(message)
344
+
345
+ def error(self, message: str, **kwargs):
346
+ """记录ERROR级别日志"""
347
+ if self._use_loguru:
348
+ loguru_logger.error(message, **kwargs)
349
+ else:
350
+ self._logger.error(message)
351
+
352
+ def critical(self, message: str, **kwargs):
353
+ """记录CRITICAL级别日志"""
354
+ if self._use_loguru:
355
+ loguru_logger.critical(message, **kwargs)
356
+ else:
357
+ self._logger.critical(message)
358
+
359
+ def exception(self, message: str, **kwargs):
360
+ """记录异常日志"""
361
+ if self._use_loguru:
362
+ loguru_logger.exception(message, **kwargs)
363
+ else:
364
+ self._logger.exception(message)
365
+
366
+ def log(self, level: str, message: str, **kwargs):
367
+ """记录指定级别日志"""
368
+ if self._use_loguru:
369
+ loguru_logger.log(level.upper(), message, **kwargs)
370
+ else:
371
+ self._logger.log(getattr(logging, level.upper()), message)
372
+
373
+ def bind(self, **extra):
374
+ """绑定额外字段到日志上下文"""
375
+ if self._use_loguru:
376
+ return loguru_logger.bind(**extra)
377
+ return self._logger
378
+
379
+ def patch(self, function):
380
+ """添加额外字段到日志消息"""
381
+ if self._use_loguru:
382
+ return loguru_logger.patch(function)
383
+ return self._logger
384
+
385
+
386
+ # 创建全局日志管理器实例
387
+ spring_logger = SpringLogger()
388
+
389
+
390
+ def init_logging(config: dict) -> None:
391
+ """初始化日志配置(读取 application.yml 的 logging 段后调用)。
392
+
393
+ 通过 ``reconfigure`` 重新配置单例 SpringLogger,使 ``logging.log_dir`` / ``level``
394
+ 等配置生效。直接 ``SpringLogger(log_dir=...)`` 因单例 ``_initialized`` 守卫不会更新参数。
395
+
396
+ 如果用户配置的 ``log_dir`` 路径不合法/不可创建/不可写入,``reconfigure`` 会抛出
397
+ ``LoggingConfigError``。本函数捕获后打印明确错误横幅到 stderr(确保用户看到),
398
+ 然后重新抛出,让上层 fail-fast 终止启动——配置写错必须让用户知道,而不是静默
399
+ 降级到默认目录。
400
+
401
+ Args:
402
+ config: 配置字典(logging 子字典),包含 level/log_dir/retention/rotation 等
403
+
404
+ Raises:
405
+ LoggingConfigError: 日志目录配置无效(路径不合法/不可创建/不可写入)
406
+ """
407
+ global spring_logger
408
+ try:
409
+ spring_logger.reconfigure(
410
+ level=config.get('level'),
411
+ log_format=config.get('log_format'),
412
+ log_dir=config.get('log_dir'),
413
+ retention=config.get('retention'),
414
+ rotation=config.get('rotation'),
415
+ )
416
+ except LoggingConfigError as e:
417
+ # 只打印格式化的错误信息(含配置项/值/原因/建议),不输出框架 traceback
418
+ print(f"\n{e}\n", file=sys.stderr)
419
+ raise
420
+
421
+
422
+ # 兼容标准logging模块
423
+ if _loguru_available:
424
+ class LoguruHandler(logging.Handler):
425
+ """Loguru处理器,用于将标准logging日志转发到Loguru"""
426
+
427
+ def emit(self, record: logging.LogRecord):
428
+ """处理日志记录"""
429
+ try:
430
+ level = loguru_logger.level(record.levelname).name
431
+ message = self.format(record)
432
+ loguru_logger.log(level, message)
433
+ except Exception:
434
+ self.handleError(record)
435
+
436
+ # 将标准logging日志转发到Loguru
437
+ logging.basicConfig(handlers=[LoguruHandler()], level=logging.INFO)