GeneralManager 0.86.0__tar.gz → 0.86.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 (342) hide show
  1. {generalmanager-0.86.0/src/GeneralManager.egg-info → generalmanager-0.86.2}/PKG-INFO +1 -1
  2. {generalmanager-0.86.0 → generalmanager-0.86.2}/pyproject.toml +1 -1
  3. {generalmanager-0.86.0 → generalmanager-0.86.2/src/GeneralManager.egg-info}/PKG-INFO +1 -1
  4. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql.py +76 -30
  5. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/data_change_context.py +41 -0
  6. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/signals.py +22 -11
  7. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/mutations.py +33 -2
  8. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm_utils/update_state.py +11 -3
  9. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/signal_bridge.py +4 -2
  10. {generalmanager-0.86.0 → generalmanager-0.86.2}/LICENSE +0 -0
  11. {generalmanager-0.86.0 → generalmanager-0.86.2}/README.md +0 -0
  12. {generalmanager-0.86.0 → generalmanager-0.86.2}/setup.cfg +0 -0
  13. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
  14. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
  15. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/GeneralManager.egg-info/requires.txt +0 -0
  16. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/GeneralManager.egg-info/top_level.txt +0 -0
  17. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/__init__.py +0 -0
  18. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/__init__.py +0 -0
  19. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/api.py +0 -0
  20. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/bucket.py +0 -0
  21. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/cache.py +0 -0
  22. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/dataframes.py +0 -0
  23. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/factory.py +0 -0
  24. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/general_manager.py +0 -0
  25. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/interface.py +0 -0
  26. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/manager.py +0 -0
  27. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/measurement.py +0 -0
  28. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/permission.py +0 -0
  29. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/rule.py +0 -0
  30. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/search.py +0 -0
  31. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/_types/utils.py +0 -0
  32. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/__init__.py +0 -0
  33. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_as_of.py +0 -0
  34. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_errors.py +0 -0
  35. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_groups.py +0 -0
  36. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_identifiers.py +0 -0
  37. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_mutations.py +0 -0
  38. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_ordering.py +0 -0
  39. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_output.py +0 -0
  40. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_prefetch.py +0 -0
  41. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_relations.py +0 -0
  42. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_resolvers.py +0 -0
  43. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_search.py +0 -0
  44. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
  45. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_subscriptions.py +0 -0
  46. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_type.py +0 -0
  47. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_view.py +0 -0
  48. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_warmup.py +0 -0
  49. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_warmup_registry.py +0 -0
  50. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
  51. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/mutation.py +0 -0
  52. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/notification_batching.py +0 -0
  53. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/property.py +0 -0
  54. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/registry.py +0 -0
  55. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/remote_api.py +0 -0
  56. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/remote_invalidation.py +0 -0
  57. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/api/remote_invalidation_client.py +0 -0
  58. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/apps.py +0 -0
  59. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/as_of.py +0 -0
  60. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/bootstrap.py +0 -0
  61. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/bucket/__init__.py +0 -0
  62. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/bucket/_materialized_bucket.py +0 -0
  63. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/bucket/_ordering.py +0 -0
  64. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/bucket/base_bucket.py +0 -0
  65. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/bucket/calculation_bucket.py +0 -0
  66. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/bucket/database_bucket.py +0 -0
  67. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/bucket/excel_bucket.py +0 -0
  68. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/bucket/group_bucket.py +0 -0
  69. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/bucket/indexing.py +0 -0
  70. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/bucket/projection.py +0 -0
  71. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/bucket/request_bucket.py +0 -0
  72. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/__init__.py +0 -0
  73. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/_dependency_graph.py +0 -0
  74. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/batch_refresh.py +0 -0
  75. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/cache_decorator.py +0 -0
  76. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/cache_tracker.py +0 -0
  77. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/dependency_cache.py +0 -0
  78. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/dependency_index.py +0 -0
  79. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/dependency_matching.py +0 -0
  80. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/dependency_publish.py +0 -0
  81. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/dependency_shards.py +0 -0
  82. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/model_dependency_collector.py +0 -0
  83. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/run_context.py +0 -0
  84. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/run_context_lru.py +0 -0
  85. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/cache/transaction_barrier.py +0 -0
  86. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/__init__.py +0 -0
  87. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/audit.py +0 -0
  88. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/bootstrap.py +0 -0
  89. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/checks.py +0 -0
  90. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/consumer.py +0 -0
  91. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/context.py +0 -0
  92. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/errors.py +0 -0
  93. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/__init__.py +0 -0
  94. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/__main__.py +0 -0
  95. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/baseline.py +0 -0
  96. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/basic_queries.yaml +0 -0
  97. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/demo_readiness.yaml +0 -0
  98. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/edge_cases.yaml +0 -0
  99. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/follow_ups.yaml +0 -0
  100. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/large_schema.yaml +0 -0
  101. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/multi_hop.yaml +0 -0
  102. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/datasets/planned_orchestration.yaml +0 -0
  103. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/diagnostics.py +0 -0
  104. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/fingerprints.py +0 -0
  105. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/fixtures.py +0 -0
  106. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/judges/__init__.py +0 -0
  107. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
  108. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/judges/contract.py +0 -0
  109. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
  110. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
  111. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/readiness.py +0 -0
  112. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/runner.py +0 -0
  113. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/evals/traces.py +0 -0
  114. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/grounding.py +0 -0
  115. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/models.py +0 -0
  116. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/__init__.py +0 -0
  117. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/budget.py +0 -0
  118. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/calculations.py +0 -0
  119. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/catalog.py +0 -0
  120. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/config.py +0 -0
  121. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/events.py +0 -0
  122. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/evidence.py +0 -0
  123. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/models.py +0 -0
  124. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/planner.py +0 -0
  125. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/provider_calls.py +0 -0
  126. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/resolver.py +0 -0
  127. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/routing.py +0 -0
  128. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/scheduler.py +0 -0
  129. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/synthesis.py +0 -0
  130. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/planned/validation.py +0 -0
  131. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/providers/__init__.py +0 -0
  132. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/providers/_shared.py +0 -0
  133. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/providers/anthropic.py +0 -0
  134. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/providers/base.py +0 -0
  135. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/providers/google.py +0 -0
  136. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/providers/ollama.py +0 -0
  137. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/providers/openai.py +0 -0
  138. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/rate_limits.py +0 -0
  139. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/routing.py +0 -0
  140. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/schema_index.py +0 -0
  141. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/settings.py +0 -0
  142. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/signals.py +0 -0
  143. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/system_prompt.py +0 -0
  144. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/tool_metadata.py +0 -0
  145. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/tools.py +0 -0
  146. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/turns.py +0 -0
  147. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/chat/views.py +0 -0
  148. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/conf.py +0 -0
  149. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/dataframes/__init__.py +0 -0
  150. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/dataframes/measurements.py +0 -0
  151. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/factory/__init__.py +0 -0
  152. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/factory/auto_factory.py +0 -0
  153. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/factory/factories.py +0 -0
  154. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/factory/factory_methods.py +0 -0
  155. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/__init__.py +0 -0
  156. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/base_interface.py +0 -0
  157. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/bundles/__init__.py +0 -0
  158. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/bundles/calculation.py +0 -0
  159. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/bundles/database.py +0 -0
  160. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/bundles/excel.py +0 -0
  161. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/bundles/remote_manager.py +0 -0
  162. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/bundles/request.py +0 -0
  163. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/__init__.py +0 -0
  164. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/base.py +0 -0
  165. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/builtin.py +0 -0
  166. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
  167. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
  168. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/calculation/input_resolution.py +0 -0
  169. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
  170. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/configuration.py +0 -0
  171. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/core/observability.py +0 -0
  172. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/core/utils.py +0 -0
  173. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/excel/__init__.py +0 -0
  174. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/exceptions.py +0 -0
  175. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
  176. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
  177. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
  178. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/factory.py +0 -0
  179. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
  180. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
  181. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/bulk.py +0 -0
  182. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/create_many.py +0 -0
  183. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/history.py +0 -0
  184. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
  185. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm/support.py +0 -0
  186. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
  187. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
  188. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
  189. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
  190. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
  191. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
  192. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
  193. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
  194. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
  195. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/registry.py +0 -0
  196. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
  197. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
  198. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/excel.py +0 -0
  199. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/excel_store.py +0 -0
  200. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/excel_workbook.py +0 -0
  201. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/infrastructure/__init__.py +0 -0
  202. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
  203. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
  204. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/__init__.py +0 -0
  205. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/calculation.py +0 -0
  206. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/database.py +0 -0
  207. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/excel.py +0 -0
  208. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/existing_model.py +0 -0
  209. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/read_only.py +0 -0
  210. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
  211. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/interfaces/request.py +0 -0
  212. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/manifests/__init__.py +0 -0
  213. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/manifests/capability_builder.py +0 -0
  214. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
  215. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/manifests/capability_models.py +0 -0
  216. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/orm_interface.py +0 -0
  217. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/requests.py +0 -0
  218. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/utils/__init__.py +0 -0
  219. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
  220. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/utils/errors.py +0 -0
  221. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/utils/history.py +0 -0
  222. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/interface/utils/models.py +0 -0
  223. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/logging.py +0 -0
  224. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/management/commands/chat_cleanup.py +0 -0
  225. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/management/commands/cleanup_upload_intents.py +0 -0
  226. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/management/commands/graphql_warmup.py +0 -0
  227. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
  228. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/management/commands/search_index.py +0 -0
  229. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/management/commands/search_reconcile.py +0 -0
  230. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
  231. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/management/commands/shell.py +0 -0
  232. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/management/commands/sync_read_only.py +0 -0
  233. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
  234. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
  235. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/manager/__init__.py +0 -0
  236. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/manager/bulk_create.py +0 -0
  237. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/manager/general_manager.py +0 -0
  238. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/manager/group_manager.py +0 -0
  239. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/manager/input.py +0 -0
  240. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/manager/meta.py +0 -0
  241. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/measurement/__init__.py +0 -0
  242. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/measurement/measurement.py +0 -0
  243. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/measurement/measurement_field.py +0 -0
  244. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/metrics/__init__.py +0 -0
  245. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/metrics/graphql.py +0 -0
  246. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0001_initial.py +0 -0
  247. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
  248. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
  249. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
  250. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0004_search_index_state.py +0 -0
  251. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
  252. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
  253. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0007_upload_intent.py +0 -0
  254. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0008_upload_finalization_versions.py +0 -0
  255. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0009_upload_cleanup_state.py +0 -0
  256. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0010_chat_pending_confirmation_portable_uniqueness.py +0 -0
  257. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0011_search_index_state_dirty_generation.py +0 -0
  258. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/0012_chat_context_watermarks.py +0 -0
  259. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/migrations/__init__.py +0 -0
  260. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/models.py +0 -0
  261. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/permission/__init__.py +0 -0
  262. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/permission/audit.py +0 -0
  263. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/permission/base_permission.py +0 -0
  264. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/permission/graphql_capabilities.py +0 -0
  265. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/permission/manager_based_permission.py +0 -0
  266. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/permission/mutation_permission.py +0 -0
  267. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/permission/permission_checks.py +0 -0
  268. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/permission/permission_data_manager.py +0 -0
  269. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/permission/utils.py +0 -0
  270. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/public_api_registry.py +0 -0
  271. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/py.typed +0 -0
  272. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/rule/__init__.py +0 -0
  273. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/rule/handler.py +0 -0
  274. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/rule/rule.py +0 -0
  275. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/__init__.py +0 -0
  276. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/async_tasks.py +0 -0
  277. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/backend.py +0 -0
  278. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/backend_registry.py +0 -0
  279. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/backends/__init__.py +0 -0
  280. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/backends/dev.py +0 -0
  281. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/backends/meilisearch.py +0 -0
  282. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/backends/opensearch.py +0 -0
  283. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/backends/typesense.py +0 -0
  284. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/checks.py +0 -0
  285. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/config.py +0 -0
  286. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/identity.py +0 -0
  287. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/indexer.py +0 -0
  288. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/invalidation.py +0 -0
  289. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/m2m_invalidation.py +0 -0
  290. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/models.py +0 -0
  291. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/reconciliation.py +0 -0
  292. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/registry.py +0 -0
  293. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/tasks.py +0 -0
  294. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/search/utils.py +0 -0
  295. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/seeding/__init__.py +0 -0
  296. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/seeding/manager_landscape.py +0 -0
  297. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/__init__.py +0 -0
  298. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/adapters.py +0 -0
  299. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/checks.py +0 -0
  300. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/config.py +0 -0
  301. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/errors.py +0 -0
  302. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/finalization.py +0 -0
  303. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/graphql.py +0 -0
  304. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/graphql_types.py +0 -0
  305. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/metrics.py +0 -0
  306. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/models.py +0 -0
  307. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/public.py +0 -0
  308. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/s3.py +0 -0
  309. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/services.py +0 -0
  310. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/tokens.py +0 -0
  311. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/types.py +0 -0
  312. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/urls.py +0 -0
  313. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/uploads/views.py +0 -0
  314. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/utils/__init__.py +0 -0
  315. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/utils/_args_to_kwargs.py +0 -0
  316. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/utils/_cache_key_encoder.py +0 -0
  317. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/utils/_make_cache_key.py +0 -0
  318. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/utils/_none_to_zero.py +0 -0
  319. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/utils/filter_parser.py +0 -0
  320. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/utils/format_string.py +0 -0
  321. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/utils/json_encoder.py +0 -0
  322. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/utils/path_mapping.py +0 -0
  323. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/utils/public_api.py +0 -0
  324. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/utils/testing.py +0 -0
  325. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/utils/type_checks.py +0 -0
  326. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/__init__.py +0 -0
  327. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/actions.py +0 -0
  328. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/backend_registry.py +0 -0
  329. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/backends/__init__.py +0 -0
  330. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/backends/celery.py +0 -0
  331. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/backends/local.py +0 -0
  332. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/backends/n8n.py +0 -0
  333. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/config.py +0 -0
  334. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/engine.py +0 -0
  335. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/event_registry.py +0 -0
  336. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/events.py +0 -0
  337. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/models.py +0 -0
  338. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/tasks.py +0 -0
  339. {generalmanager-0.86.0 → generalmanager-0.86.2}/src/general_manager/workflow/telemetry.py +0 -0
  340. {generalmanager-0.86.0 → generalmanager-0.86.2}/tests/test_settings.py +0 -0
  341. {generalmanager-0.86.0 → generalmanager-0.86.2}/tests/test_urls.py +0 -0
  342. {generalmanager-0.86.0 → generalmanager-0.86.2}/tests/testing_asgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.86.0
