GeneralManager 0.60.1__tar.gz → 0.60.2__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 (259) hide show
  1. {generalmanager-0.60.1/src/GeneralManager.egg-info → generalmanager-0.60.2}/PKG-INFO +1 -1
  2. {generalmanager-0.60.1 → generalmanager-0.60.2}/pyproject.toml +1 -1
  3. {generalmanager-0.60.1 → generalmanager-0.60.2/src/GeneralManager.egg-info}/PKG-INFO +1 -1
  4. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/property.py +4 -1
  5. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/bucket/database_bucket.py +48 -9
  6. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/cache/cache_decorator.py +188 -4
  7. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/cache/cache_tracker.py +62 -4
  8. generalmanager-0.60.2/src/general_manager/cache/dependency_cache.py +576 -0
  9. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/cache/dependency_index.py +9 -9
  10. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/cache/dependency_matching.py +73 -11
  11. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/cache/dependency_publish.py +225 -13
  12. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/cache/dependency_shards.py +39 -38
  13. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/cache/run_context.py +54 -6
  14. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/base_interface.py +209 -46
  15. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/calculation/lifecycle.py +1 -7
  16. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/core/utils.py +1 -14
  17. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +30 -2
  18. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/manager/general_manager.py +125 -46
  19. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/manager/meta.py +83 -20
  20. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/measurement/measurement.py +95 -33
  21. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/utils/json_encoder.py +3 -1
  22. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/utils/make_cache_key.py +57 -12
  23. generalmanager-0.60.1/src/general_manager/cache/dependency_cache.py +0 -297
  24. {generalmanager-0.60.1 → generalmanager-0.60.2}/LICENSE +0 -0
  25. {generalmanager-0.60.1 → generalmanager-0.60.2}/README.md +0 -0
  26. {generalmanager-0.60.1 → generalmanager-0.60.2}/setup.cfg +0 -0
  27. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
  28. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
  29. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/GeneralManager.egg-info/requires.txt +0 -0
  30. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/GeneralManager.egg-info/top_level.txt +0 -0
  31. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/__init__.py +0 -0
  32. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/__init__.py +0 -0
  33. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/api.py +0 -0
  34. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/bucket.py +0 -0
  35. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/cache.py +0 -0
  36. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/dataframes.py +0 -0
  37. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/factory.py +0 -0
  38. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/general_manager.py +0 -0
  39. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/interface.py +0 -0
  40. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/manager.py +0 -0
  41. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/measurement.py +0 -0
  42. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/permission.py +0 -0
  43. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/rule.py +0 -0
  44. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/search.py +0 -0
  45. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/_types/utils.py +0 -0
  46. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/__init__.py +0 -0
  47. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/graphql.py +0 -0
  48. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/graphql_errors.py +0 -0
  49. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/graphql_mutations.py +0 -0
  50. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/graphql_prefetch.py +0 -0
  51. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/graphql_resolvers.py +0 -0
  52. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/graphql_search.py +0 -0
  53. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
  54. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/graphql_subscriptions.py +0 -0
  55. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/graphql_view.py +0 -0
  56. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/graphql_warmup.py +0 -0
  57. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/graphql_warmup_registry.py +0 -0
  58. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
  59. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/mutation.py +0 -0
  60. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/registry.py +0 -0
  61. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/remote_api.py +0 -0
  62. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/remote_invalidation.py +0 -0
  63. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/api/remote_invalidation_client.py +0 -0
  64. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/apps.py +0 -0
  65. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/bootstrap.py +0 -0
  66. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/bucket/__init__.py +0 -0
  67. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/bucket/base_bucket.py +0 -0
  68. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/bucket/calculation_bucket.py +0 -0
  69. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/bucket/group_bucket.py +0 -0
  70. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/bucket/indexing.py +0 -0
  71. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/bucket/request_bucket.py +0 -0
  72. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/cache/__init__.py +0 -0
  73. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/cache/model_dependency_collector.py +0 -0
  74. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/cache/signals.py +0 -0
  75. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/__init__.py +0 -0
  76. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/audit.py +0 -0
  77. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/bootstrap.py +0 -0
  78. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/checks.py +0 -0
  79. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/consumer.py +0 -0
  80. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/errors.py +0 -0
  81. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/__init__.py +0 -0
  82. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/__main__.py +0 -0
  83. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/baseline.py +0 -0
  84. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/diagnostics.py +0 -0
  85. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/fingerprints.py +0 -0
  86. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/fixtures.py +0 -0
  87. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/judges/__init__.py +0 -0
  88. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
  89. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/judges/contract.py +0 -0
  90. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
  91. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
  92. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/readiness.py +0 -0
  93. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/runner.py +0 -0
  94. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/evals/traces.py +0 -0
  95. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/grounding.py +0 -0
  96. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/models.py +0 -0
  97. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/providers/__init__.py +0 -0
  98. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/providers/_shared.py +0 -0
  99. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/providers/anthropic.py +0 -0
  100. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/providers/base.py +0 -0
  101. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/providers/google.py +0 -0
  102. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/providers/ollama.py +0 -0
  103. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/providers/openai.py +0 -0
  104. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/rate_limits.py +0 -0
  105. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/routing.py +0 -0
  106. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/schema_index.py +0 -0
  107. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/settings.py +0 -0
  108. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/signals.py +0 -0
  109. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/system_prompt.py +0 -0
  110. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/tool_metadata.py +0 -0
  111. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/tools.py +0 -0
  112. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/chat/views.py +0 -0
  113. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/conf.py +0 -0
  114. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/dataframes/__init__.py +0 -0
  115. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/dataframes/measurements.py +0 -0
  116. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/factory/__init__.py +0 -0
  117. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/factory/auto_factory.py +0 -0
  118. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/factory/factories.py +0 -0
  119. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/factory/factory_methods.py +0 -0
  120. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/__init__.py +0 -0
  121. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/bundles/__init__.py +0 -0
  122. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/bundles/calculation.py +0 -0
  123. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/bundles/database.py +0 -0
  124. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/bundles/remote_manager.py +0 -0
  125. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/bundles/request.py +0 -0
  126. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/__init__.py +0 -0
  127. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/base.py +0 -0
  128. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/builtin.py +0 -0
  129. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
  130. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
  131. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/configuration.py +0 -0
  132. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/core/observability.py +0 -0
  133. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/exceptions.py +0 -0
  134. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
  135. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
  136. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
  137. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/factory.py +0 -0
  138. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
  139. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
  140. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/orm/history.py +0 -0
  141. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
  142. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
  143. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/orm/support.py +0 -0
  144. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
  145. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
  146. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
  147. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
  148. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
  149. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
  150. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
  151. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
  152. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/registry.py +0 -0
  153. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
  154. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
  155. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/infrastructure/__init__.py +0 -0
  156. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
  157. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
  158. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/interfaces/__init__.py +0 -0
  159. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/interfaces/calculation.py +0 -0
  160. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/interfaces/database.py +0 -0
  161. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/interfaces/existing_model.py +0 -0
  162. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/interfaces/read_only.py +0 -0
  163. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
  164. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/interfaces/request.py +0 -0
  165. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/manifests/__init__.py +0 -0
  166. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/manifests/capability_builder.py +0 -0
  167. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
  168. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/manifests/capability_models.py +0 -0
  169. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/orm_interface.py +0 -0
  170. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/requests.py +0 -0
  171. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/utils/__init__.py +0 -0
  172. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
  173. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/utils/errors.py +0 -0
  174. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/interface/utils/models.py +0 -0
  175. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/logging.py +0 -0
  176. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/management/commands/chat_cleanup.py +0 -0
  177. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/management/commands/graphql_warmup.py +0 -0
  178. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
  179. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/management/commands/search_index.py +0 -0
  180. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/management/commands/search_reconcile.py +0 -0
  181. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
  182. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/management/commands/shell.py +0 -0
  183. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
  184. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
  185. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/manager/__init__.py +0 -0
  186. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/manager/group_manager.py +0 -0
  187. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/manager/input.py +0 -0
  188. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/measurement/__init__.py +0 -0
  189. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/measurement/measurement_field.py +0 -0
  190. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/metrics/__init__.py +0 -0
  191. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/metrics/graphql.py +0 -0
  192. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/migrations/0001_initial.py +0 -0
  193. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
  194. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
  195. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
  196. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/migrations/0004_search_index_state.py +0 -0
  197. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
  198. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
  199. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/migrations/__init__.py +0 -0
  200. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/models.py +0 -0
  201. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/permission/__init__.py +0 -0
  202. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/permission/audit.py +0 -0
  203. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/permission/base_permission.py +0 -0
  204. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/permission/graphql_capabilities.py +0 -0
  205. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/permission/manager_based_permission.py +0 -0
  206. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/permission/mutation_permission.py +0 -0
  207. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/permission/permission_checks.py +0 -0
  208. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/permission/permission_data_manager.py +0 -0
  209. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/permission/utils.py +0 -0
  210. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/public_api_registry.py +0 -0
  211. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/py.typed +0 -0
  212. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/rule/__init__.py +0 -0
  213. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/rule/handler.py +0 -0
  214. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/rule/rule.py +0 -0
  215. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/__init__.py +0 -0
  216. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/async_tasks.py +0 -0
  217. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/backend.py +0 -0
  218. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/backend_registry.py +0 -0
  219. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/backends/__init__.py +0 -0
  220. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/backends/dev.py +0 -0
  221. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/backends/meilisearch.py +0 -0
  222. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/backends/opensearch.py +0 -0
  223. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/backends/typesense.py +0 -0
  224. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/config.py +0 -0
  225. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/indexer.py +0 -0
  226. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/models.py +0 -0
  227. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/reconciliation.py +0 -0
  228. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/registry.py +0 -0
  229. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/tasks.py +0 -0
  230. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/search/utils.py +0 -0
  231. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/seeding/__init__.py +0 -0
  232. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/seeding/manager_landscape.py +0 -0
  233. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/utils/__init__.py +0 -0
  234. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/utils/args_to_kwargs.py +0 -0
  235. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/utils/filter_parser.py +0 -0
  236. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/utils/format_string.py +0 -0
  237. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/utils/none_to_zero.py +0 -0
  238. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/utils/path_mapping.py +0 -0
  239. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/utils/public_api.py +0 -0
  240. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/utils/testing.py +0 -0
  241. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/utils/type_checks.py +0 -0
  242. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/__init__.py +0 -0
  243. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/actions.py +0 -0
  244. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/backend_registry.py +0 -0
  245. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/backends/__init__.py +0 -0
  246. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/backends/celery.py +0 -0
  247. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/backends/local.py +0 -0
  248. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/backends/n8n.py +0 -0
  249. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/config.py +0 -0
  250. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/engine.py +0 -0
  251. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/event_registry.py +0 -0
  252. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/events.py +0 -0
  253. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/models.py +0 -0
  254. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/signal_bridge.py +0 -0
  255. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/tasks.py +0 -0
  256. {generalmanager-0.60.1 → generalmanager-0.60.2}/src/general_manager/workflow/telemetry.py +0 -0
  257. {generalmanager-0.60.1 → generalmanager-0.60.2}/tests/test_settings.py +0 -0
  258. {generalmanager-0.60.1 → generalmanager-0.60.2}/tests/test_urls.py +0 -0
  259. {generalmanager-0.60.1 → generalmanager-0.60.2}/tests/testing_asgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.60.1
