GeneralManager 0.48.0__tar.gz → 0.50.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 (199) hide show
  1. {generalmanager-0.48.0/src/GeneralManager.egg-info → generalmanager-0.50.0}/PKG-INFO +1 -1
  2. {generalmanager-0.48.0 → generalmanager-0.50.0}/pyproject.toml +1 -1
  3. {generalmanager-0.48.0 → generalmanager-0.50.0/src/GeneralManager.egg-info}/PKG-INFO +1 -1
  4. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/property.py +1 -1
  5. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/cache_decorator.py +105 -62
  6. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/dependency_cache.py +10 -0
  7. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/dependency_index.py +17 -1
  8. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/dependency_publish.py +91 -1
  9. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/run_context.py +98 -6
  10. {generalmanager-0.48.0 → generalmanager-0.50.0}/LICENSE +0 -0
  11. {generalmanager-0.48.0 → generalmanager-0.50.0}/README.md +0 -0
  12. {generalmanager-0.48.0 → generalmanager-0.50.0}/setup.cfg +0 -0
  13. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
  14. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
  15. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/GeneralManager.egg-info/requires.txt +0 -0
  16. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/GeneralManager.egg-info/top_level.txt +0 -0
  17. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/__init__.py +0 -0
  18. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/__init__.py +0 -0
  19. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/api.py +0 -0
  20. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/bucket.py +0 -0
  21. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/cache.py +0 -0
  22. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/factory.py +0 -0
  23. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/general_manager.py +0 -0
  24. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/interface.py +0 -0
  25. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/manager.py +0 -0
  26. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/measurement.py +0 -0
  27. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/permission.py +0 -0
  28. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/rule.py +0 -0
  29. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/search.py +0 -0
  30. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/utils.py +0 -0
  31. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/__init__.py +0 -0
  32. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql.py +0 -0
  33. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_errors.py +0 -0
  34. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_mutations.py +0 -0
  35. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_prefetch.py +0 -0
  36. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_resolvers.py +0 -0
  37. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_search.py +0 -0
  38. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
  39. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_subscriptions.py +0 -0
  40. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_view.py +0 -0
  41. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/mutation.py +0 -0
  42. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/registry.py +0 -0
  43. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/remote_api.py +0 -0
  44. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/remote_invalidation.py +0 -0
  45. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/remote_invalidation_client.py +0 -0
  46. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/apps.py +0 -0
  47. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bootstrap.py +0 -0
  48. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bucket/__init__.py +0 -0
  49. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bucket/base_bucket.py +0 -0
  50. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bucket/calculation_bucket.py +0 -0
  51. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bucket/database_bucket.py +0 -0
  52. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bucket/group_bucket.py +0 -0
  53. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bucket/request_bucket.py +0 -0
  54. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/__init__.py +0 -0
  55. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/cache_tracker.py +0 -0
  56. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/model_dependency_collector.py +0 -0
  57. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/signals.py +0 -0
  58. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/conf.py +0 -0
  59. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/factory/__init__.py +0 -0
  60. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/factory/auto_factory.py +0 -0
  61. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/factory/factories.py +0 -0
  62. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/factory/factory_methods.py +0 -0
  63. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/__init__.py +0 -0
  64. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/base_interface.py +0 -0
  65. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/bundles/__init__.py +0 -0
  66. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/bundles/calculation.py +0 -0
  67. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/bundles/database.py +0 -0
  68. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/bundles/remote_manager.py +0 -0
  69. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/bundles/request.py +0 -0
  70. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/__init__.py +0 -0
  71. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/base.py +0 -0
  72. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/builtin.py +0 -0
  73. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
  74. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
  75. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
  76. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/configuration.py +0 -0
  77. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/core/observability.py +0 -0
  78. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/core/utils.py +0 -0
  79. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/exceptions.py +0 -0
  80. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
  81. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
  82. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
  83. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/factory.py +0 -0
  84. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
  85. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
  86. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm/history.py +0 -0
  87. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
  88. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
  89. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm/support.py +0 -0
  90. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
  91. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
  92. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
  93. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
  94. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
  95. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
  96. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
  97. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
  98. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/registry.py +0 -0
  99. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
  100. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
  101. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/infrastructure/__init__.py +0 -0
  102. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
  103. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
  104. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/__init__.py +0 -0
  105. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/calculation.py +0 -0
  106. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/database.py +0 -0
  107. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/existing_model.py +0 -0
  108. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/read_only.py +0 -0
  109. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
  110. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/request.py +0 -0
  111. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/manifests/__init__.py +0 -0
  112. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/manifests/capability_builder.py +0 -0
  113. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
  114. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/manifests/capability_models.py +0 -0
  115. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/orm_interface.py +0 -0
  116. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/requests.py +0 -0
  117. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/utils/__init__.py +0 -0
  118. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
  119. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/utils/errors.py +0 -0
  120. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/utils/models.py +0 -0
  121. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/logging.py +0 -0
  122. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/management/commands/search_index.py +0 -0
  123. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
  124. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/management/commands/shell.py +0 -0
  125. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
  126. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
  127. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/manager/__init__.py +0 -0
  128. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/manager/general_manager.py +0 -0
  129. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/manager/group_manager.py +0 -0
  130. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/manager/input.py +0 -0
  131. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/manager/meta.py +0 -0
  132. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/measurement/__init__.py +0 -0
  133. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/measurement/measurement.py +0 -0
  134. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/measurement/measurement_field.py +0 -0
  135. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/metrics/__init__.py +0 -0
  136. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/metrics/graphql.py +0 -0
  137. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/migrations/0001_initial.py +0 -0
  138. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
  139. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
  140. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/migrations/__init__.py +0 -0
  141. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/models.py +0 -0
  142. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/__init__.py +0 -0
  143. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/audit.py +0 -0
  144. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/base_permission.py +0 -0
  145. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/graphql_capabilities.py +0 -0
  146. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/manager_based_permission.py +0 -0
  147. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/mutation_permission.py +0 -0
  148. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/permission_checks.py +0 -0
  149. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/permission_data_manager.py +0 -0
  150. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/utils.py +0 -0
  151. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/public_api_registry.py +0 -0
  152. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/py.typed +0 -0
  153. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/rule/__init__.py +0 -0
  154. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/rule/handler.py +0 -0
  155. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/rule/rule.py +0 -0
  156. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/__init__.py +0 -0
  157. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/async_tasks.py +0 -0
  158. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backend.py +0 -0
  159. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backend_registry.py +0 -0
  160. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backends/__init__.py +0 -0
  161. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backends/dev.py +0 -0
  162. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backends/meilisearch.py +0 -0
  163. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backends/opensearch.py +0 -0
  164. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backends/typesense.py +0 -0
  165. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/config.py +0 -0
  166. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/indexer.py +0 -0
  167. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/registry.py +0 -0
  168. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/utils.py +0 -0
  169. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/seeding/__init__.py +0 -0
  170. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/seeding/manager_landscape.py +0 -0
  171. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/__init__.py +0 -0
  172. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/args_to_kwargs.py +0 -0
  173. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/filter_parser.py +0 -0
  174. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/format_string.py +0 -0
  175. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/json_encoder.py +0 -0
  176. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/make_cache_key.py +0 -0
  177. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/none_to_zero.py +0 -0
  178. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/path_mapping.py +0 -0
  179. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/public_api.py +0 -0
  180. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/testing.py +0 -0
  181. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/type_checks.py +0 -0
  182. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/__init__.py +0 -0
  183. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/actions.py +0 -0
  184. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/backend_registry.py +0 -0
  185. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/backends/__init__.py +0 -0
  186. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/backends/celery.py +0 -0
  187. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/backends/local.py +0 -0
  188. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/backends/n8n.py +0 -0
  189. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/config.py +0 -0
  190. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/engine.py +0 -0
  191. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/event_registry.py +0 -0
  192. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/events.py +0 -0
  193. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/models.py +0 -0
  194. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/signal_bridge.py +0 -0
  195. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/tasks.py +0 -0
  196. {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/telemetry.py +0 -0
  197. {generalmanager-0.48.0 → generalmanager-0.50.0}/tests/test_settings.py +0 -0
  198. {generalmanager-0.48.0 → generalmanager-0.50.0}/tests/test_urls.py +0 -0
  199. {generalmanager-0.48.0 → generalmanager-0.50.0}/tests/testing_asgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.48.0
3
+ Version: 0.50.0
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "GeneralManager"
7
- version = "0.48.0"
7
+ version = "0.50.0"
8
8
  description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.48.0
3
+ Version: 0.50.0
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -95,7 +95,7 @@ class GraphQLProperty(property):
95
95
  selected_cache = self.cache
96
96
  if selected_cache == "none":
97
97
  return self._raw_fget
98
- return cached(scope=cast(Literal["dependency", "run"], selected_cache))(
98
+ return cached(cache=cast(Literal["dependency", "run"], selected_cache))(
99
99
  self._raw_fget
100
100
  )
101
101
 
@@ -11,6 +11,7 @@ from typing import (
11
11
  Set,
12
12
  TypeVar,
13
13
  cast,
14
+ overload,
14
15
  )
15
16
 
16
17
  from django.core.cache import cache as django_cache
@@ -28,6 +29,7 @@ from general_manager.cache.dependency_index import (
28
29
  )
29
30
  from general_manager.cache.dependency_publish import (
30
31
  CachePublishAborted,
32
+ PendingDependencyCachePublication,
31
33
  acquire_compute_lease,
32
34
  publish_dependency_cache_entry,
33
35
  release_compute_lease,
@@ -87,102 +89,122 @@ class UnsupportedCacheScopeError(ValueError):
87
89
 
88
90
 
89
91
  class CacheTimeoutConfigurationError(ValueError):
90
- """Raised when timeout is used with an incompatible cache scope."""
92
+ """Raised when timeout is used with an incompatible cache setting."""
91
93
 
92
94
  @classmethod
93
95
  def missing_timeout(cls) -> "CacheTimeoutConfigurationError":
94
- return cls('scope="timeout" requires timeout')
96
+ return cls('cache="timeout" requires timeout')
95
97
 
96
98
  @classmethod
97
99
  def unexpected_timeout(cls) -> "CacheTimeoutConfigurationError":
98
- return cls('timeout is only supported with scope="timeout"')
100
+ return cls('timeout is only supported with cache="timeout"')
99
101
 
100
102
 
103
+ @overload
104
+ def cached(func: FuncT) -> FuncT: ...
105
+ @overload
101
106
  def cached(
107
+ func: None = None,
102
108
  timeout: Optional[int] = None,
103
109
  cache_backend: CacheBackend = django_cache,
104
110
  record_fn: RecordFn = record_dependencies,
105
111
  *,
106
- scope: CacheScope = "run",
107
- ) -> Callable[[FuncT], FuncT]:
112
+ cache: CacheScope = "run",
113
+ ) -> Callable[[FuncT], FuncT]: ...
114
+
115
+
116
+ def cached(
117
+ func: FuncT | None = None,
118
+ timeout: Optional[int] = None,
119
+ cache_backend: CacheBackend = django_cache,
120
+ record_fn: RecordFn = record_dependencies,
121
+ *,
122
+ cache: CacheScope = "run",
123
+ ) -> FuncT | Callable[[FuncT], FuncT]:
108
124
  """
109
- Decorator factory for caching a function call.
125
+ Decorator for caching a function call.
110
126
 
111
127
  By default, cached values are scoped to the active
112
128
  :class:`~general_manager.cache.run_context.CalculationRunContext` and are
113
- discarded when that run ends. Use ``scope="dependency"`` to persist values
129
+ discarded when that run ends. Use ``cache="dependency"`` to persist values
114
130
  in ``cache_backend`` and use ``record_fn`` to persist dependency metadata
115
- for invalidation. Use ``scope="timeout"`` with ``timeout`` set for
131
+ for invalidation. Use ``cache="timeout"`` with ``timeout`` set for
116
132
  cache-backend storage with time-based expiry; dependency recording is
117
- ignored for timeout-scoped values.
133
+ ignored for timeout-cached values.
118
134
 
119
135
  Parameters:
120
- timeout (int | None): Expiration in seconds for timeout-scoped cached values.
121
- Required when ``scope`` is ``"timeout"`` and invalid with any other
136
+ func (Callable[..., object] | None): Function being decorated when used
137
+ as ``@cached``. Leave unset when using ``@cached(...)``.
138
+ timeout (int | None): Expiration in seconds for timeout-cached values.
139
+ Required when ``cache`` is ``"timeout"`` and invalid with any other
122
140
  ``CacheScope``.
123
141
  cache_backend (CacheBackend): Backend used to read and write cached results.
124
142
  record_fn (RecordFn): Callback invoked to persist dependency metadata when
125
- ``scope`` is ``"dependency"``. Defaults to
143
+ ``cache`` is ``"dependency"``. Defaults to
126
144
  :func:`~general_manager.cache.dependency_index.record_dependencies`.
127
- scope (CacheScope): Cache storage strategy. ``"run"`` memoizes for the active run,
145
+ cache (CacheScope): Cache storage strategy. ``"run"`` memoizes for the active run,
128
146
  ``"dependency"`` stores in ``cache_backend`` with dependency tracking,
129
147
  ``"timeout"`` stores in ``cache_backend`` with time-based expiry, and
130
148
  ``"none"`` disables caching.
131
149
 
132
150
  Returns:
133
- Callable: Decorator that wraps the target function with caching behaviour.
151
+ Callable: Decorated function or decorator that wraps the target function
152
+ with caching behaviour.
134
153
 
135
154
  Raises:
136
- ValueError: Raised for invalid ``scope``/``timeout`` combinations, including
155
+ ValueError: Raised for invalid ``cache``/``timeout`` combinations, including
137
156
  unsupported ``CacheScope`` values, missing ``timeout`` for
138
- ``scope="timeout"``, and ``timeout`` supplied for non-timeout scopes.
157
+ ``cache="timeout"``, and ``timeout`` supplied for non-timeout cache modes.
139
158
  DependencyLockTimeoutError: Propagated from ``record_fn`` (i.e.
140
159
  :func:`~general_manager.cache.dependency_index.record_dependencies`) when the
141
- dependency-index lock cannot be acquired within the configured timeout. The cached
160
+ dependency-index lock cannot be acquired within the configured timeout. The cached
142
161
  value has already been stored at that point; only the dependency metadata is lost.
143
162
  """
144
- if scope not in {"dependency", "run", "timeout", "none"}:
145
- raise UnsupportedCacheScopeError(scope)
146
- if scope == "timeout" and timeout is None:
163
+ if cache not in {"dependency", "run", "timeout", "none"}:
164
+ raise UnsupportedCacheScopeError(cache)
165
+ if cache == "timeout" and timeout is None:
147
166
  raise CacheTimeoutConfigurationError.missing_timeout()
148
- if timeout is not None and scope != "timeout":
167
+ if timeout is not None and cache != "timeout":
149
168
  raise CacheTimeoutConfigurationError.unexpected_timeout()
150
169
 
151
- def decorator(func: FuncT) -> FuncT:
152
- @wraps(func)
170
+ def decorator(decorated_func: FuncT) -> FuncT:
171
+ @wraps(decorated_func)
153
172
  def wrapper(*args: object, **kwargs: object) -> object:
154
- if scope == "none":
155
- return func(*args, **kwargs)
173
+ if cache == "none":
174
+ return decorated_func(*args, **kwargs)
156
175
 
157
- if scope == "run":
158
- key = make_cache_key(func, args, kwargs)
176
+ if cache == "run":
177
+ key = make_cache_key(decorated_func, args, kwargs)
159
178
  with ensure_calculation_run_context() as context:
160
- return context.get_or_set(key, lambda: func(*args, **kwargs))
179
+ return context.get_or_set(
180
+ key,
181
+ lambda: decorated_func(*args, **kwargs),
182
+ )
161
183
 
162
- key = make_cache_key(func, args, kwargs)
184
+ key = make_cache_key(decorated_func, args, kwargs)
163
185
 
164
- if scope == "timeout":
186
+ if cache == "timeout":
165
187
  cached_result = cache_backend.get(key, _SENTINEL)
166
188
  if cached_result is not _SENTINEL:
167
189
  logger.debug(
168
190
  "cache hit",
169
191
  context={
170
- "function": func.__qualname__,
192
+ "function": decorated_func.__qualname__,
171
193
  "key": key,
172
- "scope": scope,
194
+ "cache": cache,
173
195
  },
174
196
  )
175
197
  return cached_result
176
198
 
177
- result = func(*args, **kwargs)
199
+ result = decorated_func(*args, **kwargs)
178
200
  cache_backend.set(key, result, timeout)
179
201
  logger.debug(
180
202
  "cache miss stored",
181
203
  context={
182
- "function": func.__qualname__,
204
+ "function": decorated_func.__qualname__,
183
205
  "key": key,
184
206
  "timeout": timeout,
185
- "scope": scope,
207
+ "cache": cache,
186
208
  },
187
209
  )
188
210
  return result
@@ -192,9 +214,9 @@ def cached(
192
214
  logger.debug(
193
215
  message,
194
216
  context={
195
- "function": func.__qualname__,
217
+ "function": decorated_func.__qualname__,
196
218
  "key": key,
197
- "scope": scope,
219
+ "cache": cache,
198
220
  },
199
221
  )
200
222
  return hit.value
@@ -232,6 +254,7 @@ def cached(
232
254
  )
233
255
  lease = acquire_compute_lease(key)
234
256
 
257
+ lease_transferred_to_context = False
235
258
  try:
236
259
  cached_hit = read_dependency_cache_hit(
237
260
  cache_backend,
@@ -243,7 +266,7 @@ def cached(
243
266
 
244
267
  started_generation = get_dependency_generation()
245
268
  with DependencyTracker() as dependencies:
246
- result = func(*args, **kwargs)
269
+ result = decorated_func(*args, **kwargs)
247
270
  ModelDependencyCollector.add_args(dependencies, args, kwargs)
248
271
 
249
272
  def record_many(
@@ -252,34 +275,52 @@ def cached(
252
275
  for entry_key, entry_dependencies in entries:
253
276
  record_fn(entry_key, set(entry_dependencies))
254
277
 
255
- try:
256
- publish_dependency_cache_entry(
257
- cache_key=key,
258
- result=result,
259
- dependencies=dependencies,
260
- cache_backend=cache_backend,
261
- timeout=timeout,
262
- started_generation=started_generation,
263
- record_many_fn=(
264
- None if record_fn is record_dependencies else record_many
265
- ),
266
- )
267
- except CachePublishAborted:
268
- logger.debug(
269
- "dependency cache publish aborted",
270
- context={
271
- "function": func.__qualname__,
272
- "key": key,
273
- "scope": scope,
274
- },
278
+ publish_context = current_calculation_run_context()
279
+ if publish_context is not None and record_fn is record_dependencies:
280
+ publish_context.buffer_dependency_cache_publication(
281
+ PendingDependencyCachePublication(
282
+ cache_key=key,
283
+ result=result,
284
+ dependencies=frozenset(dependencies),
285
+ cache_backend=cache_backend,
286
+ timeout=timeout,
287
+ started_generation=started_generation,
288
+ lease=lease,
289
+ )
275
290
  )
291
+ lease_transferred_to_context = True
292
+ else:
293
+ try:
294
+ publish_dependency_cache_entry(
295
+ cache_key=key,
296
+ result=result,
297
+ dependencies=dependencies,
298
+ cache_backend=cache_backend,
299
+ timeout=timeout,
300
+ started_generation=started_generation,
301
+ record_many_fn=(
302
+ None
303
+ if record_fn is record_dependencies
304
+ else record_many
305
+ ),
306
+ )
307
+ except CachePublishAborted:
308
+ logger.debug(
309
+ "dependency cache publish aborted",
310
+ context={
311
+ "function": decorated_func.__qualname__,
312
+ "key": key,
313
+ "cache": cache,
314
+ },
315
+ )
276
316
  finally:
277
- release_compute_lease(lease)
317
+ if not lease_transferred_to_context:
318
+ release_compute_lease(lease)
278
319
 
279
320
  logger.debug(
280
321
  "cache miss recorded",
281
322
  context={
282
- "function": func.__qualname__,
323
+ "function": decorated_func.__qualname__,
283
324
  "key": key,
284
325
  "dependency_count": len(dependencies),
285
326
  "timeout": timeout,
@@ -288,8 +329,10 @@ def cached(
288
329
  return result
289
330
 
290
331
  # fix for python 3.14:
291
- wrapper.__annotations__ = func.__annotations__
332
+ wrapper.__annotations__ = decorated_func.__annotations__
292
333
 
293
334
  return cast(FuncT, wrapper)
294
335
 
295
- return decorator
336
+ if func is None:
337
+ return decorator
338
+ return decorator(func)
@@ -45,6 +45,16 @@ class DependencyCacheGetManyBackend(DependencyCacheBackend, Protocol):
45
45
  ...
46
46
 
47
47
 
48
+ class DependencyCacheSetManyBackend(DependencyCacheBackend, Protocol):
49
+ def set_many(
50
+ self,
51
+ data: Mapping[str, Any],
52
+ timeout: int | None = None,
53
+ ) -> Any:
54
+ """Store cached values for many keys."""
55
+ ...
56
+
57
+
48
58
  def make_dependency_cache_entry(
49
59
  value: Any,
50
60
  dependencies: Iterable[Dependency],
@@ -124,6 +124,14 @@ def _set_dependency_data_change_count(count: int) -> int:
124
124
  return count
125
125
 
126
126
 
127
+ def _discard_active_context_dependency_cache_state() -> None:
128
+ from general_manager.cache.run_context import current_calculation_run_context
129
+
130
+ context = current_calculation_run_context()
131
+ if context is not None:
132
+ context.discard_dependency_cache_state()
133
+
134
+
127
135
  def begin_dependency_data_change() -> int:
128
136
  """
129
137
  Mark a data change as active and bump the dependency generation.
@@ -136,9 +144,17 @@ def begin_dependency_data_change() -> int:
136
144
  generation = _set_dependency_generation(get_dependency_generation() + 1)
137
145
  _set_dependency_data_change_count(_get_dependency_data_change_count() + 1)
138
146
  cache.set(DATA_CHANGE_LOCK_KEY, "1", None)
139
- return generation
140
147
  finally:
141
148
  release_lock()
149
+ try:
150
+ _discard_active_context_dependency_cache_state()
151
+ except Exception:
152
+ try:
153
+ end_dependency_data_change()
154
+ except Exception:
155
+ logger.exception("dependency data-change cleanup rollback failed")
156
+ raise
157
+ return generation
142
158
 
143
159
 
144
160
  def end_dependency_data_change() -> None:
@@ -4,13 +4,15 @@ from __future__ import annotations
4
4
 
5
5
  import time
6
6
  import uuid
7
+ from collections import defaultdict
7
8
  from dataclasses import dataclass
8
- from typing import Any, Callable, Iterable
9
+ from typing import Any, Callable, Iterable, TypeGuard
9
10
 
10
11
  from django.core.cache import cache as coordination_cache
11
12
 
12
13
  from general_manager.cache.dependency_cache import (
13
14
  DependencyCacheBackend,
15
+ DependencyCacheSetManyBackend,
14
16
  make_dependency_cache_entry,
15
17
  read_dependency_cache_hit,
16
18
  )
@@ -39,6 +41,19 @@ class CacheComputeLease:
39
41
  token: str
40
42
 
41
43
 
44
+ @dataclass(frozen=True)
45
+ class PendingDependencyCachePublication:
46
+ """A dependency-cache miss waiting for guarded publication."""
47
+
48
+ cache_key: str
49
+ result: Any
50
+ dependencies: frozenset[Dependency]
51
+ cache_backend: DependencyCacheBackend
52
+ timeout: int | None
53
+ started_generation: int
54
+ lease: CacheComputeLease
55
+
56
+
42
57
  RecordManyDependenciesFn = Callable[[Iterable[tuple[str, Iterable[Dependency]]]], None]
43
58
 
44
59
  COMPUTE_LOCK_PREFIX = "dependency_cache_compute_lock"
@@ -111,6 +126,81 @@ def _ensure_publish_current(started_generation: int) -> None:
111
126
  raise CachePublishAborted()
112
127
 
113
128
 
129
+ def _supports_set_many(
130
+ cache_backend: DependencyCacheBackend,
131
+ ) -> TypeGuard[DependencyCacheSetManyBackend]:
132
+ return callable(getattr(cache_backend, "set_many", None))
133
+
134
+
135
+ def _set_dependency_cache_entries(
136
+ entries: Iterable[PendingDependencyCachePublication],
137
+ ) -> None:
138
+ grouped: dict[
139
+ tuple[int, int | None],
140
+ list[PendingDependencyCachePublication],
141
+ ] = defaultdict(list)
142
+ for entry in entries:
143
+ grouped[(id(entry.cache_backend), entry.timeout)].append(entry)
144
+
145
+ for group_entries in grouped.values():
146
+ cache_backend = group_entries[0].cache_backend
147
+ timeout = group_entries[0].timeout
148
+ payloads = {
149
+ entry.cache_key: make_dependency_cache_entry(
150
+ entry.result,
151
+ entry.dependencies,
152
+ )
153
+ for entry in group_entries
154
+ }
155
+ if _supports_set_many(cache_backend):
156
+ failed_keys = cache_backend.set_many(payloads, timeout) or ()
157
+ for key in failed_keys:
158
+ if key in payloads:
159
+ cache_backend.set(key, payloads[key], timeout)
160
+ continue
161
+ for key, payload in payloads.items():
162
+ cache_backend.set(key, payload, timeout)
163
+
164
+
165
+ def publish_dependency_cache_entries(
166
+ entries: Iterable[PendingDependencyCachePublication],
167
+ ) -> None:
168
+ """Publish dependency metadata and values for current entries in one batch."""
169
+ pending_entries = tuple(entries)
170
+ if not pending_entries:
171
+ return
172
+
173
+ acquire_lock_with_retry("publish_dependency_cache_entries")
174
+ try:
175
+ if is_dependency_data_change_active():
176
+ raise CachePublishAborted()
177
+
178
+ current_generation = get_dependency_generation()
179
+ publishable_entries = tuple(
180
+ entry
181
+ for entry in pending_entries
182
+ if entry.started_generation == current_generation
183
+ )
184
+ if not publishable_entries:
185
+ return
186
+
187
+ idx = get_full_index()
188
+ for entry in publishable_entries:
189
+ if entry.dependencies:
190
+ _record_dependencies_locked(
191
+ idx,
192
+ entry.cache_key,
193
+ entry.dependencies,
194
+ )
195
+
196
+ _ensure_publish_current(current_generation)
197
+ set_full_index(idx)
198
+ _ensure_publish_current(current_generation)
199
+ _set_dependency_cache_entries(publishable_entries)
200
+ finally:
201
+ release_lock()
202
+
203
+
114
204
  def publish_dependency_cache_entry(
115
205
  *,
116
206
  cache_key: str,
@@ -8,8 +8,13 @@ from contextvars import ContextVar, Token
8
8
  from types import TracebackType
9
9
  from typing import TYPE_CHECKING, Optional, TypeVar
10
10
 
11
+ from general_manager.logging import get_logger
12
+
11
13
  if TYPE_CHECKING:
12
14
  from general_manager.cache.dependency_cache import DependencyCacheHit
15
+ from general_manager.cache.dependency_publish import (
16
+ PendingDependencyCachePublication,
17
+ )
13
18
 
14
19
  K = TypeVar("K", bound=Hashable)
15
20
  T = TypeVar("T")
@@ -19,14 +24,27 @@ _active_context: ContextVar["CalculationRunContext | None"] = ContextVar(
19
24
  default=None,
20
25
  )
21
26
  ORM_BUCKET_RESULT_PREFIX = "orm_bucket_result"
27
+ DEFAULT_DEPENDENCY_CACHE_PUBLISH_BATCH_SIZE = 1000
28
+ logger = get_logger("cache.run_context")
22
29
 
23
30
 
24
31
  class CalculationRunContext:
25
32
  """Cache calculation work for one request, graph, bulk operation, or task."""
26
33
 
27
- def __init__(self) -> None:
34
+ def __init__(
35
+ self,
36
+ *,
37
+ dependency_cache_publish_batch_size: int = (
38
+ DEFAULT_DEPENDENCY_CACHE_PUBLISH_BATCH_SIZE
39
+ ),
40
+ ) -> None:
28
41
  self._values: dict[Hashable, object] = {}
29
42
  self._dependency_cache_hits: dict[str, DependencyCacheHit] = {}
43
+ self._dependency_cache_pending_publications: dict[
44
+ str,
45
+ PendingDependencyCachePublication,
46
+ ] = {}
47
+ self._dependency_cache_publish_batch_size = dependency_cache_publish_batch_size
30
48
  self._token: Token[CalculationRunContext | None] | None = None
31
49
 
32
50
  def __enter__(self) -> "CalculationRunContext":
@@ -39,11 +57,18 @@ class CalculationRunContext:
39
57
  exc_val: BaseException | None,
40
58
  exc_tb: TracebackType | None,
41
59
  ) -> None:
42
- if self._token is not None:
43
- _active_context.reset(self._token)
44
- self._token = None
45
- self._values.clear()
46
- self._dependency_cache_hits.clear()
60
+ try:
61
+ if exc_type is None:
62
+ self.flush_dependency_cache_publications()
63
+ else:
64
+ self.discard_dependency_cache_publications()
65
+ finally:
66
+ if self._token is not None:
67
+ _active_context.reset(self._token)
68
+ self._token = None
69
+ self._values.clear()
70
+ self._dependency_cache_hits.clear()
71
+ self._dependency_cache_pending_publications.clear()
47
72
 
48
73
  def get_or_set(self, key: Hashable, loader: Callable[[], T]) -> T:
49
74
  """Return a cached value for key, loading it once per active context."""
@@ -74,6 +99,73 @@ class CalculationRunContext:
74
99
  """Return a prefetched dependency-cache hit, or default when absent."""
75
100
  return self._dependency_cache_hits.get(key, default)
76
101
 
102
+ def buffer_dependency_cache_publication(
103
+ self,
104
+ entry: PendingDependencyCachePublication,
105
+ ) -> None:
106
+ """Buffer a dependency-cache miss for guarded batch publication."""
107
+ from general_manager.cache.dependency_cache import DependencyCacheHit
108
+ from general_manager.cache.dependency_publish import release_compute_lease
109
+
110
+ previous_entry = self._dependency_cache_pending_publications.get(
111
+ entry.cache_key
112
+ )
113
+ if previous_entry is not None and previous_entry.lease != entry.lease:
114
+ release_compute_lease(previous_entry.lease)
115
+ self._dependency_cache_pending_publications[entry.cache_key] = entry
116
+ self._dependency_cache_hits[entry.cache_key] = DependencyCacheHit(
117
+ value=entry.result,
118
+ dependencies=entry.dependencies,
119
+ )
120
+ if (
121
+ len(self._dependency_cache_pending_publications)
122
+ >= self._dependency_cache_publish_batch_size
123
+ ):
124
+ self.flush_dependency_cache_publications()
125
+
126
+ def flush_dependency_cache_publications(self) -> None:
127
+ """Publish buffered dependency-cache misses and release their leases."""
128
+ entries = tuple(self._dependency_cache_pending_publications.values())
129
+ if not entries:
130
+ return
131
+ self._dependency_cache_pending_publications.clear()
132
+
133
+ from general_manager.cache.dependency_publish import (
134
+ CachePublishAborted,
135
+ publish_dependency_cache_entries,
136
+ release_compute_lease,
137
+ )
138
+
139
+ try:
140
+ publish_dependency_cache_entries(entries)
141
+ except CachePublishAborted:
142
+ logger.debug(
143
+ "dependency cache batch publish aborted",
144
+ context={"entry_count": len(entries)},
145
+ )
146
+ finally:
147
+ for entry in entries:
148
+ release_compute_lease(entry.lease)
149
+
150
+ def discard_dependency_cache_publications(self) -> None:
151
+ """Drop buffered dependency-cache misses and release their leases."""
152
+ entries = tuple(self._dependency_cache_pending_publications.values())
153
+ if not entries:
154
+ return
155
+ self._dependency_cache_pending_publications.clear()
156
+
157
+ from general_manager.cache.dependency_publish import release_compute_lease
158
+
159
+ for entry in entries:
160
+ release_compute_lease(entry.lease)
161
+
162
+ def discard_dependency_cache_state(self) -> None:
163
+ """Drop dependency-cache hits and buffered publications for this run."""
164
+ try:
165
+ self.discard_dependency_cache_publications()
166
+ finally:
167
+ self._dependency_cache_hits.clear()
168
+
77
169
  def discard_prefix(self, prefix: tuple[Hashable, ...]) -> None:
78
170
  """Discard tuple keys that start with the supplied prefix."""
79
171
  for key in list(self._values):
File without changes