loom-kernel 0.0.0__tar.gz → 0.2.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 (372) hide show
  1. loom_kernel-0.2.0/.github/workflows/ci-main.yml +113 -0
  2. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/.github/workflows/ci-pr.yml +78 -19
  3. loom_kernel-0.2.0/.github/workflows/docs.yml +82 -0
  4. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/.github/workflows/release.yml +8 -8
  5. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/.gitignore +7 -2
  6. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/.pre-commit-config.yaml +8 -1
  7. loom_kernel-0.2.0/.readthedocs.yaml +15 -0
  8. loom_kernel-0.2.0/CHANGELOG.md +120 -0
  9. loom_kernel-0.2.0/CHANGELOG_RELEASE.md +125 -0
  10. loom_kernel-0.2.0/PKG-INFO +459 -0
  11. loom_kernel-0.2.0/README.md +424 -0
  12. loom_kernel-0.2.0/codecov.yml +28 -0
  13. loom_kernel-0.2.0/docs/_static/custom.css +11 -0
  14. loom_kernel-0.2.0/docs/_static/logo-transparent.png +0 -0
  15. loom_kernel-0.2.0/docs/_static/logo.svg +4 -0
  16. loom_kernel-0.2.0/docs/architecture/adr/README.md +8 -0
  17. loom_kernel-0.2.0/docs/architecture/clean-architecture.md +10 -0
  18. loom_kernel-0.2.0/docs/architecture/overview.md +9 -0
  19. loom_kernel-0.2.0/docs/conf.py +95 -0
  20. loom_kernel-0.2.0/docs/examples-repo/index.md +477 -0
  21. loom_kernel-0.2.0/docs/guides/autocrud.md +166 -0
  22. loom_kernel-0.2.0/docs/guides/celery.md +538 -0
  23. loom_kernel-0.2.0/docs/guides/fake-repo-examples.md +28 -0
  24. loom_kernel-0.2.0/docs/guides/quickstart.md +405 -0
  25. loom_kernel-0.2.0/docs/guides/use-case-dsl.md +474 -0
  26. loom_kernel-0.2.0/docs/index.rst +41 -0
  27. loom_kernel-0.2.0/docs/reference/api/core.rst +32 -0
  28. loom_kernel-0.2.0/docs/reference/api/repository.rst +31 -0
  29. loom_kernel-0.2.0/docs/reference/api/rest.rst +24 -0
  30. loom_kernel-0.2.0/docs/reference/api/testing.rst +10 -0
  31. loom_kernel-0.2.0/docs/reference/index.rst +13 -0
  32. loom_kernel-0.2.0/docs/requirements.txt +5 -0
  33. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/pyproject.toml +35 -6
  34. loom_kernel-0.2.0/sonar-project.properties +131 -0
  35. loom_kernel-0.2.0/src/loom/__init__.py +1 -0
  36. loom_kernel-0.2.0/src/loom/celery/__init__.py +20 -0
  37. loom_kernel-0.2.0/src/loom/celery/auto.py +45 -0
  38. loom_kernel-0.2.0/src/loom/celery/bootstrap.py +776 -0
  39. loom_kernel-0.2.0/src/loom/celery/config.py +199 -0
  40. loom_kernel-0.2.0/src/loom/celery/constants.py +22 -0
  41. loom_kernel-0.2.0/src/loom/celery/event_loop.py +158 -0
  42. loom_kernel-0.2.0/src/loom/celery/runner.py +398 -0
  43. loom_kernel-0.2.0/src/loom/celery/service.py +406 -0
  44. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/backend/__init__.py +6 -0
  45. loom_kernel-0.2.0/src/loom/core/backend/core_model.py +426 -0
  46. loom_kernel-0.2.0/src/loom/core/backend/sqlalchemy.py +909 -0
  47. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/bootstrap/__init__.py +8 -1
  48. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/bootstrap/bootstrap.py +15 -5
  49. loom_kernel-0.2.0/src/loom/core/bootstrap/kernel.py +100 -0
  50. loom_kernel-0.2.0/src/loom/core/cache/abc/config.py +135 -0
  51. loom_kernel-0.2.0/src/loom/core/cache/gateway.py +280 -0
  52. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/cache/repository.py +154 -21
  53. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/cache/serializer.py +1 -0
  54. loom_kernel-0.2.0/src/loom/core/command/base.py +151 -0
  55. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/command/field.py +1 -0
  56. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/command/introspection.py +3 -15
  57. loom_kernel-0.2.0/src/loom/core/config/keys.py +24 -0
  58. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/config/loader.py +92 -29
  59. loom_kernel-0.2.0/src/loom/core/contracts/__init__.py +1 -0
  60. loom_kernel-0.2.0/src/loom/core/contracts/manifest.py +38 -0
  61. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/di/container.py +43 -28
  62. loom_kernel-0.2.0/src/loom/core/discovery/_utils.py +176 -0
  63. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/discovery/interfaces.py +17 -11
  64. loom_kernel-0.2.0/src/loom/core/discovery/manifest.py +87 -0
  65. loom_kernel-0.2.0/src/loom/core/discovery/modules.py +45 -0
  66. loom_kernel-0.2.0/src/loom/core/engine/compilable.py +42 -0
  67. loom_kernel-0.2.0/src/loom/core/engine/compiler.py +436 -0
  68. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/engine/events.py +5 -0
  69. loom_kernel-0.2.0/src/loom/core/engine/executor.py +673 -0
  70. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/engine/plan.py +52 -6
  71. loom_kernel-0.2.0/src/loom/core/errors/codes.py +32 -0
  72. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/errors/errors.py +8 -6
  73. loom_kernel-0.2.0/src/loom/core/job/__init__.py +18 -0
  74. loom_kernel-0.2.0/src/loom/core/job/callback.py +70 -0
  75. loom_kernel-0.2.0/src/loom/core/job/context.py +69 -0
  76. loom_kernel-0.2.0/src/loom/core/job/handle.py +186 -0
  77. loom_kernel-0.2.0/src/loom/core/job/job.py +82 -0
  78. loom_kernel-0.2.0/src/loom/core/job/service.py +327 -0
  79. loom_kernel-0.2.0/src/loom/core/logger/__init__.py +39 -0
  80. loom_kernel-0.2.0/src/loom/core/logger/config.py +300 -0
  81. loom_kernel-0.2.0/src/loom/core/logger/registry.py +40 -0
  82. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/logger/structlogger.py +20 -20
  83. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/model/__init__.py +4 -1
  84. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/model/base.py +45 -23
  85. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/model/field.py +3 -3
  86. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/model/introspection.py +27 -32
  87. loom_kernel-0.2.0/src/loom/core/model/projection.py +56 -0
  88. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/model/timestamped.py +2 -1
  89. loom_kernel-0.2.0/src/loom/core/projection/__init__.py +23 -0
  90. loom_kernel-0.2.0/src/loom/core/projection/loaders.py +265 -0
  91. loom_kernel-0.2.0/src/loom/core/projection/runtime.py +291 -0
  92. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/abc/repo_for.py +32 -1
  93. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/abc/repository.py +31 -1
  94. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/__init__.py +12 -4
  95. loom_kernel-0.2.0/src/loom/core/repository/sqlalchemy/integrity.py +185 -0
  96. loom_kernel-0.2.0/src/loom/core/repository/sqlalchemy/loaders.py +134 -0
  97. loom_kernel-0.2.0/src/loom/core/repository/sqlalchemy/mixins.py +505 -0
  98. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/query_compiler/compiler.py +44 -40
  99. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/query_compiler/errors.py +1 -3
  100. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/query_compiler/filters.py +17 -23
  101. loom_kernel-0.2.0/src/loom/core/repository/sqlalchemy/registry.py +125 -0
  102. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/session_manager.py +2 -2
  103. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/uow.py +4 -3
  104. loom_kernel-0.2.0/src/loom/core/response/__init__.py +5 -0
  105. loom_kernel-0.2.0/src/loom/core/response/base.py +17 -0
  106. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/transport/adapter.py +1 -1
  107. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/uow/abc.py +6 -1
  108. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/uow/context.py +1 -3
  109. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/use_case/__init__.py +6 -3
  110. loom_kernel-0.2.0/src/loom/core/use_case/_predicates.py +61 -0
  111. loom_kernel-0.2.0/src/loom/core/use_case/compute.py +120 -0
  112. loom_kernel-0.2.0/src/loom/core/use_case/constants.py +27 -0
  113. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/use_case/factory.py +11 -6
  114. loom_kernel-0.2.0/src/loom/core/use_case/field_ref.py +83 -0
  115. loom_kernel-0.2.0/src/loom/core/use_case/invoker.py +120 -0
  116. loom_kernel-0.2.0/src/loom/core/use_case/keys.py +55 -0
  117. loom_kernel-0.2.0/src/loom/core/use_case/markers.py +235 -0
  118. loom_kernel-0.2.0/src/loom/core/use_case/registry.py +88 -0
  119. loom_kernel-0.2.0/src/loom/core/use_case/rule.py +194 -0
  120. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/use_case/use_case.py +14 -6
  121. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/prometheus/adapter.py +64 -41
  122. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/prometheus/middleware.py +19 -27
  123. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/rest/adapter.py +4 -8
  124. loom_kernel-0.2.0/src/loom/rest/autocrud.py +457 -0
  125. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/rest/compiler.py +43 -3
  126. loom_kernel-0.2.0/src/loom/rest/constants.py +29 -0
  127. loom_kernel-0.2.0/src/loom/rest/errors.py +98 -0
  128. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/rest/fastapi/app.py +53 -3
  129. loom_kernel-0.2.0/src/loom/rest/fastapi/auto.py +455 -0
  130. loom_kernel-0.2.0/src/loom/rest/fastapi/openapi.py +376 -0
  131. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/rest/fastapi/router_runtime.py +188 -42
  132. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/rest/middleware.py +4 -0
  133. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/rest/model.py +55 -1
  134. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/rest/rest_adapter.py +1 -1
  135. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/testing/golden.py +17 -1
  136. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/testing/http_harness.py +5 -6
  137. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/testing/in_memory.py +97 -6
  138. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/testing/repository_harness.py +6 -1
  139. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/testing/runner.py +5 -5
  140. loom_kernel-0.2.0/tests/integration/celery_bootstrap/config/conf.celery.integration.yaml +51 -0
  141. loom_kernel-0.2.0/tests/integration/celery_bootstrap/test_auto_create_app_integration.py +402 -0
  142. loom_kernel-0.2.0/tests/integration/celery_bootstrap/test_bootstrap_worker.py +186 -0
  143. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/core/repository/sqlalchemy/test_cache_integration.py +28 -8
  144. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/core/repository/sqlalchemy/test_repository_integration.py +90 -2
  145. loom_kernel-0.2.0/tests/integration/core/rest/test_auto_interface_integration.py +227 -0
  146. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/core/rest/test_fastapi_app_integration.py +23 -10
  147. loom_kernel-0.2.0/tests/integration/core/use_case/test_custom_repository_integration.py +103 -0
  148. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/core/use_case/test_use_case_crud_integration.py +6 -10
  149. loom_kernel-0.2.0/tests/integration/fake_repo/config/conf.interfaces.yaml +14 -0
  150. loom_kernel-0.2.0/tests/integration/fake_repo/config/conf.manifest.yaml +8 -0
  151. loom_kernel-0.2.0/tests/integration/fake_repo/config/conf.modules.yaml +15 -0
  152. loom_kernel-0.0.0/tests/integration/fake_repo/src/config/conf.manifest.yaml → loom_kernel-0.2.0/tests/integration/fake_repo/config/conf.yaml +1 -5
  153. loom_kernel-0.2.0/tests/integration/fake_repo/main.py +25 -0
  154. loom_kernel-0.2.0/tests/integration/fake_repo/manifest.py +28 -0
  155. {loom_kernel-0.0.0/tests/integration/fake_repo/src/app → loom_kernel-0.2.0/tests/integration/fake_repo}/product/interface.py +9 -4
  156. loom_kernel-0.2.0/tests/integration/fake_repo/product/jobs.py +17 -0
  157. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/fake_repo/product/model.py +4 -11
  158. loom_kernel-0.2.0/tests/integration/fake_repo/product/repository.py +37 -0
  159. loom_kernel-0.2.0/tests/integration/fake_repo/product/repository_contract.py +14 -0
  160. loom_kernel-0.2.0/tests/integration/fake_repo/product/use_cases.py +162 -0
  161. loom_kernel-0.2.0/tests/unit/celery_bootstrap/test_bootstrap.py +436 -0
  162. loom_kernel-0.2.0/tests/unit/celery_bootstrap/test_event_loop.py +182 -0
  163. loom_kernel-0.2.0/tests/unit/celery_jobs/test_auto.py +73 -0
  164. loom_kernel-0.2.0/tests/unit/celery_jobs/test_celery_service.py +418 -0
  165. loom_kernel-0.2.0/tests/unit/celery_jobs/test_config.py +242 -0
  166. loom_kernel-0.2.0/tests/unit/celery_jobs/test_runner.py +599 -0
  167. loom_kernel-0.2.0/tests/unit/core/backend/test_backend_compiler.py +246 -0
  168. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/bootstrap/test_bootstrap.py +8 -0
  169. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/bootstrap/test_bootstrap_metrics.py +20 -14
  170. loom_kernel-0.2.0/tests/unit/core/bootstrap/test_kernel.py +58 -0
  171. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/cache/test_cached_repository.py +142 -0
  172. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/command/test_command_base.py +13 -10
  173. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/command/test_command_patch.py +1 -3
  174. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/config/test_config.py +96 -0
  175. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/di/test_container.py +15 -0
  176. loom_kernel-0.2.0/tests/unit/core/discovery/test_manifest.py +50 -0
  177. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/engine/test_compiler.py +79 -12
  178. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/engine/test_executor.py +277 -5
  179. loom_kernel-0.2.0/tests/unit/core/engine/test_executor_uow.py +282 -0
  180. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/engine/test_metrics.py +2 -2
  181. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/engine/test_plan.py +50 -6
  182. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/errors/test_errors.py +10 -9
  183. loom_kernel-0.2.0/tests/unit/core/job/conftest.py +23 -0
  184. loom_kernel-0.2.0/tests/unit/core/job/test_callback.py +79 -0
  185. loom_kernel-0.2.0/tests/unit/core/job/test_context.py +86 -0
  186. loom_kernel-0.2.0/tests/unit/core/job/test_handle.py +154 -0
  187. loom_kernel-0.2.0/tests/unit/core/job/test_inline_service.py +171 -0
  188. loom_kernel-0.2.0/tests/unit/core/job/test_job.py +124 -0
  189. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/logger/test_registry.py +7 -7
  190. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/model/test_timestamped.py +7 -2
  191. loom_kernel-0.2.0/tests/unit/core/projection/test_runtime.py +191 -0
  192. loom_kernel-0.2.0/tests/unit/core/repository/sqlalchemy/test_loaders.py +113 -0
  193. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/repository/sqlalchemy/test_repository.py +89 -1
  194. loom_kernel-0.2.0/tests/unit/core/tracing/__init__.py +0 -0
  195. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/tracing/test_context.py +2 -1
  196. loom_kernel-0.2.0/tests/unit/core/uow/__init__.py +0 -0
  197. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/uow/test_executor_uow.py +13 -17
  198. loom_kernel-0.2.0/tests/unit/core/use_case/__init__.py +0 -0
  199. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/use_case/test_compute.py +48 -10
  200. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/use_case/test_field_ref.py +9 -1
  201. loom_kernel-0.2.0/tests/unit/core/use_case/test_invoker.py +152 -0
  202. loom_kernel-0.2.0/tests/unit/core/use_case/test_markers.py +54 -0
  203. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/use_case/test_rule.py +77 -7
  204. loom_kernel-0.2.0/tests/unit/prometheus/__init__.py +0 -0
  205. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/prometheus/test_adapter.py +8 -7
  206. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/prometheus/test_middleware.py +30 -8
  207. loom_kernel-0.2.0/tests/unit/rest/__init__.py +0 -0
  208. loom_kernel-0.2.0/tests/unit/rest/test_autocrud.py +363 -0
  209. loom_kernel-0.2.0/tests/unit/rest/test_fastapi_auto_logger.py +128 -0
  210. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/rest/test_pydantic_adapter.py +2 -6
  211. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/rest/test_rest_adapter.py +2 -1
  212. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/rest/test_rest_compiler.py +86 -0
  213. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/rest/test_rest_model.py +74 -0
  214. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/rest/test_router_runtime.py +302 -2
  215. loom_kernel-0.2.0/tests/unit/testing/__init__.py +0 -0
  216. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/testing/test_golden.py +30 -2
  217. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/testing/test_in_memory.py +50 -0
  218. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/testing/test_runner.py +2 -2
  219. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/uv.lock +295 -15
  220. loom_kernel-0.0.0/.github/workflows/ci-main.yml +0 -49
  221. loom_kernel-0.0.0/CHANGELOG_RELEASE.md +0 -3
  222. loom_kernel-0.0.0/PKG-INFO +0 -165
  223. loom_kernel-0.0.0/README.md +0 -141
  224. loom_kernel-0.0.0/src/loom/__init__.py +0 -1
  225. loom_kernel-0.0.0/src/loom/core/backend/sqlalchemy.py +0 -281
  226. loom_kernel-0.0.0/src/loom/core/cache/abc/config.py +0 -59
  227. loom_kernel-0.0.0/src/loom/core/cache/gateway.py +0 -151
  228. loom_kernel-0.0.0/src/loom/core/command/base.py +0 -60
  229. loom_kernel-0.0.0/src/loom/core/discovery/_utils.py +0 -77
  230. loom_kernel-0.0.0/src/loom/core/discovery/manifest.py +0 -43
  231. loom_kernel-0.0.0/src/loom/core/discovery/modules.py +0 -25
  232. loom_kernel-0.0.0/src/loom/core/engine/compiler.py +0 -207
  233. loom_kernel-0.0.0/src/loom/core/engine/executor.py +0 -384
  234. loom_kernel-0.0.0/src/loom/core/logger/__init__.py +0 -12
  235. loom_kernel-0.0.0/src/loom/core/logger/registry.py +0 -21
  236. loom_kernel-0.0.0/src/loom/core/logger/std.py +0 -86
  237. loom_kernel-0.0.0/src/loom/core/model/projection.py +0 -36
  238. loom_kernel-0.0.0/src/loom/core/repository/sqlalchemy/loaders.py +0 -235
  239. loom_kernel-0.0.0/src/loom/core/repository/sqlalchemy/mixins.py +0 -436
  240. loom_kernel-0.0.0/src/loom/core/use_case/compute.py +0 -109
  241. loom_kernel-0.0.0/src/loom/core/use_case/field_ref.py +0 -45
  242. loom_kernel-0.0.0/src/loom/core/use_case/markers.py +0 -78
  243. loom_kernel-0.0.0/src/loom/core/use_case/rule.py +0 -147
  244. loom_kernel-0.0.0/src/loom/rest/errors.py +0 -58
  245. loom_kernel-0.0.0/src/loom/rest/fastapi/auto.py +0 -203
  246. loom_kernel-0.0.0/tests/integration/fake_repo/config/conf.yaml +0 -24
  247. loom_kernel-0.0.0/tests/integration/fake_repo/main.py +0 -12
  248. loom_kernel-0.0.0/tests/integration/fake_repo/product/interface.py +0 -44
  249. loom_kernel-0.0.0/tests/integration/fake_repo/product/use_cases.py +0 -38
  250. loom_kernel-0.0.0/tests/integration/fake_repo/src/__init__.py +0 -1
  251. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/__init__.py +0 -1
  252. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/manifest.py +0 -31
  253. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/product/__init__.py +0 -3
  254. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/product/category/__init__.py +0 -1
  255. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/product/category/model.py +0 -10
  256. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/product/category/schemas.py +0 -7
  257. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/product/model.py +0 -68
  258. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/product/relations.py +0 -17
  259. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/product/review/__init__.py +0 -1
  260. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/product/review/model.py +0 -12
  261. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/product/review/schemas.py +0 -9
  262. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/product/schemas.py +0 -13
  263. loom_kernel-0.0.0/tests/integration/fake_repo/src/app/product/use_cases.py +0 -43
  264. loom_kernel-0.0.0/tests/integration/fake_repo/src/config/conf.interfaces.yaml +0 -24
  265. loom_kernel-0.0.0/tests/integration/fake_repo/src/config/conf.modules.yaml +0 -28
  266. loom_kernel-0.0.0/tests/unit/core/backend/test_backend_compiler.py +0 -85
  267. loom_kernel-0.0.0/tests/unit/core/use_case/test_markers.py +0 -26
  268. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/LICENSE +0 -0
  269. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/Makefile +0 -0
  270. {loom_kernel-0.0.0/tests/golden/baselines → loom_kernel-0.2.0/docs/_static}/.gitkeep +0 -0
  271. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/backend/protocol.py +0 -0
  272. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/cache/__init__.py +0 -0
  273. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/cache/abc/__init__.py +0 -0
  274. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/cache/abc/backend.py +0 -0
  275. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/cache/abc/dependency.py +0 -0
  276. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/cache/codec.py +0 -0
  277. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/cache/decorators.py +0 -0
  278. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/cache/dependency.py +0 -0
  279. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/cache/keys.py +0 -0
  280. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/command/__init__.py +0 -0
  281. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/command/adapter.py +0 -0
  282. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/config/__init__.py +0 -0
  283. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/config/errors.py +0 -0
  284. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/config/model.py +0 -0
  285. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/di/__init__.py +0 -0
  286. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/di/scope.py +0 -0
  287. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/discovery/__init__.py +0 -0
  288. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/discovery/base.py +0 -0
  289. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/engine/__init__.py +0 -0
  290. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/engine/metrics.py +0 -0
  291. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/errors/__init__.py +0 -0
  292. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/logger/abc.py +0 -0
  293. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/model/enums.py +0 -0
  294. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/model/relation.py +0 -0
  295. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/model/types.py +0 -0
  296. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/model/types_postgres.py +0 -0
  297. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/__init__.py +0 -0
  298. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/abc/__init__.py +0 -0
  299. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/abc/query.py +0 -0
  300. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/mutation.py +0 -0
  301. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/model.py +0 -0
  302. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/profile_loader.py +0 -0
  303. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/projection.py +0 -0
  304. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/query_compiler/__init__.py +0 -0
  305. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/query_compiler/cursor.py +0 -0
  306. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/query_compiler/ordering.py +0 -0
  307. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/query_compiler/paths.py +0 -0
  308. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/query_compiler/subquery.py +0 -0
  309. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/repository.py +0 -0
  310. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/repository/sqlalchemy/transactional.py +0 -0
  311. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/tracing/__init__.py +0 -0
  312. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/tracing/context.py +0 -0
  313. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/transport/__init__.py +0 -0
  314. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/core/uow/__init__.py +0 -0
  315. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/prometheus/__init__.py +0 -0
  316. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/rest/__init__.py +0 -0
  317. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/rest/fastapi/__init__.py +0 -0
  318. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/rest/fastapi/response.py +0 -0
  319. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/src/loom/testing/__init__.py +0 -0
  320. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/__init__.py +0 -0
  321. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/conftest.py +0 -0
  322. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/golden/__init__.py +0 -0
  323. {loom_kernel-0.0.0/tests/golden/outputs → loom_kernel-0.2.0/tests/golden/baselines}/.gitkeep +0 -0
  324. {loom_kernel-0.0.0/tests/golden/plans → loom_kernel-0.2.0/tests/golden/outputs}/.gitkeep +0 -0
  325. /loom_kernel-0.0.0/tests/integration/__init__.py → /loom_kernel-0.2.0/tests/golden/plans/.gitkeep +0 -0
  326. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/helpers/__init__.py +0 -0
  327. {loom_kernel-0.0.0/tests/integration/core → loom_kernel-0.2.0/tests/integration}/__init__.py +0 -0
  328. {loom_kernel-0.0.0/tests/integration/core/repository → loom_kernel-0.2.0/tests/integration/celery_bootstrap}/__init__.py +0 -0
  329. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/conftest.py +0 -0
  330. {loom_kernel-0.0.0/tests/integration/core/repository/sqlalchemy → loom_kernel-0.2.0/tests/integration/core}/__init__.py +0 -0
  331. {loom_kernel-0.0.0/tests/integration/fake_repo → loom_kernel-0.2.0/tests/integration/core/repository}/__init__.py +0 -0
  332. {loom_kernel-0.0.0/tests/integration/fake_repo/product/category → loom_kernel-0.2.0/tests/integration/core/repository/sqlalchemy}/__init__.py +0 -0
  333. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/core/repository/sqlalchemy/conftest.py +0 -0
  334. {loom_kernel-0.0.0/tests/integration/fake_repo/product/review → loom_kernel-0.2.0/tests/integration/core/rest}/__init__.py +0 -0
  335. {loom_kernel-0.0.0/tests/unit/core/backend → loom_kernel-0.2.0/tests/integration/fake_repo}/__init__.py +0 -0
  336. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/fake_repo/config/__init__.py +0 -0
  337. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/fake_repo/product/__init__.py +0 -0
  338. {loom_kernel-0.0.0/tests/unit/core/bootstrap → loom_kernel-0.2.0/tests/integration/fake_repo/product/category}/__init__.py +0 -0
  339. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/fake_repo/product/category/model.py +0 -0
  340. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/fake_repo/product/category/schemas.py +0 -0
  341. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/fake_repo/product/relations.py +0 -0
  342. {loom_kernel-0.0.0/tests/unit/core/command → loom_kernel-0.2.0/tests/integration/fake_repo/product/review}/__init__.py +0 -0
  343. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/fake_repo/product/review/model.py +0 -0
  344. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/fake_repo/product/review/schemas.py +0 -0
  345. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/integration/fake_repo/product/schemas.py +0 -0
  346. {loom_kernel-0.0.0/tests/unit/core/config → loom_kernel-0.2.0/tests/unit/celery_bootstrap}/__init__.py +0 -0
  347. {loom_kernel-0.0.0/tests/unit/core/di → loom_kernel-0.2.0/tests/unit/celery_jobs}/__init__.py +0 -0
  348. {loom_kernel-0.0.0/tests/unit/core/engine → loom_kernel-0.2.0/tests/unit/core/backend}/__init__.py +0 -0
  349. {loom_kernel-0.0.0/tests/unit/core/errors → loom_kernel-0.2.0/tests/unit/core/bootstrap}/__init__.py +0 -0
  350. {loom_kernel-0.0.0/tests/unit/core/model → loom_kernel-0.2.0/tests/unit/core/command}/__init__.py +0 -0
  351. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/command/test_command_field.py +0 -0
  352. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/command/test_introspection.py +0 -0
  353. {loom_kernel-0.0.0/tests/unit/core/tracing → loom_kernel-0.2.0/tests/unit/core/config}/__init__.py +0 -0
  354. {loom_kernel-0.0.0/tests/unit/core/uow → loom_kernel-0.2.0/tests/unit/core/di}/__init__.py +0 -0
  355. {loom_kernel-0.0.0/tests/unit/core/use_case → loom_kernel-0.2.0/tests/unit/core/engine}/__init__.py +0 -0
  356. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/engine/test_executor_trace.py +0 -0
  357. {loom_kernel-0.0.0/tests/unit/prometheus → loom_kernel-0.2.0/tests/unit/core/errors}/__init__.py +0 -0
  358. {loom_kernel-0.0.0/tests/unit/rest → loom_kernel-0.2.0/tests/unit/core/job}/__init__.py +0 -0
  359. {loom_kernel-0.0.0/tests/unit/testing → loom_kernel-0.2.0/tests/unit/core/model}/__init__.py +0 -0
  360. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/model/test_model.py +0 -0
  361. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/repository/abc/conftest.py +0 -0
  362. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/repository/abc/test_query.py +0 -0
  363. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/repository/abc/test_repository_contract.py +0 -0
  364. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/repository/sqlalchemy/conftest.py +0 -0
  365. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/repository/sqlalchemy/test_transactional.py +0 -0
  366. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/uow/test_sqlalchemy_uow.py +0 -0
  367. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/uow/test_uow_protocols.py +0 -0
  368. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/use_case/test_factory.py +0 -0
  369. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/core/use_case/test_use_case.py +0 -0
  370. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/rest/test_middleware.py +0 -0
  371. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/rest/test_response.py +0 -0
  372. {loom_kernel-0.0.0 → loom_kernel-0.2.0}/tests/unit/testing/test_http_harness.py +0 -0