3
+ Version: 0.60.2
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.60.1"
7
+ version = "0.60.2"
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.60.1
3
+ Version: 0.60.2
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
@@ -142,7 +142,10 @@ class GraphQLProperty(property):
142
142
  @wraps(fget)
143
143
  def resolver(instance: object) -> object:
144
144
  """Resolve the property through the selected cached wrapper."""
145
- return self._get_cached_fget()(instance)
145
+ cached_fget = self._cached_fget
146
+ if cached_fget is None:
147
+ cached_fget = self._get_cached_fget()
148
+ return cached_fget(instance)
146
149
 
147
150
  super().__init__(resolver, doc=doc)
148
151
  self.is_graphql_resolver = True
@@ -11,7 +11,10 @@ from django.db.models.sql.query import Query
11
11
 
12
12
  from general_manager.bucket.base_bucket import Bucket
13
13
  from general_manager.cache.cache_tracker import DependencyTracker
14
- from general_manager.cache.dependency_index import serialize_dependency_identifier
14
+ from general_manager.cache.dependency_index import (
15
+ Dependency,
16
+ serialize_dependency_identifier,
17
+ )
15
18
  from general_manager.cache.run_context import current_calculation_run_context
16
19
  from general_manager.manager.general_manager import GeneralManager
17
20
  from general_manager.utils.filter_parser import create_filter_function