3
+ Version: 0.86.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.86.0"
7
+ version = "0.86.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.86.0
3
+ Version: 0.86.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
@@ -47,6 +47,7 @@ from general_manager.bucket._materialized_bucket import MaterializedBucket
47
47
  from general_manager.bucket.group_bucket import GroupBucket
48
48
  from general_manager.bucket.request_bucket import RequestBucket
49
49
  from general_manager.bucket.indexing import freeze_bucket_index_value
50
+ from general_manager.cache.cache_tracker import DependencyTracker
50
51
  from general_manager.cache.dependency_index import (
51
52
  Dependency,
52
53
  )
@@ -116,6 +117,7 @@ from general_manager.api.graphql_resolvers import (
116
117
  apply_pagination as _apply_pagination_fn,
117
118
  apply_grouping as _apply_grouping_fn,
118
119
  check_read_permission as _check_read_permission_fn,
120
+ check_read_permission_for_user as _check_read_permission_for_user_fn,
119
121
  can_read_instance_for_user as _can_read_instance_for_user_fn,
120
122
  create_measurement_resolver as _create_measurement_resolver_fn,
121
123
  create_normal_resolver as _create_normal_resolver_fn,
@@ -2391,6 +2393,36 @@ class GraphQL:
2391
2393
  raise
2392
2394
  return item if readable else None
2393
2395
 
2396
+ @classmethod
2397
+ def _prepare_detail_subscription(
2398
+ cls,
2399
+ manager_class: type[GeneralManager],
2400
+ identification: GraphQLIdentification,
2401
+ user: object,
2402
+ *,
2403
+ property_names: Iterable[str] = (),
2404
+ ) -> tuple[GeneralManager, set[Dependency]] | None:
2405
+ """Hydrate and authorize before priming selected readable properties.
2406
+
2407
+ Call in a worker thread: construction, native permissions and property
2408
+ reads may all access the ORM. Only hydration errors are suppressed;
2409
+ permission-backend and property failures must fail the stream closed.
2410
+ """
2411
+ with DependencyTracker() as dependencies:
2412
+ try:
2413
+ instance, _ = cls._instantiate_manager(manager_class, identification)
2414
+ except SUBSCRIPTION_HYDRATION_ERRORS:
2415
+ return None
2416
+ if not _can_read_instance_for_user_fn(instance, user):
2417
+ return None
2418
+ readable_properties = [
2419
+ name
2420
+ for name in property_names
2421
+ if _check_read_permission_for_user_fn(instance, user, name)
2422
+ ]
2423
+ cls._prime_graphql_properties(instance, readable_properties)
2424
+ return instance, dependencies
2425
+
2394
2426
  @classmethod
2395
2427
  def _add_subscription_field(
2396
2428
  cls,
@@ -2411,7 +2443,7 @@ class GraphQL:
2411
2443
 
2412
2444
  Notes:
2413
2445
  - The subscribe function requires an available channel layer and subscribes the caller to channel groups derived from the instance identification and its resolved dependencies.
2414
- - The subscribe coroutine yields SubscriptionEvent objects with fields `item` (the current instance or None if it cannot be instantiated) and `action` (a string such as `"snapshot"` or other change actions).
2446
+ - Detail events contain an authorized current instance and an action; unavailable or unreadable targets terminate the detail stream.
2415
2447
  - On termination the subscription cleans up listener tasks and unsubscribes from channel groups.
2416
2448
  """
2417
2449
  manager_field_name = pascal_to_snake(generalManagerClass.__name__)
@@ -2451,27 +2483,30 @@ class GraphQL:
2451
2483
  """
2452
2484
  Stream subscription events for a specific manager instance identified by the provided arguments.
2453
2485
 
2454
- Yields an initial `SubscriptionEvent` with `action` set to `"snapshot"` containing the current manager instance, then yields `SubscriptionEvent`s for each subsequent action. For subsequent events, `item` will be the re-instantiated manager instance or `None` if instantiation fails. The subscriber is registered on the manager's channel groups (including dependent managers' groups) and the channel subscriptions and background listener are cleaned up when the iterator is closed or cancelled.
2455
-
2456
- Parameters:
2457
- identification: Identification fields required to locate the manager instance (maps to the manager's identification signature).
2458
-
2459
- Returns:
2460
- AsyncIterator[SubscriptionEvent]: An asynchronous iterator that first yields a snapshot event and then yields update events; each event's `item` is the manager instance or `None` if it could not be instantiated.
2486
+ Authorize the request user before property priming and registration.
2487
+ Recheck access before each event; unavailable or unreadable targets
2488
+ end the iterator without forwarding the queued action. Closing the
2489
+ iterator removes its listeners and channel memberships.
2461
2490
  """
2462
2491
  identification_copy = deepcopy(identification)
2463
2492
  property_names = cls._subscription_property_names(info, generalManagerClass)
2493
+ subscription_user = info.context.user
2464
2494
  try:
2465
- instance, dependency_records = await asyncio.to_thread(
2466
- cls._instantiate_manager,
2495
+ prepared = await asyncio.to_thread(
2496
+ cls._prepare_detail_subscription,
2467
2497
  generalManagerClass,
2468
2498
  identification_copy,
2469
- collect_dependencies=True,
2499
+ subscription_user,
2470
2500
  property_names=property_names,
2471
2501
  )
2472
2502
  except Exception as exc: # pragma: no cover - bubbled to GraphQL
2473
2503
  raise GraphQLError(str(exc)) from exc
2474
2504
 
2505
+ if prepared is None:
2506
+ message = "Subscription target is unavailable or not readable."
2507
+ raise GraphQLError(message)
2508
+ instance, dependency_records = prepared
2509
+
2475
2510
  try:
2476
2511
  channel_layer = cast(
2477
2512
  BaseChannelLayer, cls._get_channel_layer(strict=True)
@@ -2498,15 +2533,16 @@ class GraphQL:
2498
2533
  group_names.add(cls._refresh_group_name(dependency_class))
2499
2534
 
2500
2535
  async def event_stream() -> AsyncIterator[SubscriptionEvent]:
2501
- """
2502
- Yield subscription events for a manager instance, starting with an initial snapshot followed by subsequent updates.
2503
-
2504
- Returns:
2505
- AsyncIterator[SubscriptionEvent]: An asynchronous iterator that first yields a `SubscriptionEvent` with `action` set to `"snapshot"` and `item` containing the current manager instance (or `None` if instantiation failed). Subsequent yields provide `SubscriptionEvent` values for each received action, where `action` is the action string and `item` is the (re-)instantiated manager or `None` if instantiation failed.
2506
-
2507
- Notes:
2508
- When the iterator is closed or exits, the background listener task is cancelled and the subscription's channel group memberships are discarded.
2509
- """
2536
+ """Deliver authorized events and clean up on every exit path."""
2537
+ # The iterator may be consumed long after subscribe() returns.
2538
+ prepared = await asyncio.to_thread(
2539
+ cls._prepare_detail_subscription,
2540
+ generalManagerClass,
2541
+ identification_copy,
2542
+ subscription_user,
2543
+ )
2544
+ if prepared is None:
2545
+ return
2510
2546
  channel_name = cast(str, await channel_layer.new_channel())
2511
2547
  queue: asyncio.Queue[str] = asyncio.Queue[str]()
2512
2548
  joined_groups = await _join_subscription_groups(
@@ -2523,19 +2559,29 @@ class GraphQL:
2523
2559
  joined_groups,
2524
2560
  listener_task,
2525
2561
  ):
2562
+ # Group registration awaits I/O; access can change meanwhile.
2563
+ prepared = await asyncio.to_thread(
2564
+ cls._prepare_detail_subscription,
2565
+ generalManagerClass,
2566
+ identification_copy,
2567
+ subscription_user,
2568
+ )
2569
+ if prepared is None:
2570
+ return
2571
+ item, _ = prepared
2526
2572
  clear_capability_context(info)
2527
- yield SubscriptionEvent(item=instance, action="snapshot")
2573
+ yield SubscriptionEvent(item=item, action="snapshot")
2528
2574
  while True:
2529
2575
  action = await queue.get()
2530
- try:
2531
- item, _ = await asyncio.to_thread(
2532
- cls._instantiate_manager,
2533
- generalManagerClass,
2534
- identification_copy,
2535
- property_names=property_names,
2536
- )
2537
- except SUBSCRIPTION_HYDRATION_ERRORS:
2538
- item = None
2576
+ prepared = await asyncio.to_thread(
2577
+ cls._prepare_detail_subscription,
2578
+ generalManagerClass,
2579
+ identification_copy,
2580
+ subscription_user,
2581
+ )
2582
+ if prepared is None:
2583
+ return
2584
+ item, _ = prepared
2539
2585
  clear_capability_context(info)
2540
2586
  yield SubscriptionEvent(item=item, action=action)
2541
2587
 
@@ -162,3 +162,44 @@ def may_reuse_data_change_transaction(database_alias: str) -> bool:
162
162
  and not owner.caller_in_atomic_block
163
163
  and is_data_change_operation_authorized(database_alias)
164
164
  )
165
+
166
+
167
+ @dataclass
168
+ class DataChangeOutcome:
169
+ """Private per-call outcome; unknown/custom mutations remain changes."""
170
+
171
+ interface: object
172
+ unchanged: bool = False
173
+
174
+
175
+ _operation_outcome: ContextVar[DataChangeOutcome | None] = ContextVar(
176
+ "data_change_operation_outcome", default=None
177
+ )
178
+
179
+
180
+ @contextmanager
181
+ def suspend_data_change_outcome() -> Iterator[None]:
182
+ """Keep nested signal/lifecycle receivers outside their parent's outcome."""
183
+ token = _operation_outcome.set(None)
184
+ try:
185
+ yield
186
+ finally:
187
+ _operation_outcome.reset(token)
188
+
189
+
190
+ @contextmanager
191
+ def capture_data_change_outcome(interface: object) -> Iterator[DataChangeOutcome]:
192
+ """Isolate the wrapped operation from pre-signals and nested mutations."""
193
+ outcome = DataChangeOutcome(interface)
194
+ token = _operation_outcome.set(outcome)
195
+ try:
196
+ yield outcome
197
+ finally:
198
+ _operation_outcome.reset(token)
199
+
200
+
201
+ def mark_data_change_unchanged(interface: object) -> None:
202
+ """Accept a proven no-op only for the interface owned by this call."""
203
+ outcome = _operation_outcome.get()
204
+ if outcome is not None and outcome.interface is interface:
205
+ outcome.unchanged = True
@@ -15,8 +15,10 @@ from django.utils.connection import ConnectionDoesNotExist
15
15
  from general_manager.cache.data_change_context import (
16
16
  DataChangeTransactionScope,
17
17
  authorize_data_change_operation,
18
+ capture_data_change_outcome,
18
19
  own_data_change_transaction,
19
20
  record_data_change_phase,
21
+ suspend_data_change_outcome,
20
22
  )
21
23
  from general_manager.as_of import reject_historical_mutation
22
24
  from general_manager.logging import get_logger
@@ -66,7 +68,9 @@ def data_change(
66
68
  clears run-scoped ORM bucket/index caches, emits `pre_data_change`, invokes
67
69
  the wrapped callable, then emits `post_data_change` with the changed
68
70
  instance, previous instance, action name, identification, and copied
69
- `_old_values` payload. Both signals receive a per-call `change_context` and
71
+ `_old_values` payload. Proven unchanged built-in ORM updates skip only the
72
+ post-change signal; pre-change preparation and transaction cleanup still run.
73
+ Both signals receive a per-call `change_context` and
70
74
  the manager interface's database alias. ORM-backed signal dispatch and the
71
75
  mutation share one transaction. GraphQL warm-up requeue keys collected
72
76
  during signal handling are drained only after the outermost active
@@ -101,6 +105,7 @@ def data_change(
101
105
  )
102
106
 
103
107
  @wraps(decorator_source)
108
+ @suspend_data_change_outcome()
104
109
  def perform_change(*args: P.args, **kwargs: P.kwargs) -> R:
105
110
  """
106
111
  Emit pre_data_change and post_data_change signals around the wrapped function call.
@@ -252,7 +257,12 @@ def data_change(
252
257
  if is_orm_backed
253
258
  else nullcontext()
254
259
  )
255
- with operation_context:
260
+ with (
261
+ operation_context,
262
+ capture_data_change_outcome(
263
+ getattr(instance_before, "_interface", None)
264
+ ) as outcome,
265
+ ):
256
266
  mutation_started = (
257
267
  perf_counter() if transaction_scope is not None else None
258
268
  )
@@ -282,15 +292,16 @@ def data_change(
282
292
  if identification is None:
283
293
  identification = pre_identification
284
294
 
285
- post_data_change.send(
286
- sender=sender,
287
- instance=instance,
288
- previous_instance=instance_before,
289
- identification=identification,
290
- action=action,
291
- old_relevant_values=old_relevant_values,
292
- **signal_kwargs,
293
- )
295
+ if not outcome.unchanged:
296
+ post_data_change.send(
297
+ sender=sender,
298
+ instance=instance,
299
+ previous_instance=instance_before,
300
+ identification=identification,
301
+ action=action,
302
+ old_relevant_values=old_relevant_values,
303
+ **signal_kwargs,
304
+ )
294
305
  if instance_before is not None:
295
306
  try:
296
307
  delattr(instance_before, "_old_values")
@@ -11,7 +11,10 @@ from django.contrib.auth import get_user_model
11
11
  from django.db import DEFAULT_DB_ALIAS, models, router, transaction
12
12
  from django.db.models import NOT_PROVIDED
13
13
 
14
- from general_manager.cache.data_change_context import owns_data_change_transaction
14
+ from general_manager.cache.data_change_context import (
15
+ mark_data_change_unchanged,
16
+ owns_data_change_transaction,
17
+ )
15
18
  from general_manager.interface.capabilities.base import CapabilityName
16
19
  from general_manager.interface.capabilities.builtin import BaseCapability
17
20
  from general_manager.interface.capabilities.orm_utils.payload_normalizer import (
@@ -20,6 +23,7 @@ from general_manager.interface.capabilities.orm_utils.payload_normalizer import
20
23
  from general_manager.interface.capabilities.orm_utils.update_state import (
21
24
  changed_many_to_many,
22
25
  is_unchanged_update,
26
+ record_skipped_update_save,
23
27
  track_update,
24
28
  )
25
29
  from general_manager.interface.utils.database_interface_protocols import (
@@ -225,6 +229,7 @@ class OrmMutationCapability(BaseCapability):
225
229
  object.__setattr__(instance, "changed_by_id", creator_id)
226
230
  instance.full_clean()
227
231
  if is_unchanged_update(instance):
232
+ record_skipped_update_save(instance)
228
233
  return instance.pk
229
234
  if batch is not None and database_alias is None:
230
235
  if (
@@ -606,7 +611,7 @@ class OrmUpdateCapability(BaseCapability):
606
611
  changed_many,
607
612
  history_comment,
608
613
  database_alias=database_alias,
609
- ):
614
+ ) as update_state:
610
615
  instance = mutation.assign_simple_attributes(
611
616
  interface_instance.__class__, instance, normalized_simple
612
617
  )
@@ -624,6 +629,32 @@ class OrmUpdateCapability(BaseCapability):
624
629
  history_comment=history_comment,
625
630
  )
626
631
  discard_orm_instance_cache(interface_instance.__class__, pk)
632
+ if update_state is not None and update_state.save_skipped:
633
+ # A custom wrapper may write after delegating to a built-in
634
+ # method. Only the complete standard path can prove a no-op.
635
+ from general_manager.interface.base_interface import InterfaceBase
636
+
637
+ standard_methods = (
638
+ (interface_instance.update, InterfaceBase.update),
639
+ (self.update, OrmUpdateCapability.update),
640
+ (
641
+ mutation.assign_simple_attributes,
642
+ OrmMutationCapability.assign_simple_attributes,
643
+ ),
644
+ (
645
+ mutation.save_with_history,
646
+ OrmMutationCapability.save_with_history,
647
+ ),
648
+ (
649
+ mutation.apply_many_to_many,
650
+ OrmMutationCapability.apply_many_to_many,
651
+ ),
652
+ )
653
+ if all(
654
+ getattr(method, "__func__", None) is standard
655
+ for method, standard in standard_methods
656
+ ):
657
+ mark_data_change_unchanged(interface_instance)
627
658
  return {"id": pk}
628
659
 
629
660
  return call_with_observability(
@@ -16,10 +16,11 @@ from django.db.models.fields.files import FieldFile
16
16
  from general_manager.measurement.measurement_field import MeasurementField
17
17
 
18
18
 
19
- @dataclass(frozen=True)
19
+ @dataclass
20
20
  class _UpdateState:
21
21
  instance: models.Model
22
22
  values: dict[str, object]
23
+ save_skipped: bool = False
23
24
 
24
25
 
25
26
  _update_state: ContextVar[_UpdateState | None] = ContextVar(
@@ -47,7 +48,7 @@ def track_update(
47
48
  history_comment: str | None,
48
49
  *,
49
50
  database_alias: str | None = None,
50
- ) -> Iterator[None]:
51
+ ) -> Iterator[_UpdateState | None]:
51
52
  """Scope the original row to its save, including nested mutation calls.
52
53
 
53
54
  The save compares after full_clean(), so validation and changes made by
@@ -85,7 +86,7 @@ def track_update(
85
86
  state = _UpdateState(instance, values)
86
87
  token = _update_state.set(state)
87
88
  try:
88
- yield
89
+ yield state
89
90
  finally:
90
91
  _update_state.reset(token)
91
92
 
@@ -107,6 +108,13 @@ def is_unchanged_update(instance: models.Model) -> bool:
107
108
  return True
108
109
 
109
110
 
111
+ def record_skipped_update_save(instance: models.Model) -> None:
112
+ """Record the built-in save decision without classifying custom wrappers."""
113
+ state = _update_state.get()
114
+ if state is not None and state.instance is instance:
115
+ state.save_skipped = True
116
+
117
+
110
118
  def changed_many_to_many(
111
119
  instance: models.Model,
112
120
  values: dict[str, list[object]],
@@ -29,6 +29,8 @@ _WORKFLOW_SIGNAL_BRIDGE_KEY = "WORKFLOW_SIGNAL_BRIDGE"
29
29
  _DISPATCH_UID = "general_manager_workflow_signal_bridge"
30
30
 
31
31
  _RESERVED_KEYS = {
32
+ "change_context",
33
+ "database_alias",
32
34
  "creator_id",
33
35
  "history_comment",
34
36
  "identification",
@@ -88,8 +90,8 @@ def _manager_change_to_event(
88
90
  for key, value in kwargs.items()
89
91
  if key not in _RESERVED_KEYS and not key.startswith("_")
90
92
  }
91
- if action in {"create", "update"} and not relevant_fields:
92
- return None
93
+ # The mutation outcome controls no-op suppression. Empty input can still
94
+ # create defaults, change values in clean(), or request an audit touch.
93
95
 
94
96
  identification = kwargs.get("identification")
95
97
  if identification is None:
File without changes