@@ -0,0 +1,113 @@
1
+ name: ci-main
2
+
3
+ on:
4
+ push:
5
+ branches: ["master"]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ validate:
12
+ runs-on: ubuntu-latest
13
+ env:
14
+ SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
15
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
16
+ SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
17
+ SONAR_PROJECT_KEY: ${{ vars.SONAR_PROJECT_KEY }}
18
+ steps:
19
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
20
+ with:
21
+ fetch-depth: 0
22
+
23
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
24
+ with:
25
+ python-version: "3.11"
26
+
27
+ - uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
28
+
29
+ - name: Quality report (strict)
30
+ uses: the-reacher-data/loom-actions/actions/python/quality-report@e731576f4f9cf4ec2b36fddfc895d328c856d29f # v1
31
+ with:
32
+ src-dir: src
33
+ test-dir: tests
34
+ coverage-threshold: "80"
35
+ fail-on-quality: "any"
36
+ fail-on-security: "high"
37
+ include-security: "true"
38
+
39
+ - name: Run pytest with coverage and JUnit output
40
+ shell: bash
41
+ run: |
42
+ set -euo pipefail
43
+ uv run --with pytest-cov pytest --cov --junitxml=junit.xml -o junit_family=legacy
44
+
45
+ - name: Upload coverage to Codecov
46
+ if: ${{ hashFiles('coverage.xml') != '' }}
47
+ uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
48
+ with:
49
+ files: ./coverage.xml
50
+ fail_ci_if_error: false
51
+ token: ${{ secrets.CODECOV_TOKEN }}
52
+
53
+ - name: Upload test results to Codecov
54
+ if: ${{ !cancelled() && hashFiles('junit.xml') != '' }}
55
+ uses: codecov/test-results-action@0fa95f0e1eeaafde2c782583b36b28ad0d8c77d3 # v1
56
+ with:
57
+ token: ${{ secrets.CODECOV_TOKEN }}
58
+
59
+ - name: SonarQube scan
60
+ if: ${{ env.SONAR_TOKEN != '' && env.SONAR_HOST_URL != '' && env.SONAR_PROJECT_KEY != '' }}
61
+ uses: SonarSource/sonarqube-scan-action@2f77a1ec69fb1d595b06f35ab27e97605bdef703 # v5
62
+ with:
63
+ args: >
64
+ -Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }}
65
+ env:
66
+ SONAR_TOKEN: ${{ env.SONAR_TOKEN }}
67
+ SONAR_HOST_URL: ${{ env.SONAR_HOST_URL }}
68
+
69
+ - name: Snyk dependency monitor
70
+ if: ${{ env.SNYK_TOKEN != '' }}
71
+ uses: snyk/actions/setup@9adf32b1121593767fc3c057af55b55db032dc04 # master
72
+ env:
73
+ SNYK_TOKEN: ${{ env.SNYK_TOKEN }}
74
+
75
+ - name: Snyk monitor by dependency scope (core/dev/extras)
76
+ if: ${{ env.SNYK_TOKEN != '' }}
77
+ shell: bash
78
+ env:
79
+ SNYK_TOKEN: ${{ env.SNYK_TOKEN }}
80
+ run: |
81
+ set -euo pipefail
82
+
83
+ MANIFEST_DIR="$RUNNER_TEMP/snyk-manifests"
84
+ mkdir -p "$MANIFEST_DIR"
85
+
86
+ uv export --frozen --no-default-groups --no-dev --no-hashes --no-emit-project \
87
+ -o "$MANIFEST_DIR/requirements-core.txt"
88
+ uv export --frozen --only-group dev --no-hashes --no-emit-project \
89
+ -o "$MANIFEST_DIR/requirements-dev.txt"
90
+
91
+ for extra in rest sqlalchemy config cache pyspark prometheus celery; do
92
+ uv export --frozen --no-default-groups --no-dev --extra "$extra" \
93
+ --no-hashes --no-emit-project \
94
+ -o "$MANIFEST_DIR/requirements-extra-${extra}.txt"
95
+ done
96
+
97
+ python -m pip install -r "$MANIFEST_DIR/requirements-core.txt"
98
+ snyk monitor --skip-unresolved \
99
+ --file="$MANIFEST_DIR/requirements-core.txt" --package-manager=pip \
100
+ --project-name="loom-py/core" --target-reference="${GITHUB_REF_NAME:-master}"
101
+ python -m pip install -r "$MANIFEST_DIR/requirements-dev.txt"
102
+ snyk monitor --skip-unresolved \
103
+ --file="$MANIFEST_DIR/requirements-dev.txt" --package-manager=pip \
104
+ --project-name="loom-py/dev" --target-reference="${GITHUB_REF_NAME:-master}"
105
+
106
+ for extra in rest sqlalchemy config cache pyspark prometheus celery; do
107
+ python -m pip install -r "$MANIFEST_DIR/requirements-extra-${extra}.txt"
108
+ snyk monitor --skip-unresolved \
109
+ --file="$MANIFEST_DIR/requirements-extra-${extra}.txt" \
110
+ --package-manager=pip \
111
+ --project-name="loom-py/extra-${extra}" \
112
+ --target-reference="${GITHUB_REF_NAME:-master}"
113
+ done
@@ -15,17 +15,20 @@ jobs:
15
15
  env:
16
16
  SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
17
17
  TEST_PYPI_API_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}
18
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
19
+ SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
20
+ SONAR_PROJECT_KEY: ${{ vars.SONAR_PROJECT_KEY }}
18
21
  steps:
19
- - uses: actions/checkout@v4
22
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
20
23
  with:
21
24
  fetch-depth: 0
22
25
  ref: ${{ github.event.pull_request.head.sha }}
23
26
 
24
- - uses: actions/setup-python@v5
27
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
25
28
  with:
26
29
  python-version: "3.11"
27
30
 
28
- - uses: astral-sh/setup-uv@v3
31
+ - uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
29
32
 
30
33
  - name: Configure uv environment path
31
34
  shell: bash
@@ -43,7 +46,7 @@ jobs:
43
46
  - name: Quality report (strict gate)
44
47
  id: quality
45
48
  continue-on-error: true
46
- uses: the-reacher-data/loom-actions/actions/python/quality-report@v1
49
+ uses: the-reacher-data/loom-actions/actions/python/quality-report@e731576f4f9cf4ec2b36fddfc895d328c856d29f # v1
47
50
  with:
48
51
  src-dir: src
49
52
  test-dir: tests
@@ -54,7 +57,7 @@ jobs:
54
57
 
55
58
  - name: Publish PR quality comment