@@ -605,10 +608,29 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
605
608
  def _can_cache_run_scoped_managers(self) -> bool:
606
609
  if self._manager_class.__init__ is not GeneralManager.__init__:
607
610
  return False
611
+ if not type.__getattribute__(
612
+ self._manager_class,
613
+ "_gm_uses_default_identification_dependency_active",
614
+ ):
615
+ return False
608
616
  return callable(
609
617
  getattr(self._manager_class.Interface, "_from_trusted_orm_instance", None)
610
618
  )
611
619
 
620
+ def _manager_identification_dependencies(
621
+ self,
622
+ managers: tuple[GeneralManagerType, ...],
623
+ ) -> frozenset[Dependency]:
624
+ manager_name = type.__getattribute__(self._manager_class, "__name__")
625
+ return frozenset(
626
+ (
627
+ manager_name,
628
+ "identification",
629
+ serialize_dependency_identifier(manager.identification),
630
+ )
631
+ for manager in managers
632
+ )
633
+
612
634
  def _get_run_scoped_managers(self) -> tuple[GeneralManagerType, ...] | None:
613
635
  """Load or reuse manager wrappers for a trusted row snapshot."""
614
636
  if not self._can_cache_run_scoped_managers():
@@ -622,16 +644,24 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
622
644
  cached = context.get_orm_bucket_managers(signature)
