GeneralManager 0.64.4__tar.gz → 0.65.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. {generalmanager-0.64.4/src/GeneralManager.egg-info → generalmanager-0.65.0}/PKG-INFO +1 -1
  2. {generalmanager-0.64.4 → generalmanager-0.65.0}/pyproject.toml +1 -1
  3. {generalmanager-0.64.4 → generalmanager-0.65.0/src/GeneralManager.egg-info}/PKG-INFO +1 -1
  4. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/GeneralManager.egg-info/SOURCES.txt +5 -0
  5. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/general_manager.py +4 -0
  6. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/search.py +4 -0
  7. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/apps.py +9 -2
  8. generalmanager-0.65.0/src/general_manager/cache/data_change_context.py +93 -0
  9. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/cache/signals.py +110 -52
  10. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm/mutations.py +27 -30
  11. generalmanager-0.65.0/src/general_manager/migrations/0011_search_index_state_dirty_generation.py +20 -0
  12. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/public_api_registry.py +10 -0
  13. generalmanager-0.65.0/src/general_manager/search/async_tasks.py +507 -0
  14. generalmanager-0.65.0/src/general_manager/search/checks.py +259 -0
  15. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/config.py +41 -7
  16. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/indexer.py +164 -119
  17. generalmanager-0.65.0/src/general_manager/search/invalidation.py +1231 -0
  18. generalmanager-0.65.0/src/general_manager/search/m2m_invalidation.py +372 -0
  19. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/models.py +55 -37
  20. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/reconciliation.py +221 -53
  21. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/finalization.py +51 -2
  22. generalmanager-0.64.4/src/general_manager/search/async_tasks.py +0 -217
  23. {generalmanager-0.64.4 → generalmanager-0.65.0}/LICENSE +0 -0
  24. {generalmanager-0.64.4 → generalmanager-0.65.0}/README.md +0 -0
  25. {generalmanager-0.64.4 → generalmanager-0.65.0}/setup.cfg +0 -0
  26. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
  27. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/GeneralManager.egg-info/requires.txt +0 -0
  28. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/GeneralManager.egg-info/top_level.txt +0 -0
  29. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/__init__.py +0 -0
  30. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/__init__.py +0 -0
  31. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/api.py +3 -3
  32. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/bucket.py +0 -0
  33. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/cache.py +2 -2
  34. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/dataframes.py +2 -2
  35. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/factory.py +0 -0
  36. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/interface.py +0 -0
  37. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/manager.py +2 -2
  38. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/measurement.py +0 -0
  39. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/permission.py +2 -2
  40. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/rule.py +0 -0
  41. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/_types/utils.py +2 -2
  42. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/__init__.py +0 -0
  43. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql.py +0 -0
  44. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql_errors.py +0 -0
  45. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql_mutations.py +0 -0
  46. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql_prefetch.py +0 -0
  47. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql_relations.py +0 -0
  48. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql_resolvers.py +0 -0
  49. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql_search.py +0 -0
  50. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
  51. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql_subscriptions.py +0 -0
  52. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql_view.py +0 -0
  53. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql_warmup.py +0 -0
  54. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql_warmup_registry.py +0 -0
  55. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
  56. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/mutation.py +0 -0
  57. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/notification_batching.py +0 -0
  58. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/property.py +0 -0
  59. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/registry.py +0 -0
  60. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/remote_api.py +0 -0
  61. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/remote_invalidation.py +0 -0
  62. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/api/remote_invalidation_client.py +0 -0
  63. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/bootstrap.py +0 -0
  64. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/bucket/__init__.py +0 -0
  65. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/bucket/base_bucket.py +0 -0
  66. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/bucket/calculation_bucket.py +0 -0
  67. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/bucket/database_bucket.py +0 -0
  68. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/bucket/group_bucket.py +0 -0
  69. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/bucket/indexing.py +0 -0
  70. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/bucket/request_bucket.py +0 -0
  71. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/cache/__init__.py +0 -0
  72. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/cache/cache_decorator.py +0 -0
  73. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/cache/cache_tracker.py +0 -0
  74. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/cache/dependency_cache.py +0 -0
  75. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/cache/dependency_index.py +0 -0
  76. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/cache/dependency_matching.py +0 -0
  77. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/cache/dependency_publish.py +0 -0
  78. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/cache/dependency_shards.py +0 -0
  79. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/cache/model_dependency_collector.py +0 -0
  80. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/cache/run_context.py +0 -0
  81. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/__init__.py +0 -0
  82. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/audit.py +0 -0
  83. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/bootstrap.py +0 -0
  84. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/checks.py +0 -0
  85. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/consumer.py +0 -0
  86. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/errors.py +0 -0
  87. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/__init__.py +0 -0
  88. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/__main__.py +0 -0
  89. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/baseline.py +0 -0
  90. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/datasets/basic_queries.yaml +0 -0
  91. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/datasets/demo_readiness.yaml +0 -0
  92. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/datasets/edge_cases.yaml +0 -0
  93. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/datasets/follow_ups.yaml +0 -0
  94. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/datasets/large_schema.yaml +0 -0
  95. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/datasets/multi_hop.yaml +0 -0
  96. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/diagnostics.py +0 -0
  97. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/fingerprints.py +0 -0
  98. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/fixtures.py +0 -0
  99. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/judges/__init__.py +0 -0
  100. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
  101. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/judges/contract.py +0 -0
  102. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
  103. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
  104. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/readiness.py +0 -0
  105. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/runner.py +0 -0
  106. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/evals/traces.py +0 -0
  107. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/grounding.py +0 -0
  108. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/models.py +0 -0
  109. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/providers/__init__.py +0 -0
  110. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/providers/_shared.py +0 -0
  111. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/providers/anthropic.py +0 -0
  112. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/providers/base.py +0 -0
  113. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/providers/google.py +0 -0
  114. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/providers/ollama.py +0 -0
  115. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/providers/openai.py +0 -0
  116. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/rate_limits.py +0 -0
  117. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/routing.py +0 -0
  118. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/schema_index.py +0 -0
  119. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/settings.py +0 -0
  120. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/signals.py +0 -0
  121. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/system_prompt.py +0 -0
  122. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/tool_metadata.py +0 -0
  123. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/tools.py +0 -0
  124. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/chat/views.py +0 -0
  125. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/conf.py +0 -0
  126. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/dataframes/__init__.py +0 -0
  127. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/dataframes/measurements.py +0 -0
  128. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/factory/__init__.py +0 -0
  129. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/factory/auto_factory.py +0 -0
  130. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/factory/factories.py +0 -0
  131. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/factory/factory_methods.py +0 -0
  132. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/__init__.py +0 -0
  133. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/base_interface.py +0 -0
  134. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/bundles/__init__.py +0 -0
  135. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/bundles/calculation.py +0 -0
  136. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/bundles/database.py +0 -0
  137. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/bundles/remote_manager.py +0 -0
  138. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/bundles/request.py +0 -0
  139. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/__init__.py +0 -0
  140. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/base.py +0 -0
  141. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/builtin.py +0 -0
  142. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
  143. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
  144. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
  145. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/configuration.py +0 -0
  146. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/core/observability.py +0 -0
  147. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/core/utils.py +0 -0
  148. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/exceptions.py +0 -0
  149. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
  150. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
  151. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
  152. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/factory.py +0 -0
  153. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
  154. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
  155. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm/history.py +0 -0
  156. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
  157. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm/support.py +0 -0
  158. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
  159. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
  160. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
  161. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
  162. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
  163. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
  164. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
  165. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
  166. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
  167. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/registry.py +0 -0
  168. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
  169. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
  170. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/infrastructure/__init__.py +0 -0
  171. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
  172. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
  173. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/__init__.py +0 -0
  174. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/calculation.py +0 -0
  175. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/database.py +0 -0
  176. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/existing_model.py +0 -0
  177. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/read_only.py +0 -0
  178. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
  179. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/interfaces/request.py +0 -0
  180. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/manifests/__init__.py +0 -0
  181. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/manifests/capability_builder.py +0 -0
  182. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
  183. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/manifests/capability_models.py +0 -0
  184. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/orm_interface.py +0 -0
  185. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/requests.py +0 -0
  186. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/utils/__init__.py +0 -0
  187. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
  188. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/utils/errors.py +0 -0
  189. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/utils/history.py +0 -0
  190. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/interface/utils/models.py +0 -0
  191. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/logging.py +0 -0
  192. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/management/commands/chat_cleanup.py +0 -0
  193. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/management/commands/cleanup_upload_intents.py +0 -0
  194. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/management/commands/graphql_warmup.py +0 -0
  195. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
  196. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/management/commands/search_index.py +0 -0
  197. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/management/commands/search_reconcile.py +0 -0
  198. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
  199. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/management/commands/shell.py +0 -0
  200. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
  201. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
  202. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/manager/__init__.py +0 -0
  203. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/manager/general_manager.py +0 -0
  204. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/manager/group_manager.py +0 -0
  205. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/manager/input.py +0 -0
  206. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/manager/meta.py +0 -0
  207. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/measurement/__init__.py +0 -0
  208. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/measurement/measurement.py +0 -0
  209. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/measurement/measurement_field.py +0 -0
  210. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/metrics/__init__.py +0 -0
  211. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/metrics/graphql.py +0 -0
  212. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/migrations/0001_initial.py +0 -0
  213. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
  214. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
  215. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
  216. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/migrations/0004_search_index_state.py +0 -0
  217. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
  218. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
  219. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/migrations/0007_upload_intent.py +0 -0
  220. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/migrations/0008_upload_finalization_versions.py +0 -0
  221. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/migrations/0009_upload_cleanup_state.py +0 -0
  222. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/migrations/0010_chat_pending_confirmation_portable_uniqueness.py +0 -0
  223. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/migrations/__init__.py +0 -0
  224. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/models.py +0 -0
  225. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/permission/__init__.py +0 -0
  226. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/permission/audit.py +0 -0
  227. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/permission/base_permission.py +0 -0
  228. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/permission/graphql_capabilities.py +0 -0
  229. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/permission/manager_based_permission.py +0 -0
  230. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/permission/mutation_permission.py +0 -0
  231. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/permission/permission_checks.py +0 -0
  232. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/permission/permission_data_manager.py +0 -0
  233. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/permission/utils.py +0 -0
  234. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/py.typed +0 -0
  235. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/rule/__init__.py +0 -0
  236. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/rule/handler.py +0 -0
  237. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/rule/rule.py +0 -0
  238. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/__init__.py +0 -0
  239. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/backend.py +0 -0
  240. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/backend_registry.py +0 -0
  241. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/backends/__init__.py +0 -0
  242. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/backends/dev.py +0 -0
  243. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/backends/meilisearch.py +0 -0
  244. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/backends/opensearch.py +0 -0
  245. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/backends/typesense.py +0 -0
  246. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/registry.py +0 -0
  247. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/tasks.py +0 -0
  248. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/search/utils.py +0 -0
  249. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/seeding/__init__.py +0 -0
  250. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/seeding/manager_landscape.py +0 -0
  251. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/__init__.py +0 -0
  252. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/adapters.py +0 -0
  253. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/checks.py +0 -0
  254. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/config.py +0 -0
  255. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/errors.py +0 -0
  256. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/graphql.py +0 -0
  257. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/graphql_types.py +0 -0
  258. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/metrics.py +0 -0
  259. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/models.py +0 -0
  260. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/public.py +0 -0
  261. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/s3.py +0 -0
  262. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/services.py +0 -0
  263. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/tokens.py +0 -0
  264. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/types.py +0 -0
  265. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/urls.py +0 -0
  266. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/uploads/views.py +0 -0
  267. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/utils/__init__.py +0 -0
  268. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/utils/args_to_kwargs.py +0 -0
  269. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/utils/filter_parser.py +0 -0
  270. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/utils/format_string.py +0 -0
  271. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/utils/json_encoder.py +0 -0
  272. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/utils/make_cache_key.py +0 -0
  273. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/utils/none_to_zero.py +0 -0
  274. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/utils/path_mapping.py +0 -0
  275. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/utils/public_api.py +0 -0
  276. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/utils/testing.py +0 -0
  277. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/utils/type_checks.py +0 -0
  278. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/__init__.py +0 -0
  279. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/actions.py +0 -0
  280. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/backend_registry.py +0 -0
  281. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/backends/__init__.py +0 -0
  282. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/backends/celery.py +0 -0
  283. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/backends/local.py +0 -0
  284. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/backends/n8n.py +0 -0
  285. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/config.py +0 -0
  286. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/engine.py +0 -0
  287. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/event_registry.py +0 -0
  288. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/events.py +0 -0
  289. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/models.py +0 -0
  290. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/signal_bridge.py +0 -0
  291. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/tasks.py +0 -0
  292. {generalmanager-0.64.4 → generalmanager-0.65.0}/src/general_manager/workflow/telemetry.py +0 -0
  293. {generalmanager-0.64.4 → generalmanager-0.65.0}/tests/test_settings.py +0 -0
  294. {generalmanager-0.64.4 → generalmanager-0.65.0}/tests/test_urls.py +0 -0
  295. {generalmanager-0.64.4 → generalmanager-0.65.0}/tests/testing_asgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.64.4
3
+ Version: 0.65.0
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "GeneralManager"
7
- version = "0.64.4"
7
+ version = "0.65.0"
8
8
  description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.64.4
3
+ Version: 0.65.0
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -59,6 +59,7 @@ src/general_manager/bucket/request_bucket.py
59
59
  src/general_manager/cache/__init__.py
60
60
  src/general_manager/cache/cache_decorator.py
61
61
  src/general_manager/cache/cache_tracker.py
62
+ src/general_manager/cache/data_change_context.py
62
63
  src/general_manager/cache/dependency_cache.py
63
64
  src/general_manager/cache/dependency_index.py
64
65
  src/general_manager/cache/dependency_matching.py
@@ -208,6 +209,7 @@ src/general_manager/migrations/0007_upload_intent.py
208
209
  src/general_manager/migrations/0008_upload_finalization_versions.py
209
210
  src/general_manager/migrations/0009_upload_cleanup_state.py
210
211
  src/general_manager/migrations/0010_chat_pending_confirmation_portable_uniqueness.py
212
+ src/general_manager/migrations/0011_search_index_state_dirty_generation.py
211
213
  src/general_manager/migrations/__init__.py
212
214
  src/general_manager/permission/__init__.py
213
215
  src/general_manager/permission/audit.py
@@ -225,8 +227,11 @@ src/general_manager/search/__init__.py
225
227
  src/general_manager/search/async_tasks.py
226
228
  src/general_manager/search/backend.py
227
229
  src/general_manager/search/backend_registry.py
230
+ src/general_manager/search/checks.py
228
231
  src/general_manager/search/config.py
229
232
  src/general_manager/search/indexer.py
233
+ src/general_manager/search/invalidation.py
234
+ src/general_manager/search/m2m_invalidation.py
230
235
  src/general_manager/search/models.py
231
236
  src/general_manager/search/reconciliation.py
232
237
  src/general_manager/search/registry.py
@@ -19,9 +19,11 @@ __all__ = [
19
19
  "RequestInterface",
20
20
  "Rule",
21
21
  "SearchBackend",
22
+ "SearchChange",
22
23
  "SearchConfigProtocol",
23
24
  "SearchConfigSpec",
24
25
  "SearchIndexer",
26
+ "SearchInvalidationRule",
25
27
  "configure_search_backend",
26
28
  "configure_search_backend_from_settings",
27
29
  "get_logger",
@@ -54,9 +56,11 @@ from general_manager.interface.interfaces.remote_manager import RemoteManagerInt
54
56
  from general_manager.interface.interfaces.request import RequestInterface
55
57
  from general_manager.rule.rule import Rule
56
58
  from general_manager.search.backend import SearchBackend
59
+ from general_manager.search.config import SearchChange
57
60
  from general_manager.search.config import SearchConfigProtocol
58
61
  from general_manager.search.config import SearchConfigSpec
59
62
  from general_manager.search.indexer import SearchIndexer
63
+ from general_manager.search.config import SearchInvalidationRule
60
64
  from general_manager.search.backend_registry import configure_search_backend
61
65
  from general_manager.search.backend_registry import (
62
66
  configure_search_backend_from_settings,
@@ -13,11 +13,13 @@ __all__ = [
13
13
  "SearchBackendError",
14
14
  "SearchBackendNotConfiguredError",
15
15
  "SearchBackendNotImplementedError",
16
+ "SearchChange",
16
17
  "SearchConfigProtocol",
17
18
  "SearchConfigSpec",
18
19
  "SearchDocument",
19
20
  "SearchHit",
20
21
  "SearchIndexer",
22
+ "SearchInvalidationRule",
21
23
  "SearchResult",
22
24
  "TypesenseBackend",
23
25
  "configure_search_backend",
@@ -37,11 +39,13 @@ from general_manager.search.backend import SearchBackendClientMissingError
37
39
  from general_manager.search.backend import SearchBackendError
38
40
  from general_manager.search.backend import SearchBackendNotConfiguredError
39
41
  from general_manager.search.backend import SearchBackendNotImplementedError
42
+ from general_manager.search.config import SearchChange
40
43
  from general_manager.search.config import SearchConfigProtocol
41
44
  from general_manager.search.config import SearchConfigSpec
42
45
  from general_manager.search.backend import SearchDocument
43
46
  from general_manager.search.backend import SearchHit
44
47
  from general_manager.search.indexer import SearchIndexer
48
+ from general_manager.search.config import SearchInvalidationRule
45
49
  from general_manager.search.backend import SearchResult
46
50
  from general_manager.search.backends.typesense import TypesenseBackend
47
51
  from general_manager.search.backend_registry import configure_search_backend
@@ -168,8 +168,10 @@ class GeneralmanagerConfig(AppConfig):
168
168
  self.install_startup_hook_runner()
169
169
  self.register_system_checks()
170
170
  from general_manager.uploads.checks import register_upload_checks
171
+ from general_manager.search.checks import register_search_checks
171
172
 
172
173
  register_upload_checks()
174
+ register_search_checks()
173
175
  _autoload_app_managers_modules()
174
176
  self.initialize_general_manager_classes(
175
177
  GeneralManagerMeta.pending_attribute_initialization,
@@ -178,14 +180,19 @@ class GeneralmanagerConfig(AppConfig):
178
180
  handle_remote_api(GeneralManagerMeta.all_classes)
179
181
  configure_audit_logger_from_settings(settings)
180
182
  configure_search_backend_from_settings(settings)
183
+ from general_manager.search.invalidation import configure_search_invalidation
184
+ from general_manager.search.m2m_invalidation import (
185
+ configure_search_m2m_invalidation,
186
+ )
187
+
188
+ configure_search_invalidation()
189
+ configure_search_m2m_invalidation()
181
190
  configure_workflow_engine_from_settings(settings)
182
191
  configure_event_registry_from_settings(settings)
183
192
  configure_workflow_signal_bridge_from_settings(settings)
184
193
  configure_workflow_beat_schedule_from_settings(settings)
185
194
  configure_search_reconcile_beat_schedule_from_settings(settings)
186
195
  configure_graphql_warmup_beat_schedule_from_settings(settings)
187
- import general_manager.search.indexer as _search_indexer # noqa: F401
188
-
189
196
  from general_manager.conf import get_setting
190
197
 
191
198
  if get_setting("AUTOCREATE_GRAPHQL", False):
@@ -0,0 +1,93 @@
1
+ """Context-local ownership metadata for ORM data-change transactions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator
6
+ from contextlib import contextmanager
7
+ from contextvars import ContextVar
8
+ from dataclasses import dataclass
9
+
10
+
11
+ @dataclass
12
+ class _DataChangeTransaction:
13
+ """Live marker for one framework-owned transaction envelope."""
14
+
15
+ database_alias: str
16
+ caller_in_atomic_block: bool
17
+ active: bool = True
18
+
19
+
20
+ _owned_transactions: ContextVar[tuple[_DataChangeTransaction, ...]] = ContextVar(
21
+ "owned_data_change_transactions",
22
+ default=(),
23
+ )
24
+ _authorized_operations: ContextVar[tuple[_DataChangeTransaction, ...]] = ContextVar(
25
+ "authorized_data_change_operations",
26
+ default=(),
27
+ )
28
+
29
+
30
+ def _current_owner(database_alias: str) -> _DataChangeTransaction | None:
31
+ for owner in reversed(_owned_transactions.get()):
32
+ if owner.active and owner.database_alias == database_alias:
33
+ return owner
34
+ return None
35
+
36
+
37
+ @contextmanager
38
+ def own_data_change_transaction(
39
+ database_alias: str,
40
+ *,
41
+ caller_in_atomic_block: bool,
42
+ ) -> Iterator[None]:
43
+ """Record a live framework envelope without inspecting Django internals."""
44
+ owner = _DataChangeTransaction(
45
+ database_alias=database_alias,
46
+ caller_in_atomic_block=caller_in_atomic_block,
47
+ )
48
+ token = _owned_transactions.set((*_owned_transactions.get(), owner))
49
+ try:
50
+ yield
51
+ finally:
52
+ owner.active = False
53
+ _owned_transactions.reset(token)
54
+
55
+
56
+ @contextmanager
57
+ def authorize_data_change_operation(database_alias: str) -> Iterator[None]:
58
+ """Authorize only the decorated manager method to reuse its envelope."""
59
+ owner = _current_owner(database_alias)
60
+ if owner is None:
61
+ yield
62
+ return
63
+ token = _authorized_operations.set((*_authorized_operations.get(), owner))
64
+ try:
65
+ yield
66
+ finally:
67
+ _authorized_operations.reset(token)
68
+
69
+
70
+ def owns_data_change_transaction(database_alias: str) -> bool:
71
+ """Return whether this context has a live framework envelope for an alias."""
72
+ return _current_owner(database_alias) is not None
73
+
74
+
75
+ def is_data_change_operation_authorized(database_alias: str) -> bool:
76
+ """Return whether the current owner is running its decorated method body."""
77
+ owner = _current_owner(database_alias)
78
+ if owner is None:
79
+ return False
80
+ return any(
81
+ authorized is owner and authorized.active
82
+ for authorized in _authorized_operations.get()
83
+ )
84
+
85
+
86
+ def may_reuse_data_change_transaction(database_alias: str) -> bool:
87
+ """Conservatively authorize reuse when the manager opened the outer block."""
88
+ owner = _current_owner(database_alias)
89
+ return bool(
90
+ owner is not None
91
+ and not owner.caller_in_atomic_block
92
+ and is_data_change_operation_authorized(database_alias)
93
+ )
@@ -2,12 +2,19 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from contextlib import nullcontext
5
6
  from copy import deepcopy
6
- from django.dispatch import Signal
7
- from typing import Callable, TypeVar, ParamSpec, cast, overload
8
-
9
7
  from functools import wraps
8
+ from typing import Callable, ParamSpec, TypeVar, cast, overload
9
+
10
+ from django.db import DEFAULT_DB_ALIAS, connections, transaction
11
+ from django.dispatch import Signal
12
+ from django.utils.connection import ConnectionDoesNotExist
10
13
 
14
+ from general_manager.cache.data_change_context import (
15
+ authorize_data_change_operation,
16
+ own_data_change_transaction,
17
+ )
11
18
  from general_manager.logging import get_logger
12
19
 
13
20
  post_data_change = Signal()
@@ -20,6 +27,16 @@ R = TypeVar("R")
20
27
  logger = get_logger("cache.signals")
21
28
 
22
29
 
30
+ def _caller_in_atomic_block(database_alias: str) -> bool:
31
+ """Read public transaction state when the configured alias is available."""
32
+ try:
33
+ return bool(connections[database_alias].in_atomic_block)
34
+ except ConnectionDoesNotExist:
35
+ # Preserve lightweight callers that replace ``transaction.atomic`` in
36
+ # isolation. A real atomic call will still reject an unknown alias.
37
+ return False
38
+
39
+
23
40
  @overload
24
41
  def data_change(func: Callable[P, R]) -> Callable[P, R]: ...
25
42
 
@@ -39,9 +56,12 @@ def data_change(
39
56
  clears run-scoped ORM bucket/index caches, emits `pre_data_change`, invokes
40
57
  the wrapped callable, then emits `post_data_change` with the changed
41
58
  instance, previous instance, action name, identification, and copied
42
- `_old_values` payload. GraphQL warm-up requeue keys collected during signal
43
- handling are drained only after the outermost active data-change barrier has
44
- closed; failed mutations drain pending keys but do not enqueue rewarm work.
59
+ `_old_values` payload. Both signals receive a per-call `change_context` and
60
+ the manager interface's database alias. ORM-backed signal dispatch and the
61
+ mutation share one transaction. GraphQL warm-up requeue keys collected
62
+ during signal handling are drained only after the outermost active
63
+ data-change barrier has closed; failed mutations drain pending keys but do
64
+ not enqueue rewarm work.
45
65
 
46
66
  Parameters:
47
67
  func: Function that performs a data mutation. Methods named `create`
@@ -85,6 +105,29 @@ def data_change(
85
105
  is_dependency_data_change_active,
86
106
  )
87
107
  from general_manager.cache.run_context import current_calculation_run_context
108
+ from general_manager.interface.orm_interface import OrmInterfaceBase
109
+
110
+ action = decorator_source.__name__
111
+ if action == "create":
112
+ sender = args[0]
113
+ instance_before = None
114
+ else:
115
+ instance_before = args[0]
116
+ sender = instance_before.__class__
117
+
118
+ interface = getattr(sender, "Interface", None)
119
+ is_orm_backed = isinstance(interface, type) and issubclass(
120
+ interface, OrmInterfaceBase
121
+ )
122
+ database_alias = DEFAULT_DB_ALIAS
123
+ if is_orm_backed:
124
+ database_alias = getattr(interface, "database", None) or DEFAULT_DB_ALIAS
125
+ change_context: dict[str, object] = {}
126
+ signal_kwargs = {
127
+ **kwargs,
128
+ "change_context": change_context,
129
+ "database_alias": database_alias,
130
+ }
88
131
 
89
132
  primary_exc: BaseException | None = None
90
133
  completed = False
@@ -95,55 +138,70 @@ def data_change(
95
138
  context.clear_bucket_indexes()
96
139
  context.clear_trusted_orm_managers()
97
140
  try:
98
- action = func.__name__
99
- if func.__name__ == "create":
100
- sender = args[0]
101
- instance_before = None
102
- else:
103
- instance = args[0]
104
- sender = instance.__class__
105
- instance_before = instance
106
- pre_data_change.send(
107
- sender=sender,
108
- instance=instance_before,
109
- action=action,
110
- **kwargs,
141
+ transaction_context = (
142
+ transaction.atomic(using=database_alias)
143
+ if is_orm_backed
144
+ else nullcontext()
111
145
  )
112
- old_relevant_values = getattr(instance_before, "_old_values", {})
113
- pre_identification = deepcopy(
114
- getattr(instance_before, "identification", None)
146
+ caller_in_atomic_block = (
147
+ _caller_in_atomic_block(database_alias) if is_orm_backed else False
115
148
  )
116
- if isinstance(func, classmethod):
117
- inner = cast(Callable[P, R], func.__func__)
118
- result = inner(*args, **kwargs)
119
- else:
120
- result = func(*args, **kwargs)
121
-
122
- context = current_calculation_run_context()
123
- if context is not None:
124
- context.clear_orm_bucket_results()
125
- context.clear_bucket_indexes()
126
- context.clear_trusted_orm_managers()
127
-
128
- instance = result
129
- identification = getattr(instance, "identification", None)
130
- if identification is None:
131
- identification = pre_identification
132
-
133
- post_data_change.send(
134
- sender=sender,
135
- instance=instance,
136
- previous_instance=instance_before,
137
- identification=identification,
138
- action=action,
139
- old_relevant_values=old_relevant_values,
140
- **kwargs,
149
+ ownership_context = (
150
+ own_data_change_transaction(
151
+ database_alias,
152
+ caller_in_atomic_block=caller_in_atomic_block,
153
+ )
154
+ if is_orm_backed
155
+ else nullcontext()
141
156
  )
142
- if instance_before is not None:
143
- try:
144
- delattr(instance_before, "_old_values")
145
- except AttributeError:
146
- pass
157
+ with transaction_context, ownership_context:
158
+ pre_data_change.send(
159
+ sender=sender,
160
+ instance=instance_before,
161
+ action=action,
162
+ **signal_kwargs,
163
+ )
164
+ old_relevant_values = getattr(instance_before, "_old_values", {})
165
+ pre_identification = deepcopy(
166
+ getattr(instance_before, "identification", None)
167
+ )
168
+ operation_context = (
169
+ authorize_data_change_operation(database_alias)
170
+ if is_orm_backed
171
+ else nullcontext()
172
+ )
173
+ with operation_context:
174
+ if isinstance(func, classmethod):
175
+ inner = cast(Callable[P, R], func.__func__)
176
+ result = inner(*args, **kwargs)
177
+ else:
178
+ result = func(*args, **kwargs)
179
+
180
+ context = current_calculation_run_context()
181
+ if context is not None:
182
+ context.clear_orm_bucket_results()
183
+ context.clear_bucket_indexes()
184
+ context.clear_trusted_orm_managers()
185
+
186
+ instance = result
187
+ identification = getattr(instance, "identification", None)
188
+ if identification is None:
189
+ identification = pre_identification
190
+
191
+ post_data_change.send(
192
+ sender=sender,
193
+ instance=instance,
194
+ previous_instance=instance_before,
195
+ identification=identification,
196
+ action=action,
197
+ old_relevant_values=old_relevant_values,
198
+ **signal_kwargs,
199
+ )
200
+ if instance_before is not None:
201
+ try:
202
+ delattr(instance_before, "_old_values")
203
+ except AttributeError:
204
+ pass
147
205
  completed = True
148
206
  except BaseException as error:
149
207
  primary_exc = error
@@ -3,14 +3,15 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from collections.abc import Iterator
6
- from contextlib import contextmanager
6
+ from contextlib import AbstractContextManager, contextmanager
7
7
  from contextvars import ContextVar
8
8
  from typing import TYPE_CHECKING, Any, ClassVar, cast
9
9
 
10
10
  from django.contrib.auth import get_user_model
11
- from django.db import models, transaction
11
+ from django.db import DEFAULT_DB_ALIAS, models, 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
15
  from general_manager.interface.capabilities.base import CapabilityName
15
16
  from general_manager.interface.capabilities.builtin import BaseCapability
16
17
  from general_manager.interface.capabilities.orm_utils.payload_normalizer import (
@@ -67,6 +68,23 @@ def _without_save_with_history_savepoint() -> Iterator[None]:
67
68
  _save_with_history_savepoint.reset(token)
68
69
 
69
70
 
71
+ def _mutation_atomic(
72
+ database_alias: str | None,
73
+ *,
74
+ savepoint: bool = True,
75
+ ) -> AbstractContextManager[None]:
76
+ """Open a mutation boundary without nesting under its signal envelope."""
77
+ alias = database_alias or DEFAULT_DB_ALIAS
78
+ use_savepoint = savepoint and not owns_data_change_transaction(alias)
79
+ if database_alias:
80
+ if use_savepoint:
81
+ return transaction.atomic(using=database_alias)
82
+ return transaction.atomic(using=database_alias, savepoint=False)
83
+ if use_savepoint:
84
+ return transaction.atomic()
85
+ return transaction.atomic(savepoint=False)
86
+
87
+
70
88
  class OrmMutationCapability(BaseCapability):
71
89
  """Common utilities to modify ORM instances."""
72
90
 
@@ -174,19 +192,10 @@ class OrmMutationCapability(BaseCapability):
174
192
  database_alias = support.get_database_alias(interface_cls)
175
193
  if database_alias:
176
194
  instance._state.db = database_alias
177
- savepoint = _save_with_history_savepoint.get()
178
- if database_alias:
179
- atomic_context = (
180
- transaction.atomic(using=database_alias)
181
- if savepoint
182
- else transaction.atomic(using=database_alias, savepoint=False)
183
- )
184
- else:
185
- atomic_context = (
186
- transaction.atomic()
187
- if savepoint
188
- else transaction.atomic(savepoint=False)
189
- )
195
+ atomic_context = _mutation_atomic(
196
+ database_alias,
197
+ savepoint=_save_with_history_savepoint.get(),
198
+ )
190
199
  with atomic_context:
191
200
  _assign_history_actor(
192
201
  instance,
@@ -388,11 +397,7 @@ class OrmCreateCapability(BaseCapability):
388
397
  )
389
398
  support = get_support_capability(interface_cls)
390
399
  database_alias = support.get_database_alias(interface_cls)
391
- atomic_context = (
392
- transaction.atomic(using=database_alias)
393
- if database_alias
394
- else transaction.atomic()
395
- )
400
+ atomic_context = _mutation_atomic(database_alias)
396
401
  with atomic_context:
397
402
  with _without_save_with_history_savepoint():
398
403
  pk = mutation.save_with_history(
@@ -538,11 +543,7 @@ class OrmUpdateCapability(BaseCapability):
538
543
  interface_instance.__class__, instance, normalized_simple
539
544
  )
540
545
  database_alias = support.get_database_alias(interface_instance.__class__)
541
- atomic_context = (
542
- transaction.atomic(using=database_alias)
543
- if database_alias
544
- else transaction.atomic()
545
- )
546
+ atomic_context = _mutation_atomic(database_alias)
546
547
  with atomic_context:
547
548
  with _without_save_with_history_savepoint():
548
549
  pk = mutation.save_with_history(
@@ -666,11 +667,7 @@ class OrmDeleteCapability(BaseCapability):
666
667
  if model_has_field(instance, "changed_by"):
667
668
  object.__setattr__(instance, "changed_by_id", creator_id)
668
669
  call_update_change_reason(instance, history_comment_local)
669
- atomic_context = (
670
- transaction.atomic(using=database_alias)
671
- if database_alias
672
- else transaction.atomic()
673
- )
670
+ atomic_context = _mutation_atomic(database_alias)
674
671
  with atomic_context:
675
672
  if database_alias:
676
673
  instance.delete(using=database_alias)
@@ -0,0 +1,20 @@
1
+ # Generated by Django 5.2.10 on 2026-07-15 00:00
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ (
9
+ "general_manager",
10
+ "0010_chat_pending_confirmation_portable_uniqueness",
11
+ ),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.AddField(
16
+ model_name="searchindexstate",
17
+ name="dirty_generation",
18
+ field=models.PositiveBigIntegerField(default=0),
19
+ ),
20
+ ]
@@ -22,6 +22,11 @@ GENERAL_MANAGER_EXPORTS: LazyExportMap = {
22
22
  "IndexConfig": ("general_manager.search.config", "IndexConfig"),
23
23
  "SearchConfigProtocol": ("general_manager.search.config", "SearchConfigProtocol"),
24
24
  "SearchConfigSpec": ("general_manager.search.config", "SearchConfigSpec"),
25
+ "SearchChange": ("general_manager.search.config", "SearchChange"),
26
+ "SearchInvalidationRule": (
27
+ "general_manager.search.config",
28
+ "SearchInvalidationRule",
29
+ ),
25
30
  "resolve_search_config": (
26
31
  "general_manager.search.config",
27
32
  "resolve_search_config",
@@ -678,6 +683,11 @@ SEARCH_EXPORTS: LazyExportMap = {
678
683
  "IndexConfig": ("general_manager.search.config", "IndexConfig"),
679
684
  "SearchConfigProtocol": ("general_manager.search.config", "SearchConfigProtocol"),
680
685
  "SearchConfigSpec": ("general_manager.search.config", "SearchConfigSpec"),
686
+ "SearchChange": ("general_manager.search.config", "SearchChange"),
687
+ "SearchInvalidationRule": (
688
+ "general_manager.search.config",
689
+ "SearchInvalidationRule",
690
+ ),
681
691
  "resolve_search_config": (
682
692
  "general_manager.search.config",
683
693
  "resolve_search_config",