56
59
  if: ${{ always() && steps.quality.outputs.report-file != '' }}
57
- uses: the-reacher-data/loom-actions/actions/core/pr-comment-update@v1
60
+ uses: the-reacher-data/loom-actions/actions/core/pr-comment-update@e731576f4f9cf4ec2b36fddfc895d328c856d29f # v1
58
61
  with:
59
62
  github-token: ${{ secrets.GITHUB_TOKEN }}
60
63
  tags: "<!-- loom-quality-report -->"
@@ -62,7 +65,7 @@ jobs:
62
65
 
63
66
  - name: Compute candidate prerelease version
64
67
  id: version
65
- uses: the-reacher-data/loom-actions/actions/release/versioning-branch-semantic@v1
68
+ uses: the-reacher-data/loom-actions/actions/release/versioning-branch-semantic@e731576f4f9cf4ec2b36fddfc895d328c856d29f # v1
66
69
  with:
67
70
  branch: ${{ github.head_ref }}
68
71
  prerelease: "true"
@@ -70,7 +73,7 @@ jobs:
70
73
  - name: Generate changelog preview
71
74
  id: changelog
72
75
  continue-on-error: true
73
- uses: the-reacher-data/loom-actions/actions/release/changelog-conventional-commit@v1
76
+ uses: the-reacher-data/loom-actions/actions/release/changelog-conventional-commit@e731576f4f9cf4ec2b36fddfc895d328c856d29f # v1
74
77
  with:
75
78
  mode: "pr"
76
79
  branch: ${{ github.event.pull_request.head.sha }}
@@ -81,36 +84,92 @@ jobs:
81
84
 
82
85
  - name: Publish PR changelog comment
83
86
  if: ${{ steps.changelog.outcome == 'success' && steps.changelog.outputs.output_path != '' }}
84
- uses: the-reacher-data/loom-actions/actions/core/pr-comment-update@v1
87
+ uses: the-reacher-data/loom-actions/actions/core/pr-comment-update@e731576f4f9cf4ec2b36fddfc895d328c856d29f # v1
85
88
  with:
86
89
  github-token: ${{ secrets.GITHUB_TOKEN }}
87
90
  tags: "<!-- changelog-preview -->,<!-- branch-source:${{ github.head_ref }} -->"
88
91
  body-file: ${{ steps.changelog.outputs.output_path }}
89
92
 
93
+ - name: Run pytest with coverage and JUnit output
94
+ shell: bash
95
+ run: |
96
+ set -euo pipefail
97
+ uv run --with pytest-cov pytest --cov --junitxml=junit.xml -o junit_family=legacy
98
+
90
99
  - name: Upload coverage to Codecov
91
100
  if: ${{ always() && hashFiles('coverage.xml') != '' }}
92
- uses: codecov/codecov-action@v5
101
+ uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
93
102
  with:
94
103
  files: ./coverage.xml
95
104
  fail_ci_if_error: false
96
105
  token: ${{ secrets.CODECOV_TOKEN }}
97
106
 
98
- - name: Snyk dependency scan
107
+ - name: Upload test results to Codecov
108
+ if: ${{ !cancelled() && hashFiles('junit.xml') != '' }}
109
+ uses: codecov/test-results-action@0fa95f0e1eeaafde2c782583b36b28ad0d8c77d3 # v1
110
+ with:
111
+ token: ${{ secrets.CODECOV_TOKEN }}
112
+
113
+ - name: SonarQube scan
114
+ if: ${{ env.SONAR_TOKEN != '' && env.SONAR_HOST_URL != '' && env.SONAR_PROJECT_KEY != '' }}
115
+ uses: SonarSource/sonarqube-scan-action@2f77a1ec69fb1d595b06f35ab27e97605bdef703 # v5
116
+ with:
117
+ args: >
118
+ -Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }}
119
+ env:
120
+ SONAR_TOKEN: ${{ env.SONAR_TOKEN }}
121
+ SONAR_HOST_URL: ${{ env.SONAR_HOST_URL }}
122
+
123
+ - name: Setup Snyk CLI
124
+ if: ${{ env.SNYK_TOKEN != '' }}
125
+ uses: snyk/actions/setup@9adf32b1121593767fc3c057af55b55db032dc04 # master
126
+
127
+ - name: Snyk dependency scan by scope (core/dev/extras)
99
128
  id: snyk
100
129
  if: ${{ env.SNYK_TOKEN != '' }}
101
130
  continue-on-error: true
102
- uses: snyk/actions/python@master
103
- with:
104
- command: test
105
- args: --severity-threshold=high --all-projects --sarif-file-output=snyk.sarif
131
+ shell: bash
106
132
  env:
107
133
  SNYK_TOKEN: ${{ env.SNYK_TOKEN }}
134
+ run: |
135
+ set -euo pipefail
108
136
 
109
- - name: Upload Snyk SARIF to GitHub Security
110
- if: ${{ always() && env.SNYK_TOKEN != '' && hashFiles('snyk.sarif') != '' }}
111
- uses: github/codeql-action/upload-sarif@v3
112
- with:
113
- sarif_file: snyk.sarif
137
+ MANIFEST_DIR="$RUNNER_TEMP/snyk-manifests"
138
+ mkdir -p "$MANIFEST_DIR"
139
+
140
+ uv export --frozen --no-default-groups --no-dev --no-hashes --no-emit-project \
141
+ -o "$MANIFEST_DIR/requirements-core.txt"
142
+ uv export --frozen --only-group dev --no-hashes --no-emit-project \
143
+ -o "$MANIFEST_DIR/requirements-dev.txt"
144
+
145
+ for extra in rest sqlalchemy config cache pyspark prometheus celery; do
146
+ uv export --frozen --no-default-groups --no-dev --extra "$extra" \
147
+ --no-hashes --no-emit-project \
148
+ -o "$MANIFEST_DIR/requirements-extra-${extra}.txt"
149
+ done
150
+
151
+ python -m pip install -r "$MANIFEST_DIR/requirements-core.txt"
152
+ snyk test --severity-threshold=high \
153
+ --skip-unresolved \
154
+ --file="$MANIFEST_DIR/requirements-core.txt" \
155
+ --package-manager=pip \
156
+ --project-name="loom-py/core"
157
+
158
+ python -m pip install -r "$MANIFEST_DIR/requirements-dev.txt"
159
+ snyk test --severity-threshold=high \
160
+ --skip-unresolved \
161
+ --file="$MANIFEST_DIR/requirements-dev.txt" \
162
+ --package-manager=pip \
163
+ --project-name="loom-py/dev"
164
+
165
+ for extra in rest sqlalchemy config cache pyspark prometheus celery; do
166
+ python -m pip install -r "$MANIFEST_DIR/requirements-extra-${extra}.txt"
167
+ snyk test --severity-threshold=high \
168
+ --skip-unresolved \
169
+ --file="$MANIFEST_DIR/requirements-extra-${extra}.txt" \
170
+ --package-manager=pip \
171
+ --project-name="loom-py/extra-${extra}"
172
+ done
114
173
 
115
174
  - name: Publish prerelease package to TestPyPI
116
175
  if: ${{ github.event.pull_request.head.repo.full_name == github.repository && env.TEST_PYPI_API_TOKEN != '' && steps.version.outputs.deploy == 'true' }}
@@ -0,0 +1,82 @@
1
+ name: docs
2
+
3
+ on:
4
+ pull_request:
5
+ branches: ["master"]
6
+ paths:
7
+ - "docs/**"
8
+ - "src/**/*.py"
9
+ - "README.md"
10
+ - "pyproject.toml"
11
+ - ".github/workflows/docs.yml"
12
+ push:
13
+ branches: ["master"]
14
+ paths:
15
+ - "docs/**"
16
+ - "src/**/*.py"
17
+ - "README.md"
18
+ - "pyproject.toml"
19
+ - ".github/workflows/docs.yml"
20
+ workflow_dispatch:
21
+
22
+ # Prevent concurrent deploys; let an in-progress run finish.
23
+ concurrency:
24
+ group: pages
25
+ cancel-in-progress: false
26
+
27
+ jobs:
28
+ build:
29
+ runs-on: ubuntu-latest
30
+ # contents: read is scoped here — build does not need pages or id-token.
31
+ permissions:
32
+ contents: read
33
+ steps:
34
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
35
+
36
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
37
+ with:
38
+ python-version: "3.11"
39
+
40
+ - uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
41
+
42
+ - name: Build docs (strict)
43
+ shell: bash
44
+ run: |
45
+ set -euo pipefail
46
+ uv run \
47
+ --extra rest \
48
+ --extra sqlalchemy \
49
+ --extra cache \
50
+ --extra config \
51
+ --extra prometheus \
52
+ --with 'sphinx>=8.0' \
53
+ --with 'furo>=2024.8.6' \
54
+ --with 'myst-parser>=3.0' \
55
+ --with 'sphinx-copybutton>=0.5' \
56
+ --with 'sphinx-design>=0.6' \
57
+ sphinx-build -W -b html docs docs/_build/html
58
+
59
+ # upload-pages-artifact creates the zip format required by deploy-pages.
60
+ # Used instead of upload-artifact so the deploy job can consume it directly.
61
+ - name: Upload Pages artifact
62
+ uses: actions/upload-pages-artifact@v3
63
+ with:
64
+ path: docs/_build/html
65
+
66
+ deploy:
67
+ needs: build
68
+ runs-on: ubuntu-latest
69
+ # Only deploy on push to master, not on PRs.
70
+ if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
71
+ # pages: write + id-token: write are required for OIDC-based Pages deployment.
72
+ # Scoped here — deploy does not need contents read.
73
+ permissions:
74
+ pages: write
75
+ id-token: write
76
+ environment:
77
+ name: github-pages
78
+ url: ${{ steps.deploy.outputs.page_url }}
79
+ steps:
80
+ - name: Deploy to GitHub Pages
81
+ id: deploy
82
+ uses: actions/deploy-pages@v4
@@ -27,15 +27,15 @@ jobs:
27
27
  PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
28
28
  RELEASE_BRANCH: ${{ github.event_name == 'workflow_dispatch' && inputs.source_branch || github.event.pull_request.head.ref }}
29
29
  steps:
30
- - uses: actions/checkout@v4
30
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
31
31
  with:
32
32
  fetch-depth: 0
33
33
 
34
- - uses: actions/setup-python@v5
34
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
35
35
  with:
36
36
  python-version: "3.11"
37
37
 
38
- - uses: astral-sh/setup-uv@v3
38
+ - uses: astral-sh/setup-uv@caf0cab7a618c569241d31dcd442f54681755d39 # v3
39
39
 
40
40
  - name: Configure uv environment path
41
41
  shell: bash
@@ -45,7 +45,7 @@ jobs:
45
45
 
46
46
  - name: Compute release version from merged branch
47
47
  id: version
48
- uses: the-reacher-data/loom-actions/actions/release/versioning-branch-semantic@v1
48
+ uses: the-reacher-data/loom-actions/actions/release/versioning-branch-semantic@e731576f4f9cf4ec2b36fddfc895d328c856d29f # v1
49
49
  with:
50
50
  branch: ${{ env.RELEASE_BRANCH }}
51
51
  prerelease: "false"
@@ -53,7 +53,7 @@ jobs:
53
53
  - name: Generate release changelog
54
54
  id: changelog
55
55
  if: ${{ steps.version.outputs.deploy == 'true' && github.event_name != 'workflow_dispatch' }}
56
- uses: the-reacher-data/loom-actions/actions/release/changelog-conventional-commit@v1
56
+ uses: the-reacher-data/loom-actions/actions/release/changelog-conventional-commit@e731576f4f9cf4ec2b36fddfc895d328c856d29f # v1
57
57
  with:
58
58
  mode: "release"
59
59
  branch: ${{ env.RELEASE_BRANCH }}
@@ -94,13 +94,13 @@ jobs:
94
94
 
95
95
  - name: Publish package to PyPI (token mode)
96
96
  if: ${{ steps.version.outputs.deploy == 'true' && env.PYPI_API_TOKEN != '' }}
97
- uses: pypa/gh-action-pypi-publish@release/v1
97
+ uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1
98
98
  with:
99
99
  password: ${{ env.PYPI_API_TOKEN }}
100
100
 
101
101
  - name: Publish package to PyPI (trusted publisher)
102
102
  if: ${{ steps.version.outputs.deploy == 'true' && env.PYPI_API_TOKEN == '' }}
103
- uses: pypa/gh-action-pypi-publish@release/v1
103
+ uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1
104
104
 
105
105
  - name: Create and push tags
106
106
  if: ${{ steps.version.outputs.deploy == 'true' }}
@@ -127,7 +127,7 @@ jobs:
127
127
 
128
128
  - name: Create GitHub release
129
129
  if: ${{ steps.version.outputs.deploy == 'true' }}
130
- uses: softprops/action-gh-release@v2
130
+ uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
131
131
  with:
132
132
  tag_name: v${{ steps.version.outputs.version }}
133
133
  name: v${{ steps.version.outputs.version }}
@@ -8,7 +8,6 @@ wheels/
8
8
 
9
9
  # Virtual environments
10
10
  .venv
11
-
12
11
  # Local/editor/cache noise
13
12
  .mypy_cache/
14
13
  .pytest_cache/
@@ -17,10 +16,16 @@ wheels/
17
16
  coverage.xml
18
17
  htmlcov/
19
18
  .vscode/
20
- docs/
19
+ docs/__pycache__/
21
20
  .claude/
22
21
  .claude.md
23
22
  AGENTS.md
24
23
 
25
24
  # Internal discussion docs (not part of distributable source)
26
25
  specs/
26
+
27
+ # Sphinx build output — generated by ReadTheDocs, never commit
28
+ docs/_build/
29
+
30
+ # autosummary stubs — regenerated by Sphinx on every build
31
+ docs/reference/api/generated/
@@ -24,6 +24,13 @@ repos:
24
24
 
25
25
  - id: mypy
26
26
  name: mypy strict
27
- entry: uv run mypy --no-incremental src tests
27
+ entry: uv run mypy --no-incremental src
28
28
  language: system
29
29
  pass_filenames: false
30
+
31
+ - id: pyright
32
+ name: pyright
33
+ entry: uv run pyright
34
+ language: system
35
+ pass_filenames: false
36
+ types: [python]
@@ -0,0 +1,15 @@
1
+ version: 2
2
+
3
+ build:
4
+ os: ubuntu-24.04
5
+ tools:
6
+ python: "3.11"
7
+
8
+ sphinx:
9
+ configuration: docs/conf.py
10
+
11
+ python:
12
+ install:
13
+ - requirements: docs/requirements.txt
14
+ - method: pip
15
+ path: .
@@ -0,0 +1,120 @@
1
+ # 🚀 Release 0.2.0 ([#9](https://github.com/the-reacher-data/loom-py/pull/9)) ([`2f669ab`](https://github.com/the-reacher-data/loom-py/commit/2f669ab205c7255eb6494e4cdb8ab8092817af62))
2
+
3
+
4
+ ## ✨ Features
5
+ ### core
6
+ - **core:** typed repository abstractions and SQLAlchemy backend<br>
7
+ > Async repository protocol (RepositoryRead, RepositoryWrite, RepoFor) backed
8
+ > by SQLAlchemy 2.0 async session. Struct-based model system using
9
+ > msgspec.Struct
10
+ > as the single source of truth — models compile to SA mapped classes at startup
11
+ > via compile_all(). count() and UPDATE RETURNING included as first-class
12
+ > operations.
13
+
14
+ - **core:** use-case DSL with field refs, compute, rules and typed markers<br>
15
+ > Declarative use-case definition via Input, Load, LoadById, Exists, Compute and
16
+ > Rule markers. Signature inspection runs once at compile time; RuntimeExecutor
17
+ > drives execution from an immutable ExecutionPlan. No per-request reflection.
18
+
19
+ - **core:** ApplicationInvoker and named use-case registry<br>
20
+ > Use cases and job callbacks invoke other use cases by type through
21
+ > ApplicationInvoker without direct coupling. A named registry maps use-case
22
+ > keys to compiled instances at bootstrap, providing a stable cross-invocation
23
+ > contract.
24
+
25
+ - **core:** compiled model artifact and cache entity keys<br>
26
+ > compile_all() produces a typed CompiledCore artifact exposing stable entity
27
+ > keys used by the cache layer for deterministic repository-level invalidation
28
+ > across reads and writes.
29
+
30
+ - **core:** executor skips UoW for read-only use cases and GET routes<br>
31
+ > UseCase.read_only=True and all GET routes bypass UoW.begin/commit, removing
32
+ > at minimum one BEGIN+COMMIT round-trip from every read request on PostgreSQL.
33
+
34
+
35
+ ### projection
36
+ - **projection:** compiler-driven memory/SQL routing<br>
37
+ > Projections are source-agnostic at declaration time. The backend compiler
38
+ > decides at compile_all() whether each projection runs in-memory (relation
39
+ > already loaded in the active profile) or via SQL. Users declare only
40
+ > CountLoader, ExistsLoader, or JoinFieldsLoader — no source= parameter.
41
+ > Internal _Memory* and _Sql* loaders are synthesized at compile time.
42
+
43
+
44
+ ### cache
45
+ - **cache:** aiocache gateway with auto-inferred invalidation specs<br>
46
+ > CachedRepository wraps any repository with read-through/write-through caching.
47
+ > ONE_TO_MANY depends_on specs are auto-generated from field annotations — no
48
+ > explicit declaration needed. Explicit depends_on always wins.
49
+
50
+
51
+ ### job
52
+ - **job:** async job domain model and orchestration primitives<br>
53
+ > Job[ResultT] base class with Celery routing ClassVars. JobHandle / JobGroup
54
+ > with dual-mode waiting (Celery + inline). JobCallback lifecycle with
55
+ > on_success/on_failure. Dispatch is transactionally safe — jobs flush on
56
+ > UoW commit and are cleared on rollback.
57
+
58
+
59
+ ### celery
60
+ - **celery:** production-ready Celery integration layer<br>
61
+ > CeleryJobService, persistent worker event loop, trace propagation, eager
62
+ > fallback, and task_default_queue routing so callbacks land on the correct
63
+ > consumed queue. bootstrap_worker compiles use cases, repositories, and
64
+ > registers Celery tasks in a single call.
65
+
66
+ - **celery:** worker job discovery from modules or manifest<br>
67
+ > bootstrap_worker discovers and registers Job classes automatically from
68
+ > module include paths (mode: modules) or from a typed WorkerManifest
69
+ > (mode: manifest). WorkerManifest replaces scattered JOBS/USE_CASES/INTERFACES
70
+ > module attributes with a single typed contract.
71
+
72
+ - **celery:** interfaces= and use_cases= on bootstrap_worker<br>
73
+ > Callbacks that call ApplicationInvoker need matching use-case keys compiled
74
+ > in the worker. interfaces= extracts use-case types from RestInterface route
75
+ > declarations (including AutoCRUD-generated ones). use_cases= handles
76
+ > non-AutoCRUD scenarios. Both can be combined with discovery mode.
77
+
78
+
79
+ ### rest
80
+ - **rest:** AutoCRUD and FastAPI adapter<br>
81
+ > RestInterface.auto=True generates full CRUD routes at class definition time
82
+ > via build_auto_routes(). OpenAPI contracts expose query params, pagination
83
+ > defaults, and decoupled CreateInput/UpdateInput write DTOs. Discovery engine
84
+ > mounts all declared interfaces at bootstrap.
85
+
86
+
87
+ ### observability
88
+ - **observability:** trace_id propagation and Prometheus adapter<br>
89
+ > trace_id injected into every request context and propagated to job callbacks.
90
+ > MetricsAdapter protocol emits execution events; PrometheusAdapter records
91
+ > latency histograms and error counters with low cardinality labels.
92
+
93
+
94
+
95
+
96
+ ## 📖 Documentation
97
+ - Sphinx documentation platform with full public guides<br>
98
+ > Quickstart, use-case DSL reference, AutoCRUD guide, Celery integration guide
99
+ > (job definition, dispatch, callbacks, YAML reference, bootstrap options,
100
+ > ApplicationInvoker, Docker-compose stack), and dummy-loom examples-repo
101
+ > walkthrough. Deployed to Read the Docs.
102
+
103
+
104
+
105
+
106
+
107
+ ## ⚡ Performance
108
+ ### repository
109
+ - **repository:** single-query total count for offset pagination<br>
110
+ > list_with_query with PaginationMode.OFFSET issues a single SELECT COUNT(*)
111
+ > instead of a separate full-table scan, eliminating one round-trip per
112
+ > paginated list operation.
113
+
114
+
115
+ ### engine
116
+ - **engine:** UPDATE RETURNING replaces SELECT + flush + refresh<br>
117
+ > SQLAlchemyUpdateMixin.update() issues a single UPDATE ... RETURNING
118
+ > round-trip.
119
+ > Server-side onupdate expressions are pre-computed at init time and injected
120
+ > into the SET clause automatically.