623
645
  if cached is not None:
624
646
  managers = cast(tuple[GeneralManagerType, ...], cached)
625
- for manager in managers:
626
- self._manager_class._track_identification_dependency(
627
- manager.identification
628
- )
647
+ dependencies = context.get_orm_bucket_manager_dependencies(signature)
648
+ if dependencies is not None:
649
+ DependencyTracker._track_many_validated(dependencies)
650
+ else:
651
+ for manager in managers:
652
+ self._manager_class._track_identification_dependency(
653
+ manager.identification
654
+ )
629
655
  return managers
630
656
  rows = self._get_run_scoped_rows()
631
657
  if rows is None:
632
658
  return None
633
659
  managers = tuple(self._build_manager_from_instance(row) for row in rows)
634
- context.set_orm_bucket_managers(signature, managers)
660
+ context.set_orm_bucket_managers(
661
+ signature,
662
+ managers,
663
+ self._manager_identification_dependencies(managers),
664
+ )
635
665
  return managers
636
666
 
637
667
  @staticmethod
@@ -675,9 +705,18 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
675
705
 
676
706
  def _track_effective_dependencies(self) -> None:
677
707
  """Record the bucket's effective filter/exclude state when it is evaluated."""
678
- manager_name = self._manager_class.__name__
679
- normalized_filters = self._normalize_dependency_mapping(self.filters)
680
- normalized_excludes = self._normalize_dependency_mapping(self.excludes)
708
+ manager_name = type.__getattribute__(self._manager_class, "__name__")
709
+ needs_sort_payload = bool(self._sort_keys)
710
+ normalized_filters = (
711
+ self._normalize_dependency_mapping(self.filters)
712
+ if self.filters or needs_sort_payload
713
+ else {}
714
+ )
715
+ normalized_excludes = (
716
+ self._normalize_dependency_mapping(self.excludes)
717
+ if self.excludes or needs_sort_payload
718
+ else {}
719
+ )
681
720
  if self.filters:
682
721
  DependencyTracker._track_validated(
683
722
  manager_name,
@@ -2,6 +2,7 @@
2
2
 
3
3
  from collections.abc import Callable, Iterable
4
4
  from functools import wraps
5
+ from hashlib import sha256
5
6
  from typing import (
6
7
  Literal,
7
8
  Protocol,
@@ -15,6 +16,16 @@ from django.core.cache import cache as django_cache
15
16
  from general_manager.cache.cache_tracker import DependencyTracker
16
17
  from general_manager.cache.dependency_cache import (
17
18
  DependencyCacheHit,
19
+ dependency_cache_prefetch_bundle_key,
20
+ dependency_cache_prefetch_segment_bundle_key,
21
+ dependency_cache_prefetch_segment_index_key,
22
+ dependency_cache_prefetch_segment_value_bundle_key,
23
+ dependency_cache_prefetch_value_bundle_key,
24
+ read_many_dependency_cache_hits,
25
+ read_many_dependency_cache_prefetch_bundle_hits,
26
+ read_many_dependency_cache_prefetch_bundle_values,
27
+ read_dependency_cache_prefetch_bundle_hits,
28
+ read_dependency_cache_prefetch_bundle_values,
18
29
  read_dependency_cache_hit,
19
30
  replay_dependency_cache_hit,
20
31
  )
@@ -63,6 +74,10 @@ FuncT = TypeVar("FuncT", bound=Callable[..., object])
63
74
  CacheScope = Literal["dependency", "run", "timeout", "none"]
64
75
 
65
76
  _SENTINEL = object()
77
+ _RUN_CACHE_MISS = object()
78
+ _DEPENDENCY_CACHE_PREFETCH_MANIFEST_PREFIX = "dependency_cache_prefetch_manifest"
79
+ _DEPENDENCY_CACHE_PREFETCH_ATTEMPT_PREFIX = "dependency_cache_prefetch_attempt"
80
+ _DEPENDENCY_CACHE_PREFETCH_VALUE_PREFIX = "dependency_cache_prefetch_value"
66
81
  logger = get_logger("cache.decorator")
67
82
 
68
83
 
@@ -169,7 +184,130 @@ def cached(
169
184
  if timeout is not None and cache != "timeout":
170
185
  raise CacheTimeoutConfigurationError.unexpected_timeout()
171
186
 
187
+ def dependency_cache_prefetch_manifest_key(
188
+ decorated_func: Callable[..., object],
189
+ ) -> str:
190
+ raw = f"{decorated_func.__module__}:{decorated_func.__qualname__}".encode()
191
+ digest = sha256(raw, usedforsecurity=False).hexdigest()
192
+ return f"{_DEPENDENCY_CACHE_PREFETCH_MANIFEST_PREFIX}:{digest}"
193
+
194
+ def prefetch_dependency_cache_manifest(
195
+ context: object,
196
+ manifest_key: str,
197
+ ) -> None:
198
+ if not callable(getattr(cache_backend, "get_many", None)):
199
+ return
200
+ if not all(
201
+ hasattr(context, attr)
202
+ for attr in (
203
+ "get",
204
+ "set",
205
+ "set_dependency_cache_hits",
206
+ )
207
+ ):
208
+ return
209
+ attempt_key = (
210
+ _DEPENDENCY_CACHE_PREFETCH_ATTEMPT_PREFIX,
211
+ id(cache_backend),
212
+ manifest_key,
213
+ )
214
+ if context.get(attempt_key, False): # type: ignore[attr-defined]
215
+ return
216
+ context.set(attempt_key, True) # type: ignore[attr-defined]
217
+
218
+ segment_tokens = cache_backend.get(
219
+ dependency_cache_prefetch_segment_index_key(manifest_key),
220
+ (),
221
+ )
222
+ if isinstance(segment_tokens, (tuple, list, frozenset, set)):
223
+ valid_segment_tokens = tuple(
224
+ token for token in segment_tokens if isinstance(token, str)
225
+ )
226
+ else:
227
+ valid_segment_tokens = ()
228
+
229
+ if not DependencyTracker.is_active():
230
+ if valid_segment_tokens:
231
+ segment_value_keys = tuple(
232
+ dependency_cache_prefetch_segment_value_bundle_key(
233
+ manifest_key,
234
+ segment_token,
235
+ )
236
+ for segment_token in valid_segment_tokens
237
+ )
238
+ segment_values = read_many_dependency_cache_prefetch_bundle_values(
239
+ cache_backend,
240
+ segment_value_keys,
241
+ )
242
+ if segment_values:
243
+ for cache_key, value in segment_values.items():
244
+ context.set( # type: ignore[attr-defined]
245
+ (
246
+ _DEPENDENCY_CACHE_PREFETCH_VALUE_PREFIX,
247
+ id(cache_backend),
248
+ cache_key,
249
+ ),
250
+ value,
251
+ )
252
+ return
253
+
254
+ bundle_values = read_dependency_cache_prefetch_bundle_values(
255
+ cache_backend,
256
+ dependency_cache_prefetch_value_bundle_key(manifest_key),
257
+ )
258
+ if bundle_values:
259
+ for cache_key, value in bundle_values.items():
260
+ context.set( # type: ignore[attr-defined]
261
+ (
262
+ _DEPENDENCY_CACHE_PREFETCH_VALUE_PREFIX,
263
+ id(cache_backend),
264
+ cache_key,
265
+ ),
266
+ value,
267
+ )
268
+ return
269
+
270
+ if valid_segment_tokens:
271
+ segment_bundle_keys = tuple(
272
+ dependency_cache_prefetch_segment_bundle_key(
273
+ manifest_key,
274
+ segment_token,
275
+ )
276
+ for segment_token in valid_segment_tokens
277
+ )
278
+ segment_hits = read_many_dependency_cache_prefetch_bundle_hits(
279
+ cache_backend,
280
+ segment_bundle_keys,
281
+ )
282
+ if segment_hits:
283
+ context.set_dependency_cache_hits(segment_hits) # type: ignore[attr-defined]
284
+ return
285
+
286
+ bundle_hits = read_dependency_cache_prefetch_bundle_hits(
287
+ cache_backend,
288
+ dependency_cache_prefetch_bundle_key(manifest_key),
289
+ )
290
+ if bundle_hits:
291
+ context.set_dependency_cache_hits(bundle_hits) # type: ignore[attr-defined]
292
+ return
293
+
294
+ manifest = cache_backend.get(manifest_key, ())
295
+ if not isinstance(manifest, (tuple, list, frozenset, set)):
296
+ return
297
+ cache_keys = tuple(key for key in manifest if isinstance(key, str))
298
+ if not cache_keys:
299
+ return
300
+ hits = read_many_dependency_cache_hits(cache_backend, cache_keys)
301
+ if hits:
302
+ context.set_dependency_cache_hits(hits) # type: ignore[attr-defined]
303
+
172
304
  def decorator(decorated_func: FuncT) -> FuncT:
305
+ prefetch_manifest_key = (
306
+ dependency_cache_prefetch_manifest_key(decorated_func)
307
+ if cache == "dependency"
308
+ else None
309
+ )
310
+
173
311
  @wraps(decorated_func)
174
312
  def wrapper(*args: object, **kwargs: object) -> object:
175
313
  if cache == "none":
@@ -177,11 +315,21 @@ def cached(
177
315
 
178
316
  if cache == "run":
179
317
  key = make_cache_key(decorated_func, args, kwargs)
318
+ active_context = current_calculation_run_context()
319
+ if active_context is not None:
320
+ cached_run_value = active_context.get(key, _RUN_CACHE_MISS)
321
+ if cached_run_value is not _RUN_CACHE_MISS:
322
+ return cached_run_value
323
+ result = decorated_func(*args, **kwargs)
324
+ active_context.set(key, result)
325
+ return result
180
326
  with ensure_calculation_run_context() as context:
181
- return context.get_or_set(
182
- key,
183
- lambda: decorated_func(*args, **kwargs),
184
- )
327
+ cached_run_value = context.get(key, _RUN_CACHE_MISS)
328
+ if cached_run_value is not _RUN_CACHE_MISS:
329
+ return cached_run_value
330
+ result = decorated_func(*args, **kwargs)
331
+ context.set(key, result)
332
+ return result
185
333
 
186
334
  key = make_cache_key(decorated_func, args, kwargs)
187
335
 
@@ -223,8 +371,23 @@ def cached(
223
371
  )
224
372
  return hit.value
225
373
 
374
+ def prefetched_value_from_context(context: object) -> object:
375
+ if DependencyTracker.is_active():
376
+ return _SENTINEL
377
+ return context.get( # type: ignore[attr-defined]
378
+ (
379
+ _DEPENDENCY_CACHE_PREFETCH_VALUE_PREFIX,
380
+ id(cache_backend),
381
+ key,
382
+ ),
383
+ _SENTINEL,
384
+ )
385
+
226
386
  prefetch_context = current_calculation_run_context()
227
387
  if prefetch_context is not None:
388
+ prefetched_value = prefetched_value_from_context(prefetch_context)
389
+ if prefetched_value is not _SENTINEL:
390
+ return prefetched_value
228
391
  prefetched_hit = prefetch_context.get_dependency_cache_hit(
229
392
  key, _SENTINEL
230
393
  )
@@ -233,6 +396,22 @@ def cached(
233
396
  prefetched_hit,
234
397
  "cache hit from dependency prefetch",
235
398
  )
399
+ if prefetch_manifest_key is not None:
400
+ prefetch_dependency_cache_manifest(
401
+ prefetch_context,
402
+ prefetch_manifest_key,
403
+ )
404
+ prefetched_value = prefetched_value_from_context(prefetch_context)
405
+ if prefetched_value is not _SENTINEL:
406
+ return prefetched_value
407
+ prefetched_hit = prefetch_context.get_dependency_cache_hit(
408
+ key, _SENTINEL
409
+ )
410
+ if isinstance(prefetched_hit, DependencyCacheHit):
411
+ return return_cached_hit(
412
+ prefetched_hit,
413
+ "cache hit from dependency prefetch manifest",
414
+ )
236
415
 
237
416
  cached_hit = read_dependency_cache_hit(
238
417
  cache_backend,
@@ -288,6 +467,10 @@ def cached(
288
467
  timeout=timeout,
289
468
  started_generation=started_generation,
290
469
  lease=lease,
470
+ dependencies_trusted=DependencyTracker._dependencies_are_tracker_captured(
471
+ dependencies
472
+ ),
473
+ prefetch_manifest_key=prefetch_manifest_key,
291
474
  )
292
475
  )
293
476
  lease_transferred_to_context = True
@@ -305,6 +488,7 @@ def cached(
305
488
  if record_fn is record_dependencies
306
489
  else record_many
307
490
  ),
491
+ prefetch_manifest_key=prefetch_manifest_key,
308
492
  )
309
493
  except CachePublishAborted:
310
494
  logger.debug(
@@ -1,5 +1,6 @@
1
1
  """Context manager utilities for tracking cache dependencies per thread."""
2
2
 
3
+ from collections.abc import Collection, Iterable
3
4
  import threading
4
5
  from types import TracebackType
5
6
 
@@ -30,18 +31,32 @@ class _InvalidDependencyTrackerOperationError(ValueError):
30
31
  super().__init__(f"Unsupported dependency tracker operation: {operation!r}")
31
32
 
32
33
 
34
+ class _TrackedDependencySet(set[Dependency]):
35
+ """Dependency set returned by DependencyTracker for framework-captured deps."""
36
+
37
+
33
38
  class _DependencyStorage(threading.local):
34
39
  """Thread-local dependency tracking stack."""
35
40
 
36
41
  def __init__(self) -> None:
37
42
  """Initialize an inactive dependency stack for one thread."""
38
- self.dependencies: list[set[Dependency]] = []
43
+ self.dependencies: list[_TrackedDependencySet] = []
39
44
  self.depth = -1
45
+ self.stack_version = 0
46
+ self.last_dependency: Dependency | None = None
47
+ self.last_dependency_stack_version = -1
48
+ self.seen_dependencies: set[Dependency] = set()
49
+ self.seen_dependencies_stack_version = -1
40
50
 
41
51
  def reset(self) -> None:
42
52
  """Clear all active dependency scopes for the current thread."""
43
53
  self.dependencies.clear()
44
54
  self.depth = -1
55
+ self.stack_version += 1
56
+ self.last_dependency = None
57
+ self.last_dependency_stack_version = -1
58
+ self.seen_dependencies.clear()
59
+ self.seen_dependencies_stack_version = -1
45
60
 
46
61
 
47
62
  _dependency_storage = _DependencyStorage()
@@ -69,7 +84,8 @@ class DependencyTracker:
69
84
  thread-local storage does not mutate sets that were already returned.
70
85
  """
71
86
  _dependency_storage.depth += 1
72
- _dependency_storage.dependencies.append(set())
87
+ _dependency_storage.dependencies.append(_TrackedDependencySet())
88
+ _dependency_storage.stack_version += 1
73
89
  return _dependency_storage.dependencies[_dependency_storage.depth]
74
90
 
75
91
  def __exit__(
@@ -98,6 +114,9 @@ class DependencyTracker:
98
114
  return
99
115
  _dependency_storage.dependencies.pop()
100
116
  _dependency_storage.depth -= 1
117
+ _dependency_storage.stack_version += 1
118
+ _dependency_storage.last_dependency = None
119
+ _dependency_storage.last_dependency_stack_version = -1
101
120
 
102
121
  @staticmethod
103
122
  def track(
@@ -144,11 +163,50 @@ class DependencyTracker:
144
163
  identifier: str,
145
164
  ) -> None:
146
165
  """Record an already-validated dependency tuple in active collectors."""
147
- if _dependency_storage.depth < 0:
166
+ storage = _dependency_storage
167
+ if storage.depth < 0:
148
168
  return
169
+ if storage.last_dependency_stack_version == storage.stack_version:
170
+ last_dependency = storage.last_dependency
171
+ if (
172
+ last_dependency is not None
173
+ and last_dependency[0] == class_name
174
+ and last_dependency[1] == operation
175
+ and last_dependency[2] == identifier
176
+ ):
177
+ return
149
178
  dependency = (class_name, operation, identifier)
179
+ if storage.seen_dependencies_stack_version != storage.stack_version:
180
+ storage.seen_dependencies.clear()
181
+ storage.seen_dependencies_stack_version = storage.stack_version
182
+ elif dependency in storage.seen_dependencies:
183
+ return
184
+ if storage.depth == 0:
185
+ storage.dependencies[0].add(dependency)
186
+ else:
187
+ for dep_set in storage.dependencies:
188
+ dep_set.add(dependency)
189
+ storage.last_dependency = dependency
190
+ storage.last_dependency_stack_version = storage.stack_version
191
+ storage.seen_dependencies.add(dependency)
192
+
193
+ @staticmethod
194
+ def _track_many_validated(dependencies: Iterable[Dependency]) -> None:
195
+ """Record already-validated dependency tuples in active collectors."""
196
+ if _dependency_storage.depth < 0:
197
+ return
198
+ reusable_dependencies: Collection[Dependency]
199
+ if isinstance(dependencies, Collection):
200
+ reusable_dependencies = dependencies
201
+ else:
202
+ reusable_dependencies = tuple(dependencies)
150
203
  for dep_set in _dependency_storage.dependencies:
151
- dep_set.add(dependency)
204
+ dep_set.update(reusable_dependencies)
205
+
206
+ @staticmethod
207
+ def _dependencies_are_tracker_captured(dependencies: object) -> bool:
208
+ """Return whether dependencies came from this tracker implementation."""
209
+ return isinstance(dependencies, _TrackedDependencySet)
152
210
 
153
211
  @staticmethod
154
212
  def is_active() -> bool: