pycraftcore 2.0.0__tar.gz → 2.1.0__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 (210) hide show
  1. pycraftcore-2.1.0/PKG-INFO +418 -0
  2. pycraftcore-2.1.0/Readme.md +316 -0
  3. pycraftcore-2.1.0/pyproject.toml +113 -0
  4. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/adapter/schema.py +4 -1
  5. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/enum/operation_type.py +1 -0
  6. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/model/operation.py +12 -2
  7. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/__init__.py +4 -0
  8. pycraftcore-2.1.0/src/pycraftcore/file_handler/adapter/extension/svg/svg_reader.py +8 -0
  9. pycraftcore-2.1.0/src/pycraftcore/file_handler/adapter/extension/svg/svg_writer.py +8 -0
  10. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/handler.py +3 -0
  11. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/sqlite/adapter.py +2 -2
  12. pycraftcore-2.1.0/src/pycraftcore/retry/__init__.py +0 -0
  13. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/adapter/python/host_bridge_server.py +4 -1
  14. pycraftcore-2.1.0/src/pycraftcore/serializer/__init__.py +0 -0
  15. pycraftcore-2.1.0/src/pycraftcore.egg-info/PKG-INFO +418 -0
  16. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore.egg-info/SOURCES.txt +4 -0
  17. pycraftcore-2.1.0/src/pycraftcore.egg-info/requires.txt +96 -0
  18. pycraftcore-2.0.0/PKG-INFO +0 -257
  19. pycraftcore-2.0.0/Readme.md +0 -217
  20. pycraftcore-2.0.0/pyproject.toml +0 -84
  21. pycraftcore-2.0.0/src/pycraftcore.egg-info/PKG-INFO +0 -257
  22. pycraftcore-2.0.0/src/pycraftcore.egg-info/requires.txt +0 -26
  23. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/LICENSE +0 -0
  24. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/setup.cfg +0 -0
  25. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/__init__.py +0 -0
  26. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/__init__.py +0 -0
  27. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/adapter/__init__.py +0 -0
  28. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/adapter/load_application_configuration.py +0 -0
  29. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/adapter/omega_configuration_reader.py +0 -0
  30. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/enum/__init__.py +0 -0
  31. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/enum/connector_type.py +0 -0
  32. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/enum/file_operation_action.py +0 -0
  33. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/enum/run_type_application.py +0 -0
  34. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/enum/run_type_environment.py +0 -0
  35. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/model/__init__.py +0 -0
  36. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/model/configuration.py +0 -0
  37. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/model/connector.py +0 -0
  38. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/port/__init__.py +0 -0
  39. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/port/configuration.py +0 -0
  40. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/application_configuration/port/configuration_reader.py +0 -0
  41. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/authentication/__init__.py +0 -0
  42. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/authentication/model/__init__.py +0 -0
  43. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/authentication/model/auth_type.py +0 -0
  44. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/authentication/model/basic_auth.py +0 -0
  45. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/authentication/model/no_auth.py +0 -0
  46. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/authentication/model/token_auth.py +0 -0
  47. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/__init__.py +0 -0
  48. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/adapter/__init__.py +0 -0
  49. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/adapter/aiobreaker_circuit_breaker_policy.py +0 -0
  50. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/configuration/__init__.py +0 -0
  51. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/configuration/circuit_breaker_configuration.py +0 -0
  52. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/enum/__init__.py +0 -0
  53. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/enum/circuit_breaker_status.py +0 -0
  54. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/exception/__init__.py +0 -0
  55. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/exception/circuit_breaker_open_exception.py +0 -0
  56. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/port/__init__.py +0 -0
  57. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/circuit_breaker/port/async_circuit_breaker.py +0 -0
  58. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/__init__.py +0 -0
  59. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/adapter/__init__.py +0 -0
  60. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/adapter/compute_engine.py +0 -0
  61. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/adapter/numpy_arithmetic_operation.py +0 -0
  62. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/adapter/scipy_calculus_operation.py +0 -0
  63. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/const_typing.py +0 -0
  64. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/port/__init__.py +0 -0
  65. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/port/arithmetic_operation.py +0 -0
  66. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/port/calculus_operation.py +0 -0
  67. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/computation_engine/port/engine.py +0 -0
  68. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/context/__init__.py +0 -0
  69. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/context/request_id_context.py +0 -0
  70. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/__init__.py +0 -0
  71. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/__init__.py +0 -0
  72. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/csv/__init__.py +0 -0
  73. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/csv/csv_reader.py +0 -0
  74. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/csv/csv_writer.py +0 -0
  75. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/json/__init__.py +0 -0
  76. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/json/json_reader.py +0 -0
  77. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/json/json_writer.py +0 -0
  78. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/markdown/__init__.py +0 -0
  79. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/markdown/markdown_reader.py +0 -0
  80. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/markdown/markdown_writer.py +0 -0
  81. {pycraftcore-2.0.0/src/pycraftcore/file_handler/adapter/extension/txt → pycraftcore-2.1.0/src/pycraftcore/file_handler/adapter/extension/svg}/__init__.py +0 -0
  82. {pycraftcore-2.0.0/src/pycraftcore/file_handler/adapter/extension/yml → pycraftcore-2.1.0/src/pycraftcore/file_handler/adapter/extension/txt}/__init__.py +0 -0
  83. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/txt/txt_reader.py +0 -0
  84. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/txt/txt_writer.py +0 -0
  85. {pycraftcore-2.0.0/src/pycraftcore/file_handler/enum → pycraftcore-2.1.0/src/pycraftcore/file_handler/adapter/extension/yml}/__init__.py +0 -0
  86. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/yml/yml_reader.py +0 -0
  87. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/extension/yml/yml_writer.py +0 -0
  88. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/factory.py +0 -0
  89. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/provider.py +0 -0
  90. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/adapter/strategy.py +0 -0
  91. {pycraftcore-2.0.0/src/pycraftcore/http → pycraftcore-2.1.0/src/pycraftcore/file_handler/enum}/__init__.py +0 -0
  92. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/enum/read_chunk_mode.py +0 -0
  93. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/port/__init__.py +0 -0
  94. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/port/file_handler_factory.py +0 -0
  95. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/port/file_handler_provider.py +0 -0
  96. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/port/reader.py +0 -0
  97. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/port/writer.py +0 -0
  98. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/schema/__init__.py +0 -0
  99. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/file_handler/schema/file_manifest.py +0 -0
  100. {pycraftcore-2.0.0/src/pycraftcore/http/context → pycraftcore-2.1.0/src/pycraftcore/http}/__init__.py +0 -0
  101. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/adapter/__init__.py +0 -0
  102. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/adapter/aiohttp_client.py +0 -0
  103. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/adapter/httpx_client.py +0 -0
  104. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/configuration/__init__.py +0 -0
  105. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/configuration/client_configuration.py +0 -0
  106. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/configuration/http_client_configuration.py +0 -0
  107. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/configuration/limits_configuration.py +0 -0
  108. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/configuration/security_configuration.py +0 -0
  109. {pycraftcore-2.0.0/src/pycraftcore/http/policy → pycraftcore-2.1.0/src/pycraftcore/http/context}/__init__.py +0 -0
  110. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/context/request_context.py +0 -0
  111. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/enum/__init__.py +0 -0
  112. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/enum/http_method.py +0 -0
  113. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/middleware/__init__.py +0 -0
  114. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/middleware/request_id_middleware.py +0 -0
  115. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/middleware/request_middleware.py +0 -0
  116. {pycraftcore-2.0.0/src/pycraftcore/profiler → pycraftcore-2.1.0/src/pycraftcore/http/policy}/__init__.py +0 -0
  117. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/policy/http_error_policy.py +0 -0
  118. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/port/__init__.py +0 -0
  119. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/http/port/async_http_client.py +0 -0
  120. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/logger/__init__.py +0 -0
  121. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/logger/adapter/__init__.py +0 -0
  122. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/logger/adapter/standard_logger.py +0 -0
  123. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/logger/configuration.py +0 -0
  124. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/logger/port/__init__.py +0 -0
  125. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/logger/port/logger.py +0 -0
  126. {pycraftcore-2.0.0/src/pycraftcore/query_language → pycraftcore-2.1.0/src/pycraftcore/profiler}/__init__.py +0 -0
  127. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/profiler/profiled.py +0 -0
  128. /pycraftcore-2.0.0/src/pycraftcore/query_language/constants/sql/__init__.py → /pycraftcore-2.1.0/src/pycraftcore/py.typed +0 -0
  129. {pycraftcore-2.0.0/src/pycraftcore/repository → pycraftcore-2.1.0/src/pycraftcore/query_language}/__init__.py +0 -0
  130. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/adapter/__init__.py +0 -0
  131. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/adapter/sql/__init__.py +0 -0
  132. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/adapter/sql/adapter.py +0 -0
  133. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/adapter/sql/factory.py +0 -0
  134. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/constants/__init__.py +0 -0
  135. {pycraftcore-2.0.0/src/pycraftcore/repository/adapter/no_sql → pycraftcore-2.1.0/src/pycraftcore/query_language/constants/sql}/__init__.py +0 -0
  136. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/constants/sql/allowed_sql_root_statements.py +0 -0
  137. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/constants/sql/forbidden_sql_statements.py +0 -0
  138. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/port/__init__.py +0 -0
  139. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/query_language/port/query_handler.py +0 -0
  140. {pycraftcore-2.0.0/src/pycraftcore/repository/adapter/no_sql/mongodb → pycraftcore-2.1.0/src/pycraftcore/repository}/__init__.py +0 -0
  141. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/__init__.py +0 -0
  142. {pycraftcore-2.0.0/src/pycraftcore/repository/adapter/sql → pycraftcore-2.1.0/src/pycraftcore/repository/adapter/no_sql}/__init__.py +0 -0
  143. {pycraftcore-2.0.0/src/pycraftcore/repository/adapter/sql/postgresql → pycraftcore-2.1.0/src/pycraftcore/repository/adapter/no_sql/mongodb}/__init__.py +0 -0
  144. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/no_sql/mongodb/adapter.py +0 -0
  145. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/no_sql/mongodb/factory.py +0 -0
  146. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/no_sql/mongodb/mapper.py +0 -0
  147. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/no_sql/mongodb/schema.py +0 -0
  148. {pycraftcore-2.0.0/src/pycraftcore/resilient_http → pycraftcore-2.1.0/src/pycraftcore/repository/adapter/sql}/__init__.py +0 -0
  149. {pycraftcore-2.0.0/src/pycraftcore/retry → pycraftcore-2.1.0/src/pycraftcore/repository/adapter/sql/postgresql}/__init__.py +0 -0
  150. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/postgresql/adapter.py +0 -0
  151. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/postgresql/factory.py +0 -0
  152. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/postgresql/mapper.py +0 -0
  153. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/postgresql/schema.py +0 -0
  154. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/sqlite/__init__.py +0 -0
  155. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/sqlite/factory.py +0 -0
  156. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/sqlite/mapper.py +0 -0
  157. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/adapter/sql/sqlite/schema.py +0 -0
  158. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/port/__init__.py +0 -0
  159. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/repository/port/repository.py +0 -0
  160. {pycraftcore-2.0.0/src/pycraftcore/serializer → pycraftcore-2.1.0/src/pycraftcore/resilient_http}/__init__.py +0 -0
  161. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/adapter/__init__.py +0 -0
  162. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/adapter/resilient_client.py +0 -0
  163. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/adapter/resilient_http_client_factory.py +0 -0
  164. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/adapter/resilient_transport.py +0 -0
  165. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/adapter/resilient_transport_factory.py +0 -0
  166. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/configuration/__init__.py +0 -0
  167. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/configuration/resilient_http_configuration.py +0 -0
  168. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/port/__init__.py +0 -0
  169. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/resilient_http/port/async_resilient_http_client.py +0 -0
  170. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/retry/adapter/__init__.py +0 -0
  171. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/retry/adapter/tenacity_retry_policy.py +0 -0
  172. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/retry/configuration/__init__.py +0 -0
  173. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/retry/configuration/retry_configuration.py +0 -0
  174. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/retry/port/__init__.py +0 -0
  175. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/retry/port/retry.py +0 -0
  176. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/__init__.py +0 -0
  177. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/adapter/__init__.py +0 -0
  178. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/adapter/python/__init__.py +0 -0
  179. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/adapter/python/adapter.py +0 -0
  180. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/adapter/python/factory.py +0 -0
  181. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/adapter/python/python_runner_template.py +0 -0
  182. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/port/__init__.py +0 -0
  183. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/port/code.py +0 -0
  184. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/schema/__init__.py +0 -0
  185. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/schema/code_result.py +0 -0
  186. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/schema/code_stdout.py +0 -0
  187. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/schema/host_bridge.py +0 -0
  188. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/runtime/schema/safe_code_settings.py +0 -0
  189. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/serializer/adapter/__init__.py +0 -0
  190. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/serializer/adapter/binary_serializer.py +0 -0
  191. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/serializer/adapter/dictionary_serializer.py +0 -0
  192. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/serializer/adapter/json_serializer.py +0 -0
  193. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/serializer/port/__init__.py +0 -0
  194. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/serializer/port/serializer.py +0 -0
  195. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/__init__.py +0 -0
  196. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/__init__.py +0 -0
  197. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/null_telemetry.py +0 -0
  198. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/open_telemetry.py +0 -0
  199. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/open_telemetry_logger_provider.py +0 -0
  200. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/open_telemetry_meter_provider.py +0 -0
  201. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/open_telemetry_provider.py +0 -0
  202. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/adapter/open_telemetry_tracer.py +0 -0
  203. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/port/__init__.py +0 -0
  204. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/port/logger_provider.py +0 -0
  205. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/port/meter_provider.py +0 -0
  206. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/port/telemetry.py +0 -0
  207. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/port/trace_provider.py +0 -0
  208. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore/telemetry/port/tracer.py +0 -0
  209. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore.egg-info/dependency_links.txt +0 -0
  210. {pycraftcore-2.0.0 → pycraftcore-2.1.0}/src/pycraftcore.egg-info/top_level.txt +0 -0
@@ -0,0 +1,418 @@
1
+ Metadata-Version: 2.4
2
+ Name: pycraftcore
3
+ Version: 2.1.0
4
+ Summary: Provides reusable technical infrastructure and adapter implementations for Python applications
5
+ Author-email: Toufik Saouchi <toufik.saouchi@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/s-toufik/pycraftcore
8
+ Project-URL: Repository, https://github.com/s-toufik/pycraftcore
9
+ Project-URL: Issues, https://github.com/s-toufik/pycraftcore/issues
10
+ Keywords: infrastructure,hexagonal-architecture,configuration,logging,opentelemetry,resilient-http,repository,sandbox,asyncio
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Framework :: AsyncIO
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.14
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Provides-Extra: authentication
24
+ Provides-Extra: context
25
+ Provides-Extra: logger
26
+ Provides-Extra: profiler
27
+ Requires-Dist: pyinstrument; extra == "profiler"
28
+ Provides-Extra: query-language
29
+ Requires-Dist: sqlglot; extra == "query-language"
30
+ Provides-Extra: computation-engine
31
+ Requires-Dist: numpy; extra == "computation-engine"
32
+ Requires-Dist: scipy; extra == "computation-engine"
33
+ Provides-Extra: serializer
34
+ Requires-Dist: orjson; extra == "serializer"
35
+ Requires-Dist: msgpack; extra == "serializer"
36
+ Requires-Dist: pydantic>=2; extra == "serializer"
37
+ Provides-Extra: file-handler
38
+ Requires-Dist: orjson; extra == "file-handler"
39
+ Requires-Dist: pyyaml; extra == "file-handler"
40
+ Provides-Extra: runtime
41
+ Requires-Dist: pydantic>=2; extra == "runtime"
42
+ Provides-Extra: circuit-breaker
43
+ Requires-Dist: aiobreaker; extra == "circuit-breaker"
44
+ Requires-Dist: pycraftcore[logger]; extra == "circuit-breaker"
45
+ Provides-Extra: retry
46
+ Requires-Dist: tenacity; extra == "retry"
47
+ Requires-Dist: pycraftcore[logger]; extra == "retry"
48
+ Provides-Extra: http
49
+ Requires-Dist: aiohttp; extra == "http"
50
+ Requires-Dist: httpx; extra == "http"
51
+ Requires-Dist: orjson; extra == "http"
52
+ Requires-Dist: starlette; extra == "http"
53
+ Requires-Dist: pycraftcore[context,logger]; extra == "http"
54
+ Provides-Extra: resilient-http
55
+ Requires-Dist: aiohttp; extra == "resilient-http"
56
+ Requires-Dist: httpx; extra == "resilient-http"
57
+ Requires-Dist: pycraftcore[circuit_breaker,http,logger,retry,telemetry]; extra == "resilient-http"
58
+ Provides-Extra: application-configuration
59
+ Requires-Dist: omegaconf; extra == "application-configuration"
60
+ Requires-Dist: pydantic>=2; extra == "application-configuration"
61
+ Requires-Dist: python-dotenv; extra == "application-configuration"
62
+ Requires-Dist: pycraftcore[authentication,http,logger]; extra == "application-configuration"
63
+ Provides-Extra: telemetry
64
+ Requires-Dist: opentelemetry-api; extra == "telemetry"
65
+ Requires-Dist: opentelemetry-sdk; extra == "telemetry"
66
+ Requires-Dist: opentelemetry-exporter-otlp; extra == "telemetry"
67
+ Requires-Dist: opentelemetry-instrumentation-logging; extra == "telemetry"
68
+ Requires-Dist: pycraftcore[application_configuration,context]; extra == "telemetry"
69
+ Provides-Extra: repository
70
+ Requires-Dist: aiosqlite; extra == "repository"
71
+ Requires-Dist: pymongo; extra == "repository"
72
+ Requires-Dist: psycopg[binary]; extra == "repository"
73
+ Requires-Dist: psycopg-pool; extra == "repository"
74
+ Requires-Dist: orjson; extra == "repository"
75
+ Requires-Dist: pydantic>=2; extra == "repository"
76
+ Requires-Dist: pycraftcore[application_configuration,authentication]; extra == "repository"
77
+ Provides-Extra: runtime-sandbox
78
+ Requires-Dist: pandas; extra == "runtime-sandbox"
79
+ Requires-Dist: numpy; extra == "runtime-sandbox"
80
+ Requires-Dist: matplotlib; extra == "runtime-sandbox"
81
+ Requires-Dist: python-dateutil; extra == "runtime-sandbox"
82
+ Requires-Dist: pycraftcore[runtime]; extra == "runtime-sandbox"
83
+ Provides-Extra: all
84
+ Requires-Dist: pycraftcore[application_configuration]; extra == "all"
85
+ Requires-Dist: pycraftcore[authentication]; extra == "all"
86
+ Requires-Dist: pycraftcore[circuit_breaker]; extra == "all"
87
+ Requires-Dist: pycraftcore[computation_engine]; extra == "all"
88
+ Requires-Dist: pycraftcore[context]; extra == "all"
89
+ Requires-Dist: pycraftcore[file_handler]; extra == "all"
90
+ Requires-Dist: pycraftcore[http]; extra == "all"
91
+ Requires-Dist: pycraftcore[logger]; extra == "all"
92
+ Requires-Dist: pycraftcore[profiler]; extra == "all"
93
+ Requires-Dist: pycraftcore[query_language]; extra == "all"
94
+ Requires-Dist: pycraftcore[repository]; extra == "all"
95
+ Requires-Dist: pycraftcore[resilient_http]; extra == "all"
96
+ Requires-Dist: pycraftcore[retry]; extra == "all"
97
+ Requires-Dist: pycraftcore[runtime]; extra == "all"
98
+ Requires-Dist: pycraftcore[runtime-sandbox]; extra == "all"
99
+ Requires-Dist: pycraftcore[serializer]; extra == "all"
100
+ Requires-Dist: pycraftcore[telemetry]; extra == "all"
101
+ Dynamic: license-file
102
+
103
+ # pycraftcore
104
+
105
+ The shared technical foundation of the homelab's Python services: configuration, logging, telemetry, resilient HTTP, database repositories, safe SQL and a sandbox for running untrusted Python. Small, swappable building blocks rather than a framework — every module is an interface (a port) plus the implementations behind it (adapters).
106
+
107
+ **[Using pycraftcore](#using-pycraftcore)** — install it and use each module in your service.
108
+ **[Working on pycraftcore](#working-on-pycraftcore)** — how it is organised and how to release it.
109
+
110
+ ```text
111
+ your service (agent-orchestrator, agent-toolbox, …)
112
+ │
113
+ ▼
114
+ ┌─ pycraftcore ─────────────────────────────────────────────────────────────┐
115
+ │ │
116
+ │ set up application_configuration · logger · telemetry · context │
117
+ │ talk out http · resilient_http (retry + circuit_breaker) │
118
+ │ store repository: SQLite · PostgreSQL · MongoDB │
119
+ │ run safely query_language (read-only SQL) · runtime (Python sandbox) │
120
+ │ utilities file_handler · serializer · computation_engine · profiler │
121
+ │ … new modules, same port + adapter shape │
122
+ │ │
123
+ └───────────────────────────────────────────────────────────────────────────┘
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Using pycraftcore
129
+
130
+ ### Install
131
+
132
+ Requires Python 3.14. Each module's third-party dependencies sit behind an extra of the same name, so a service installs only what it uses:
133
+
134
+ ```bash
135
+ uv add "pycraftcore[http,repository]" # or: pip install "pycraftcore[http,repository]"
136
+ uv add "pycraftcore[all]" # every module
137
+ ```
138
+
139
+ A bare `pycraftcore` installs no third-party packages: only `authentication`, `context` and `logger` work without an extra. For any other module, importing its adapters without the extra fails with `ModuleNotFoundError`.
140
+
141
+ | Extra | Third-party packages | Also brings in |
142
+ |---|---|---|
143
+ | `authentication`, `context`, `logger` | none | — |
144
+ | `profiler` | pyinstrument | — |
145
+ | `query_language` | sqlglot | — |
146
+ | `computation_engine` | numpy, scipy | — |
147
+ | `serializer` | orjson, msgpack, pydantic | — |
148
+ | `file_handler` | orjson, pyyaml | — |
149
+ | `runtime` | pydantic | — |
150
+ | `runtime-sandbox` | pandas, numpy, matplotlib, python-dateutil | `runtime` |
151
+ | `circuit_breaker` | aiobreaker | `logger` |
152
+ | `retry` | tenacity | `logger` |
153
+ | `http` | aiohttp, httpx, orjson, starlette | `context`, `logger` |
154
+ | `application_configuration` | omegaconf, pydantic, python-dotenv | `http`, `logger`, `authentication` |
155
+ | `telemetry` | opentelemetry (api, sdk, otlp exporter, logging instrumentation) | `application_configuration`, `context` |
156
+ | `resilient_http` | aiohttp, httpx | `circuit_breaker`, `http`, `retry`, `telemetry` |
157
+ | `repository` | aiosqlite, pymongo, psycopg, psycopg-pool, orjson, pydantic | `application_configuration`, `authentication` |
158
+
159
+ List every module your code imports, even when one extra already brings in another. That way your service stays correct if these links change. For the Python sandbox, use `runtime-sandbox` rather than `runtime`: the sandboxed code runs on your service's interpreter, so the libraries it may import (pandas, numpy, matplotlib, dateutil) must be installed there.
160
+
161
+ Development builds are published to TestPyPI; use them for testing only. Their dependencies come from PyPI, so add it as an extra index:
162
+
163
+ ```bash
164
+ pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "pycraftcore[http]"
165
+ ```
166
+
167
+ ### What's inside
168
+
169
+ Each module stands on its own: use the ones you need. The modules today (new ones follow the same shape, see [Working on pycraftcore](#working-on-pycraftcore)):
170
+
171
+ | Package | What it gives you |
172
+ |---|---|
173
+ | `application_configuration` | YAML configuration (connectors and operations) driven by environment variables, loaded into typed objects |
174
+ | `authentication` | The auth models a connector can use: none, basic, token |
175
+ | `logger` | One log format for the whole process, with the request id on every line |
176
+ | `context` | `request_id_context`, the request id shared by logs, telemetry and outgoing calls |
177
+ | `telemetry` | OpenTelemetry traces, metrics and logs, exported to an OTLP collector (or nothing) |
178
+ | `http` | Async HTTP clients (httpx, aiohttp), request-id and request-context middlewares, retry policies |
179
+ | `resilient_http` | An HTTP transport with retries and a circuit breaker built in |
180
+ | `retry`, `circuit_breaker` | The two policies on their own, for any async call |
181
+ | `repository` | Async repositories built from a database connector — today SQLite (pooled), PostgreSQL and MongoDB |
182
+ | `query_language` | Checks that a SQL statement is read-only, and translates between dialects, before it reaches a database |
183
+ | `runtime` | Runs untrusted Python in a separate process with memory, time and module limits; optionally lets it call your functions |
184
+ | `file_handler` | Read and write files by extension (today csv, json, yml, md, txt, svg); a new format is a new reader/writer pair |
185
+ | `serializer` | JSON, dict and binary (msgpack) serialisation of dataclasses |
186
+ | `computation_engine` | Numerical helpers: financial arithmetic, integration, interpolation |
187
+ | `profiler` | `@profiled`, an async profiling decorator |
188
+
189
+ ### Configuration
190
+
191
+ A service's configuration is a folder of YAML files, with values taken from environment variables (`${oc.env:NAME}`):
192
+
193
+ | Path | What it holds |
194
+ |---|---|
195
+ | `root.yml` | The environment and the list of connectors and operations in use |
196
+ | `<env>/connector/*.yml` | Where things are: APIs, databases, files, MCP servers, telemetry |
197
+ | `<env>/operation/*.yml` | What you do with them; each operation points at a connector |
198
+
199
+ ```yaml
200
+ # <env>/connector/api.yml
201
+ connector:
202
+ weather:
203
+ name: weather
204
+ type: api
205
+ base_url: https://<api-host>
206
+ timeout: 5
207
+ retry: 3
208
+ auth:
209
+ type: token
210
+ key_name: apikey
211
+ key_value: ${oc.env:WEATHER_API_KEY}
212
+ ```
213
+
214
+ ```yaml
215
+ # <env>/operation/weather.yml
216
+ operation:
217
+ forecast:
218
+ name: forecast
219
+ type: api
220
+ connector: ${connector.weather}
221
+ endpoint: /forecast
222
+ method: GET
223
+ parameters:
224
+ days: 3
225
+ ```
226
+
227
+ ```yaml
228
+ # root.yml
229
+ application_configuration:
230
+ env: ${oc.env:APP_ENV,debug}
231
+ run: async
232
+ connector:
233
+ api:
234
+ weather: ${connector.weather}
235
+ operation:
236
+ forecast: ${operation.forecast}
237
+ ```
238
+
239
+ ```python
240
+ from pathlib import Path
241
+
242
+ from pycraftcore.application_configuration.adapter import (
243
+ LoadApplicationConfiguration,
244
+ OmegaConfigurationReader,
245
+ )
246
+ from pycraftcore.application_configuration.enum import RunTypeEnvironment
247
+ from pycraftcore.logger.adapter import StandardLogger
248
+
249
+ reader = OmegaConfigurationReader(RunTypeEnvironment.debug, Path("config"))
250
+ config = LoadApplicationConfiguration(reader, StandardLogger()).load()
251
+
252
+ connector = config.connector.api("weather") # ApiConnector
253
+ operation = config.operation.api("forecast") # ApiOperation
254
+ ```
255
+
256
+ `config.connector` returns typed connectors per kind (`.api()`, `.database()`, `.file()`, `.mcp()`, `.telemetry()`) and `config.operation` typed operations (`.api()`, `.file()`); the `type` field of each entry decides which fields it takes.
257
+
258
+ ### Logging and the request id
259
+
260
+ ```python
261
+ from pycraftcore.logger import configure_logging
262
+ from pycraftcore.logger.adapter import StandardLogger
263
+
264
+ configure_logging("INFO")
265
+ logger = StandardLogger()
266
+ logger.info("booted")
267
+ ```
268
+
269
+ ```text
270
+ 2026-09-27 10:06:17.679 | INFO | 1bddfefb-… | controller:execute:51 - stream request accepted
271
+ ```
272
+
273
+ - The third column is `request_id_context`. In a web service, `RequestIDMiddleware` (`pycraftcore.http.middleware`) sets it from the `X-Request-ID` header; every line logged during that request shows it, `-` otherwise. Don't write it in your messages.
274
+ - Uvicorn logs use the same format; chatty libraries (httpx, mcp, pymongo…) stay at `WARNING`.
275
+ - Calling `configure_logging` again replaces the handler rather than adding a second one.
276
+
277
+ ### Telemetry
278
+
279
+ ```python
280
+ from pycraftcore.application_configuration.enum import RunTypeEnvironment
281
+ from pycraftcore.telemetry.adapter import OpenTelemetryProvider
282
+
283
+ telemetry = OpenTelemetryProvider(
284
+ service_name="my-service",
285
+ environment=RunTypeEnvironment.debug,
286
+ otlp_endpoint="<collector-host>:<port>", # None: nothing is exported
287
+ )
288
+ tracer = telemetry.tracer("my-service")
289
+ ```
290
+
291
+ Spans and log records carry the request id as an attribute, so logs and traces line up in Grafana.
292
+
293
+ ### Resilient HTTP
294
+
295
+ A transport that retries what is worth retrying and stops calling a backend that keeps failing:
296
+
297
+ ```python
298
+ from pycraftcore.circuit_breaker.configuration import CircuitBreakerSettings
299
+ from pycraftcore.http.configuration import HttpClientSettings, LimitsSettings
300
+ from pycraftcore.http.policy.http_error_policy import is_business_error, is_retryable
301
+ from pycraftcore.resilient_http.adapter import ResilientTransportFactory
302
+ from pycraftcore.resilient_http.configuration import ResilientHttpSettings
303
+ from pycraftcore.retry.configuration import RetrySettings
304
+
305
+ settings = ResilientHttpSettings(
306
+ http=HttpClientSettings(limits=LimitsSettings(timeout=10)),
307
+ retry=RetrySettings(
308
+ retry_count=3, retry_delay=1, max_retry_delay=20, should_retry=is_retryable
309
+ ),
310
+ circuit_breaker=CircuitBreakerSettings(
311
+ failure_threshold=3, recovery_timeout=30, is_excluded=is_business_error, name="weather"
312
+ ),
313
+ )
314
+ client = ResilientTransportFactory(
315
+ settings=settings, trace_manager=tracer, logger=logger
316
+ ).create_async_client()
317
+ ```
318
+
319
+ `client` is an `httpx.AsyncClient`: pass it to any library that accepts one.
320
+
321
+ ### Databases
322
+
323
+ Repositories are built from a database connector and share one interface, `AsyncRepository.execute(statement)`:
324
+
325
+ ```python
326
+ from pycraftcore.repository.adapter import SqliteRepositoryFactory, SqliteSettingsMapper
327
+
328
+ factory = SqliteRepositoryFactory(SqliteSettingsMapper(config.connector.database("users"))())
329
+ repository = await factory.connect()
330
+ rows = await repository.execute("SELECT id, email FROM users LIMIT 10")
331
+ await factory.disconnect()
332
+ ```
333
+
334
+ `PostgresRepositoryFactory` / `PostgresSettingsMapper` and `MongoRepositoryFactory` / `MongoSettingsMapper` work the same way, and so will any database added later: a factory, a settings mapper and a repository behind the same `AsyncRepository` port.
335
+
336
+ ### Read-only SQL
337
+
338
+ ```python
339
+ from pycraftcore.query_language.adapter import SqlHandlerFactory
340
+
341
+ statement = SqlHandlerFactory()("SELECT * FROM users", dialect="sqlite").transpile()
342
+ ```
343
+
344
+ `transpile()` raises when the statement writes, deletes or changes the schema, so only reads reach the database.
345
+
346
+ ### Running untrusted Python
347
+
348
+ ```python
349
+ from pycraftcore.runtime.adapter import PythonSafeCodeFactory
350
+ from pycraftcore.runtime.schema import CodeResult, SafeCodeSettings
351
+
352
+ factory = PythonSafeCodeFactory(settings=SafeCodeSettings(code_timeout=30, max_memory_mb=256))
353
+ output = await factory(code="import math\nresult = math.sqrt(2)").execute()
354
+ print(CodeResult.from_stdout(output.stdout).value) # 1.4142135623730951
355
+ ```
356
+
357
+ - The code runs in its own process; only the modules in `PYTHON_ALLOWLIST` can be imported (pandas, numpy, matplotlib, datetime…).
358
+ - `working_directory` in `SafeCodeSettings` confines file access to one folder.
359
+ - With a `HostBridgeServer`, the code can call functions of your service as if they were local — this is how agent-toolbox lets sandboxed code use its other tools.
360
+
361
+ ---
362
+
363
+ ## Working on pycraftcore
364
+
365
+ ### How a module is organised
366
+
367
+ Every package follows the same shape, so a new backend is a new adapter, never a change for the services that use the port:
368
+
369
+ ```text
370
+ pycraftcore/<module>/
371
+ ├── port/ Protocols the services depend on (AsyncRepository, Code, Logger…)
372
+ ├── adapter/ Implementations (SqliteRepository, PythonSafeCode, StandardLogger…)
373
+ ├── configuration/ Pydantic settings objects (RetrySettings, CircuitBreakerSettings…)
374
+ ├── schema/ Data passed across the port (CodeResult, SafeCodeSettings…)
375
+ └── enum/ Fixed choices (ConnectorType, HttpMethod…)
376
+ ```
377
+
378
+ Not every module needs every folder: add a folder when it has something to hold.
379
+
380
+ ### Design rules
381
+
382
+ - Services import the **port** and receive an adapter; they never construct a third-party client themselves.
383
+ - Adapters are thin: they translate between the port and the library, and keep library types out of the port.
384
+ - Settings are validated pydantic models; nothing reads environment variables except the configuration loader.
385
+ - Every public module is typed: `ty` runs in strict mode.
386
+
387
+ ### Adding a module or an adapter
388
+
389
+ 1. Define the protocol in `<module>/port/`.
390
+ 2. Implement it in `<module>/adapter/`, with its settings in `configuration/` and the data it exchanges in `schema/`.
391
+ 3. Export the public names from the package's `__init__.py`.
392
+ 4. Declare its third-party packages as an extra named after the module in `[project.optional-dependencies]` (pull in other modules with `pycraftcore[<module>]`). Then add it to `all`, to the [Install](#install) table, and to the `extras` matrix in `.github/workflows/cicd.yml`, which installs each extra alone and imports its package.
393
+ 5. Add tests under `tests/<module>/`, and the module to [What's inside](#whats-inside) — with a short example under Using pycraftcore if services will use it directly.
394
+
395
+ ### Tests and checks
396
+
397
+ ```bash
398
+ make install_dev # uv sync --group dev --all-extras
399
+ make check # ruff + ty + pytest
400
+ make test
401
+ make lint / make fix / make format / make typecheck
402
+ uv run pytest --cov=pycraftcore --cov-report=term-missing # coverage per module
403
+ ```
404
+
405
+ Pre-commit runs the test suite before every commit and push:
406
+
407
+ ```bash
408
+ uv run pre-commit install --hook-type pre-commit --hook-type pre-push
409
+ ```
410
+
411
+ ### Releasing
412
+
413
+ 1. Bump `version` in `pyproject.toml`.
414
+ 2. `make build`.
415
+ 3. `make publish_dev` to TestPyPI (needs `TEST_PYPI_TOKEN`), try it in a service, then `make publish` to PyPI (needs `RELEASE_PYPI_TOKEN`).
416
+ 4. Raise the minimum version in the services that need the change (`pycraftcore[<extras>]>=…` in their `pyproject.toml`), then `uv lock --upgrade-package pycraftcore`.
417
+
418
+ To work on pycraftcore and a service at the same time, point the service at your checkout with an editable path dependency in its `pyproject.toml`: `pycraftcore = { path = "../pycraftcore", editable = true }` under `[tool.uv.sources]`.