GeneralManager 0.69.2__tar.gz → 0.71.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 (297) hide show
  1. {generalmanager-0.69.2/src/GeneralManager.egg-info → generalmanager-0.71.0}/PKG-INFO +1 -1
  2. {generalmanager-0.69.2 → generalmanager-0.71.0}/pyproject.toml +1 -1
  3. {generalmanager-0.69.2 → generalmanager-0.71.0/src/GeneralManager.egg-info}/PKG-INFO +1 -1
  4. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/permission.py +2 -0
  5. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_errors.py +8 -3
  6. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_resolvers.py +60 -41
  7. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_search.py +16 -4
  8. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/bucket/base_bucket.py +16 -1
  9. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/bucket/calculation_bucket.py +95 -5
  10. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/bucket/database_bucket.py +17 -1
  11. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/bucket/request_bucket.py +12 -1
  12. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/permission/base_permission.py +104 -26
  13. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/permission/manager_based_permission.py +290 -97
  14. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/permission/permission_checks.py +140 -35
  15. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/public_api_registry.py +4 -0
  16. {generalmanager-0.69.2 → generalmanager-0.71.0}/LICENSE +0 -0
  17. {generalmanager-0.69.2 → generalmanager-0.71.0}/README.md +0 -0
  18. {generalmanager-0.69.2 → generalmanager-0.71.0}/setup.cfg +0 -0
  19. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
  20. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
  21. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/GeneralManager.egg-info/requires.txt +0 -0
  22. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/GeneralManager.egg-info/top_level.txt +0 -0
  23. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/__init__.py +0 -0
  24. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/__init__.py +0 -0
  25. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/api.py +0 -0
  26. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/bucket.py +0 -0
  27. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/cache.py +0 -0
  28. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/dataframes.py +0 -0
  29. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/factory.py +0 -0
  30. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/general_manager.py +0 -0
  31. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/interface.py +0 -0
  32. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/manager.py +0 -0
  33. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/measurement.py +0 -0
  34. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/rule.py +0 -0
  35. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/search.py +0 -0
  36. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/_types/utils.py +0 -0
  37. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/__init__.py +0 -0
  38. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql.py +0 -0
  39. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_as_of.py +0 -0
  40. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_mutations.py +0 -0
  41. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_prefetch.py +0 -0
  42. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_relations.py +0 -0
  43. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
  44. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_subscriptions.py +0 -0
  45. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_view.py +0 -0
  46. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_warmup.py +0 -0
  47. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_warmup_registry.py +0 -0
  48. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
  49. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/mutation.py +0 -0
  50. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/notification_batching.py +0 -0
  51. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/property.py +0 -0
  52. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/registry.py +0 -0
  53. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/remote_api.py +0 -0
  54. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/remote_invalidation.py +0 -0
  55. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/api/remote_invalidation_client.py +0 -0
  56. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/apps.py +0 -0
  57. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/as_of.py +0 -0
  58. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/bootstrap.py +0 -0
  59. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/bucket/__init__.py +0 -0
  60. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/bucket/group_bucket.py +0 -0
  61. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/bucket/indexing.py +0 -0
  62. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/__init__.py +0 -0
  63. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/cache_decorator.py +0 -0
  64. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/cache_tracker.py +0 -0
  65. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/data_change_context.py +0 -0
  66. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/dependency_cache.py +0 -0
  67. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/dependency_index.py +0 -0
  68. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/dependency_matching.py +0 -0
  69. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/dependency_publish.py +0 -0
  70. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/dependency_shards.py +0 -0
  71. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/model_dependency_collector.py +0 -0
  72. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/run_context.py +0 -0
  73. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/run_context_lru.py +0 -0
  74. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/cache/signals.py +0 -0
  75. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/__init__.py +0 -0
  76. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/audit.py +0 -0
  77. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/bootstrap.py +0 -0
  78. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/checks.py +0 -0
  79. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/consumer.py +0 -0
  80. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/errors.py +0 -0
  81. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/__init__.py +0 -0
  82. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/__main__.py +0 -0
  83. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/baseline.py +0 -0
  84. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/datasets/basic_queries.yaml +0 -0
  85. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/datasets/demo_readiness.yaml +0 -0
  86. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/datasets/edge_cases.yaml +0 -0
  87. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/datasets/follow_ups.yaml +0 -0
  88. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/datasets/large_schema.yaml +0 -0
  89. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/datasets/multi_hop.yaml +0 -0
  90. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/diagnostics.py +0 -0
  91. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/fingerprints.py +0 -0
  92. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/fixtures.py +0 -0
  93. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/judges/__init__.py +0 -0
  94. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
  95. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/judges/contract.py +0 -0
  96. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
  97. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
  98. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/readiness.py +0 -0
  99. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/runner.py +0 -0
  100. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/evals/traces.py +0 -0
  101. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/grounding.py +0 -0
  102. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/models.py +0 -0
  103. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/providers/__init__.py +0 -0
  104. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/providers/_shared.py +0 -0
  105. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/providers/anthropic.py +0 -0
  106. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/providers/base.py +0 -0
  107. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/providers/google.py +0 -0
  108. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/providers/ollama.py +0 -0
  109. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/providers/openai.py +0 -0
  110. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/rate_limits.py +0 -0
  111. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/routing.py +0 -0
  112. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/schema_index.py +0 -0
  113. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/settings.py +0 -0
  114. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/signals.py +0 -0
  115. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/system_prompt.py +0 -0
  116. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/tool_metadata.py +0 -0
  117. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/tools.py +0 -0
  118. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/chat/views.py +0 -0
  119. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/conf.py +0 -0
  120. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/dataframes/__init__.py +0 -0
  121. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/dataframes/measurements.py +0 -0
  122. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/factory/__init__.py +0 -0
  123. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/factory/auto_factory.py +0 -0
  124. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/factory/factories.py +0 -0
  125. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/factory/factory_methods.py +0 -0
  126. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/__init__.py +0 -0
  127. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/base_interface.py +0 -0
  128. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/bundles/__init__.py +0 -0
  129. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/bundles/calculation.py +0 -0
  130. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/bundles/database.py +0 -0
  131. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/bundles/remote_manager.py +0 -0
  132. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/bundles/request.py +0 -0
  133. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/__init__.py +0 -0
  134. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/base.py +0 -0
  135. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/builtin.py +0 -0
  136. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
  137. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
  138. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
  139. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/configuration.py +0 -0
  140. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/core/observability.py +0 -0
  141. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/core/utils.py +0 -0
  142. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/exceptions.py +0 -0
  143. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
  144. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
  145. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
  146. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/factory.py +0 -0
  147. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
  148. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
  149. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/orm/history.py +0 -0
  150. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
  151. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
  152. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/orm/support.py +0 -0
  153. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
  154. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
  155. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
  156. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
  157. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
  158. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
  159. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
  160. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
  161. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
  162. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/registry.py +0 -0
  163. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
  164. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
  165. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/infrastructure/__init__.py +0 -0
  166. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
  167. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
  168. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/interfaces/__init__.py +0 -0
  169. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/interfaces/calculation.py +0 -0
  170. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/interfaces/database.py +0 -0
  171. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/interfaces/existing_model.py +0 -0
  172. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/interfaces/read_only.py +0 -0
  173. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
  174. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/interfaces/request.py +0 -0
  175. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/manifests/__init__.py +0 -0
  176. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/manifests/capability_builder.py +0 -0
  177. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
  178. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/manifests/capability_models.py +0 -0
  179. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/orm_interface.py +0 -0
  180. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/requests.py +0 -0
  181. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/utils/__init__.py +0 -0
  182. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
  183. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/utils/errors.py +0 -0
  184. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/utils/history.py +0 -0
  185. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/interface/utils/models.py +0 -0
  186. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/logging.py +0 -0
  187. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/management/commands/chat_cleanup.py +0 -0
  188. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/management/commands/cleanup_upload_intents.py +0 -0
  189. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/management/commands/graphql_warmup.py +0 -0
  190. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
  191. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/management/commands/search_index.py +0 -0
  192. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/management/commands/search_reconcile.py +0 -0
  193. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
  194. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/management/commands/shell.py +0 -0
  195. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
  196. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
  197. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/manager/__init__.py +0 -0
  198. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/manager/general_manager.py +0 -0
  199. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/manager/group_manager.py +0 -0
  200. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/manager/input.py +0 -0
  201. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/manager/meta.py +0 -0
  202. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/measurement/__init__.py +0 -0
  203. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/measurement/measurement.py +0 -0
  204. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/measurement/measurement_field.py +0 -0
  205. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/metrics/__init__.py +0 -0
  206. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/metrics/graphql.py +0 -0
  207. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/0001_initial.py +0 -0
  208. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
  209. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
  210. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
  211. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/0004_search_index_state.py +0 -0
  212. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
  213. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
  214. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/0007_upload_intent.py +0 -0
  215. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/0008_upload_finalization_versions.py +0 -0
  216. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/0009_upload_cleanup_state.py +0 -0
  217. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/0010_chat_pending_confirmation_portable_uniqueness.py +0 -0
  218. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/0011_search_index_state_dirty_generation.py +0 -0
  219. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/migrations/__init__.py +0 -0
  220. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/models.py +0 -0
  221. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/permission/__init__.py +0 -0
  222. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/permission/audit.py +0 -0
  223. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/permission/graphql_capabilities.py +0 -0
  224. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/permission/mutation_permission.py +0 -0
  225. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/permission/permission_data_manager.py +0 -0
  226. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/permission/utils.py +0 -0
  227. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/py.typed +0 -0
  228. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/rule/__init__.py +0 -0
  229. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/rule/handler.py +0 -0
  230. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/rule/rule.py +0 -0
  231. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/__init__.py +0 -0
  232. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/async_tasks.py +0 -0
  233. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/backend.py +0 -0
  234. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/backend_registry.py +0 -0
  235. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/backends/__init__.py +0 -0
  236. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/backends/dev.py +0 -0
  237. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/backends/meilisearch.py +0 -0
  238. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/backends/opensearch.py +0 -0
  239. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/backends/typesense.py +0 -0
  240. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/checks.py +0 -0
  241. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/config.py +0 -0
  242. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/indexer.py +0 -0
  243. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/invalidation.py +0 -0
  244. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/m2m_invalidation.py +0 -0
  245. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/models.py +0 -0
  246. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/reconciliation.py +0 -0
  247. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/registry.py +0 -0
  248. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/tasks.py +0 -0
  249. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/search/utils.py +0 -0
  250. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/seeding/__init__.py +0 -0
  251. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/seeding/manager_landscape.py +0 -0
  252. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/__init__.py +0 -0
  253. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/adapters.py +0 -0
  254. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/checks.py +0 -0
  255. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/config.py +0 -0
  256. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/errors.py +0 -0
  257. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/finalization.py +0 -0
  258. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/graphql.py +0 -0
  259. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/graphql_types.py +0 -0
  260. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/metrics.py +0 -0
  261. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/models.py +0 -0
  262. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/public.py +0 -0
  263. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/s3.py +0 -0
  264. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/services.py +0 -0
  265. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/tokens.py +0 -0
  266. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/types.py +0 -0
  267. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/urls.py +0 -0
  268. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/uploads/views.py +0 -0
  269. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/utils/__init__.py +0 -0
  270. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/utils/args_to_kwargs.py +0 -0
  271. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/utils/filter_parser.py +0 -0
  272. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/utils/format_string.py +0 -0
  273. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/utils/json_encoder.py +0 -0
  274. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/utils/make_cache_key.py +0 -0
  275. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/utils/none_to_zero.py +0 -0
  276. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/utils/path_mapping.py +0 -0
  277. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/utils/public_api.py +0 -0
  278. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/utils/testing.py +0 -0
  279. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/utils/type_checks.py +0 -0
  280. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/__init__.py +0 -0
  281. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/actions.py +0 -0
  282. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/backend_registry.py +0 -0
  283. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/backends/__init__.py +0 -0
  284. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/backends/celery.py +0 -0
  285. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/backends/local.py +0 -0
  286. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/backends/n8n.py +0 -0
  287. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/config.py +0 -0
  288. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/engine.py +0 -0
  289. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/event_registry.py +0 -0
  290. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/events.py +0 -0
  291. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/models.py +0 -0
  292. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/signal_bridge.py +0 -0
  293. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/tasks.py +0 -0
  294. {generalmanager-0.69.2 → generalmanager-0.71.0}/src/general_manager/workflow/telemetry.py +0 -0
  295. {generalmanager-0.69.2 → generalmanager-0.71.0}/tests/test_settings.py +0 -0
  296. {generalmanager-0.69.2 → generalmanager-0.71.0}/tests/test_urls.py +0 -0
  297. {generalmanager-0.69.2 → generalmanager-0.71.0}/tests/testing_asgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.69.2
3
+ Version: 0.71.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.69.2"
7
+ version = "0.71.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.69.2
3
+ Version: 0.71.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
@@ -16,6 +16,7 @@ __all__ = [
16
16
  "OverrideManagerPermission",
17
17
  "PermissionAuditEvent",
18
18
  "PermissionDataManager",
19
+ "PermissionFilterDecision",
19
20
  "configure_audit_logger",
20
21
  "configure_audit_logger_from_settings",
21
22
  "mutation_capability",
@@ -44,6 +45,7 @@ from general_manager.permission.manager_based_permission import (
44
45
  )
45
46
  from general_manager.permission.audit import PermissionAuditEvent
46
47
  from general_manager.permission.permission_data_manager import PermissionDataManager
48
+ from general_manager.permission.permission_checks import PermissionFilterDecision
47
49
  from general_manager.permission.audit import configure_audit_logger
48
50
  from general_manager.permission.audit import configure_audit_logger_from_settings
49
51
  from general_manager.permission.graphql_capabilities import mutation_capability
@@ -655,7 +655,7 @@ def get_read_permission_filter(
655
655
 
656
656
  Returns:
657
657
  A ``ReadPermissionPlan`` with ``filters``, ``requires_instance_check``,
658
- and ``instance_check_reasons`` fields. ``ReadPermissionPlan`` is an
658
+ ``instance_check_reasons``, and ``decision`` fields. ``ReadPermissionPlan`` is an
659
659
  internal adapter from ``general_manager.permission.base_permission``, not
660
660
  a stable public import path, but generated resolvers rely on these
661
661
  fields.
@@ -677,7 +677,8 @@ def get_read_permission_filter(
677
677
  ``("no_prefilter_backend",)``.
678
678
 
679
679
  Managers without a permission factory receive an empty filter plan and
680
- do not require instance checks. This is a default-allow read policy for
680
+ do not require instance checks, with an explicit ``"allow_all"``
681
+ decision. This is a default-allow read policy for
681
682
  managers that do not define ``Permission``; no additional GraphQL
682
683
  permission filtering or per-object read authorization is applied by this
683
684
  helper. More generally, ``filters=[]`` with
@@ -715,4 +716,8 @@ def get_read_permission_filter(
715
716
  )
716
717
  if permission_attribute is not None:
717
718
  raise InvalidReadPermissionConfigurationError(generalManagerClass.__name__)
718
- return ReadPermissionPlan(filters=[], requires_instance_check=False)
719
+ return ReadPermissionPlan(
720
+ filters=[],
721
+ requires_instance_check=False,
722
+ decision="allow_all",
723
+ )
@@ -90,12 +90,17 @@ class UnsupportedExcludeNoneRelationFilterError(ValueError):
90
90
 
91
91
  @dataclass(slots=True)
92
92
  class ReadAuthorizationResult(Generic[GeneralManagerT]):
93
- """Aggregate outcome from GraphQL read prefiltering and row authorization."""
93
+ """Aggregate outcome from GraphQL read prefiltering and row authorization.
94
+
95
+ Counts remain ``None`` when authorization can return a bucket lazily without
96
+ inspecting or counting it. They are populated when a row-level gate actually
97
+ scans candidates; deny-all plans use known zero counts.
98
+ """
94
99
 
95
100
  queryset: Bucket[GeneralManagerT]
96
- candidate_count: int
97
- authorized_count: int
98
- denied_count: int
101
+ candidate_count: int | None
102
+ authorized_count: int | None
103
+ denied_count: int | None
99
104
  backend_shape: str
100
105
  requires_instance_check: bool
101
106
  instance_check_reasons: tuple[str, ...]
@@ -266,7 +271,7 @@ def apply_read_authorization(
266
271
  final row gate runs and an aggregate log event is emitted only if the final
267
272
  authorization result still requires instance checks.
268
273
  Unrestricted plans that do not require instance checks return the original
269
- queryset with matching candidate and authorized counts.
274
+ queryset without evaluating it to compute counts.
270
275
  """
271
276
  permission_plan = get_read_permission_filter(general_manager_class, info)
272
277
  backend_shape = get_backend_shape(general_manager_class)
@@ -275,16 +280,40 @@ def apply_read_authorization(
275
280
  backend_shape=backend_shape,
276
281
  )
277
282
 
278
- filtered_queryset = queryset
279
- if not permission_plan.requires_instance_check and permission_plan.filters == [
280
- {"filter": {}, "exclude": {}}
281
- ]:
282
- candidate_count = len(queryset)
283
+ if permission_plan.decision == "deny_all":
284
+ return ReadAuthorizationResult(
285
+ queryset=queryset.none(),
286
+ candidate_count=0,
287
+ authorized_count=0,
288
+ denied_count=0,
289
+ backend_shape=backend_shape,
290
+ requires_instance_check=False,
291
+ instance_check_reasons=(),
292
+ )
293
+
294
+ if permission_plan.decision == "allow_all":
295
+ return ReadAuthorizationResult(
296
+ queryset=queryset,
297
+ candidate_count=None,
298
+ authorized_count=None,
299
+ denied_count=None,
300
+ backend_shape=backend_shape,
301
+ requires_instance_check=False,
302
+ instance_check_reasons=(),
303
+ )
304
+
305
+ filtered_queryset: Bucket[GeneralManagerT] | None = queryset
306
+ if (
307
+ not permission_plan.requires_instance_check
308
+ and len(permission_plan.filters) == 1
309
+ and not permission_plan.filters[0].get("filter")
310
+ and not permission_plan.filters[0].get("exclude")
311
+ ):
283
312
  result = ReadAuthorizationResult(
284
313
  queryset=queryset,
285
- candidate_count=candidate_count,
286
- authorized_count=candidate_count,
287
- denied_count=0,
314
+ candidate_count=None,
315
+ authorized_count=None,
316
+ denied_count=None,
288
317
  backend_shape=backend_shape,
289
318
  requires_instance_check=False,
290
319
  instance_check_reasons=instance_check_reasons,
@@ -292,7 +321,7 @@ def apply_read_authorization(
292
321
  return result
293
322
 
294
323
  if permission_plan.filters:
295
- filtered_queryset = queryset.none()
324
+ filtered_queryset = None
296
325
  for permission_filter in permission_plan.filters:
297
326
  filter_dict = permission_filter.get("filter", {})
298
327
  exclude_dict = permission_filter.get("exclude", {})
@@ -300,7 +329,10 @@ def apply_read_authorization(
300
329
  qs_perm = queryset
301
330
  else:
302
331
  qs_perm = queryset.filter(**filter_dict).exclude(**exclude_dict)
303
- filtered_queryset = filtered_queryset | qs_perm
332
+ filtered_queryset = (
333
+ qs_perm if filtered_queryset is None else filtered_queryset | qs_perm
334
+ )
335
+ assert filtered_queryset is not None
304
336
 
305
337
  result = filter_queryset_by_read_permission(
306
338
  filtered_queryset,
@@ -332,19 +364,17 @@ def filter_queryset_by_read_permission(
332
364
  Apply final row-level read authorization to a bucket.
333
365
 
334
366
  When an instance gate is not required, or the manager has no Permission
335
- class, the original queryset is returned with all candidates authorized.
336
- Otherwise each candidate is checked with ``can_read_instance()``. Authorized
337
- rows with an ``identification["id"]`` are rebuilt through an ``id__in``
338
- filter; authorized rows without an id are unioned back as concrete manager
339
- instances.
367
+ class, the original queryset is returned without eagerly computing counts.
368
+ Otherwise each candidate is checked with ``can_read_instance()`` and the
369
+ originating bucket reconstructs the exact authorized instance subset using
370
+ its backend-native representation.
340
371
  """
341
372
  if not requires_instance_check:
342
- candidate_count = len(queryset)
343
373
  return ReadAuthorizationResult(
344
374
  queryset=queryset,
345
- candidate_count=candidate_count,
346
- authorized_count=candidate_count,
347
- denied_count=0,
375
+ candidate_count=None,
376
+ authorized_count=None,
377
+ denied_count=None,
348
378
  backend_shape=backend_shape,
349
379
  requires_instance_check=False,
350
380
  instance_check_reasons=instance_check_reasons,
@@ -354,36 +384,25 @@ def filter_queryset_by_read_permission(
354
384
  general_manager_class, "Permission", None
355
385
  )
356
386
  if PermissionClass is None:
357
- candidate_count = len(queryset)
358
387
  return ReadAuthorizationResult(
359
388
  queryset=queryset,
360
- candidate_count=candidate_count,
361
- authorized_count=candidate_count,
362
- denied_count=0,
389
+ candidate_count=None,
390
+ authorized_count=None,
391
+ denied_count=None,
363
392
  backend_shape=backend_shape,
364
393
  requires_instance_check=False,
365
394
  instance_check_reasons=instance_check_reasons,
366
395
  )
367
396
 
368
- authorized_ids: list[object] = []
369
397
  authorized_instances: list[GeneralManagerT] = []
370
398
  candidate_count = 0
371
399
  for instance in queryset:
372
400
  candidate_count += 1
373
401
  if PermissionClass(instance, info.context.user).can_read_instance():
374
- identification = cast(Mapping[str, object], instance.identification)
375
- instance_id = identification.get("id")
376
- if instance_id is None:
377
- authorized_instances.append(instance)
378
- else:
379
- authorized_ids.append(instance_id)
402
+ authorized_instances.append(instance)
380
403
 
381
- authorized_queryset = (
382
- queryset.filter(id__in=authorized_ids) if authorized_ids else queryset.none()
383
- )
384
- for instance in authorized_instances:
385
- authorized_queryset = authorized_queryset | instance
386
- authorized_count = len(authorized_ids) + len(authorized_instances)
404
+ authorized_queryset = queryset.with_instances(authorized_instances)
405
+ authorized_count = len(authorized_instances)
387
406
  return ReadAuthorizationResult(
388
407
  queryset=authorized_queryset,
389
408
  candidate_count=candidate_count,
@@ -382,6 +382,11 @@ def passes_permission_filters(
382
382
  """
383
383
  if permission_plan is None:
384
384
  permission_plan = get_read_permission_filter(instance.__class__, info)
385
+ decision = getattr(permission_plan, "decision", "conditional")
386
+ if decision == "allow_all":
387
+ return True
388
+ if decision == "deny_all":
389
+ return False
385
390
  permission_filters = permission_plan.filters
386
391
  if not permission_filters:
387
392
  return can_read_instance(instance, info)
@@ -1046,14 +1051,21 @@ def register_search_query(
1046
1051
  for manager_class in manager_classes:
1047
1052
  type_label = manager_class.__name__
1048
1053
  permission_plan = get_read_permission_filter(manager_class, info)
1054
+ decision = getattr(permission_plan, "decision", "conditional")
1055
+ if decision == "deny_all":
1056
+ continue
1049
1057
  backend_shape = get_backend_shape(manager_class)
1050
1058
  instance_check_reasons = resolve_instance_check_reasons(
1051
1059
  permission_plan,
1052
1060
  backend_shape=backend_shape,
1053
1061
  )
1054
- filter_groups = merge_permission_filters(
1055
- parsed_filters,
1056
- permission_plan.filters,
1062
+ filter_groups = (
1063
+ parsed_filters or None
1064
+ if decision == "allow_all"
1065
+ else merge_permission_filters(
1066
+ parsed_filters,
1067
+ permission_plan.filters,
1068
+ )
1057
1069
  )
1058
1070
  total_hits_for_manager = 0
1059
1071
  candidate_hits_for_manager = 0
@@ -1127,7 +1139,7 @@ def register_search_query(
1127
1139
  total += total_hits_for_manager
1128
1140
  if not manager_total_is_exact:
1129
1141
  total_is_exact = False
1130
- if permission_plan.requires_instance_check:
1142
+ if decision == "conditional" and permission_plan.requires_instance_check:
1131
1143
  logger.info(
1132
1144
  "graphql read authorization summary",
1133
1145
  context={
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
  from abc import ABC, abstractmethod
5
- from collections.abc import Hashable, Mapping
5
+ from collections.abc import Hashable, Iterable, Mapping
6
6
  from typing import (
7
7
  Generator,
8
8
  TYPE_CHECKING,
@@ -437,3 +437,18 @@ class Bucket(ABC, Generic[GeneralManagerType]):
437
437
  "The 'none' method is not implemented in the base Bucket class. "
438
438
  "Subclasses should implement this method to return an empty bucket."
439
439
  )
440
+
441
+ def with_instances(
442
+ self,
443
+ instances: Iterable[GeneralManagerType],
444
+ ) -> Bucket[GeneralManagerType]:
445
+ """Return a bucket containing exactly the supplied manager instances.
446
+
447
+ Callers provide instances in source iteration order and implementations
448
+ retain that order. Backends may override this compatibility fallback to
449
+ avoid repeated unions or unsupported lookups.
450
+ """
451
+ subset = self.none()
452
+ for instance in instances:
453
+ subset = subset | instance
454
+ return subset
@@ -219,6 +219,7 @@ class CalculationBucket(Bucket[GeneralManagerType]):
219
219
  self._excludes = parse_filters(self.exclude_definitions, possible_values)
220
220
 
221
221
  self._data: list[Combination] | None = None
222
+ self._allowed_identifications: list[Combination] | None = None
222
223
  self.sort_key = sort_key
223
224
  self.reverse = reverse
224
225
  self._effective_search_date = current_as_of_date()
@@ -251,6 +252,10 @@ class CalculationBucket(Bucket[GeneralManagerType]):
251
252
  reverse,
252
253
  )
253
254
  bucket._effective_search_date = self._effective_search_date
255
+ if self._allowed_identifications is not None:
256
+ bucket._allowed_identifications = [
257
+ dict(identification) for identification in self._allowed_identifications
258
+ ]
254
259
  return bucket
255
260
 
256
261
  def __eq__(self, other: object) -> bool:
@@ -292,6 +297,7 @@ class CalculationBucket(Bucket[GeneralManagerType]):
292
297
  ),
293
298
  {
294
299
  "data": self._data,
300
+ "allowed_identifications": self._allowed_identifications,
295
301
  "effective_search_date": self._effective_search_date,
296
302
  },
297
303
  )
@@ -307,6 +313,10 @@ class CalculationBucket(Bucket[GeneralManagerType]):
307
313
  None
308
314
  """
309
315
  self._data = cast(list[Combination] | None, state.get("data"))
316
+ self._allowed_identifications = cast(
317
+ list[Combination] | None,
318
+ state.get("allowed_identifications"),
319
+ )
310
320
  self._effective_search_date = cast(
311
321
  "datetime | None", state.get("effective_search_date")
312
322
  )
@@ -360,12 +370,29 @@ class CalculationBucket(Bucket[GeneralManagerType]):
360
370
  }
361
371
 
362
372
  other._ensure_as_of_compatible()
363
- return self._derive(
373
+ combined = self._derive(
364
374
  filter_definitions=combined_filters,
365
375
  exclude_definitions=combined_excludes,
366
376
  sort_key=None,
367
377
  reverse=False,
368
378
  )
379
+ if self._allowed_identifications is None:
380
+ if other._allowed_identifications is not None:
381
+ combined._allowed_identifications = [
382
+ dict(identification)
383
+ for identification in other._allowed_identifications
384
+ ]
385
+ elif other._allowed_identifications is not None:
386
+ other_allowed_keys = {
387
+ freeze_bucket_index_value(identification)
388
+ for identification in other._allowed_identifications
389
+ }
390
+ combined._allowed_identifications = [
391
+ dict(identification)
392
+ for identification in self._allowed_identifications
393
+ if freeze_bucket_index_value(identification) in other_allowed_keys
394
+ ]
395
+ return combined
369
396
 
370
397
  def __str__(self) -> str:
371
398
  """
@@ -431,11 +458,22 @@ class CalculationBucket(Bucket[GeneralManagerType]):
431
458
  sorted_filters["input_excludes"],
432
459
  snapshot_iterables=snapshot_iterables,
433
460
  )
434
- if sorted_filters["prop_filters"] or sorted_filters["prop_excludes"]:
461
+ allowed_identification_keys = None
462
+ if self._allowed_identifications is not None:
463
+ allowed_identification_keys = {
464
+ freeze_bucket_index_value(identification)
465
+ for identification in self._allowed_identifications
466
+ }
467
+ if (
468
+ sorted_filters["prop_filters"]
469
+ or sorted_filters["prop_excludes"]
470
+ or allowed_identification_keys is not None
471
+ ):
435
472
  preview_iterator = self._iter_prop_filtered_identifications(
436
473
  preview_iterator,
437
474
  sorted_filters["prop_filters"],
438
475
  sorted_filters["prop_excludes"],
476
+ allowed_identification_keys,
439
477
  )
440
478
  preview = list(islice(preview_iterator, limit + 1))
441
479
 
@@ -656,6 +694,7 @@ class CalculationBucket(Bucket[GeneralManagerType]):
656
694
  self._manager_class,
657
695
  freeze_bucket_index_value(self.filter_definitions),
658
696
  freeze_bucket_index_value(self.exclude_definitions),
697
+ freeze_bucket_index_value(self._allowed_identifications),
659
698
  self._normalized_sort_key(),
660
699
  self.reverse,
661
700
  )
@@ -738,6 +777,21 @@ class CalculationBucket(Bucket[GeneralManagerType]):
738
777
  sorted_filters["input_filters"],
739
778
  sorted_filters["input_excludes"],
740
779
  )
780
+ manager_combinations: list[GeneralManagerType] | None = None
781
+ if self._allowed_identifications is not None:
782
+ allowed_identification_keys = {
783
+ freeze_bucket_index_value(identification)
784
+ for identification in self._allowed_identifications
785
+ }
786
+ manager_combinations = [
787
+ manager
788
+ for manager in self._manager_combinations(current_combinations)
789
+ if freeze_bucket_index_value(manager.identification)
790
+ in allowed_identification_keys
791
+ ]
792
+ current_combinations = self._manager_identifications(
793
+ manager_combinations
794
+ )
741
795
  sort_key = self._normalized_sort_key()
742
796
  needs_manager_access = (
743
797
  bool(sorted_filters["prop_filters"])
@@ -746,9 +800,10 @@ class CalculationBucket(Bucket[GeneralManagerType]):
746
800
  )
747
801
 
748
802
  if needs_manager_access:
749
- manager_combinations = self._manager_combinations(
750
- current_combinations
751
- )
803
+ if manager_combinations is None:
804
+ manager_combinations = self._manager_combinations(
805
+ current_combinations
806
+ )
752
807
  manager_combinations = self._filter_prop_combinations(
753
808
  manager_combinations,
754
809
  sorted_filters["prop_filters"],
@@ -1006,6 +1061,7 @@ class CalculationBucket(Bucket[GeneralManagerType]):
1006
1061
  combinations: Iterable[Combination],
1007
1062
  prop_filters: ParsedFilters,
1008
1063
  prop_excludes: ParsedFilters,
1064
+ allowed_identification_keys: set[Hashable] | None = None,
1009
1065
  ) -> Generator[Combination, None, None]:
1010
1066
  """
1011
1067
  Lazily apply property filters and yield manager identifications.
@@ -1016,6 +1072,12 @@ class CalculationBucket(Bucket[GeneralManagerType]):
1016
1072
  """
1017
1073
  for combo in combinations:
1018
1074
  manager = self._manager_class(**combo)
1075
+ if (
1076
+ allowed_identification_keys is not None
1077
+ and freeze_bucket_index_value(manager.identification)
1078
+ not in allowed_identification_keys
1079
+ ):
1080
+ continue
1019
1081
  if self._filter_prop_combinations([manager], prop_filters, prop_excludes):
1020
1082
  yield manager.identification
1021
1083
 
@@ -1222,4 +1284,32 @@ class CalculationBucket(Bucket[GeneralManagerType]):
1222
1284
  own.exclude_definitions = {}
1223
1285
  own._filters = {}
1224
1286
  own._excludes = {}
1287
+ own._allowed_identifications = []
1225
1288
  return own
1289
+
1290
+ def with_instances(
1291
+ self,
1292
+ instances: Iterable[GeneralManagerType],
1293
+ ) -> CalculationBucket[GeneralManagerType]:
1294
+ """Return a materialized subset using declared input identifications."""
1295
+ self._ensure_as_of_compatible()
1296
+ from general_manager.manager.general_manager import GeneralManager
1297
+
1298
+ identifications: list[Combination] = []
1299
+ for instance in instances:
1300
+ if instance.__class__ != self._manager_class:
1301
+ raise IncompatibleBucketTypeError(self.__class__, type(instance))
1302
+ if isinstance(instance, GeneralManager):
1303
+ instance._ensure_as_of_compatible()
1304
+ identifications.append(dict(instance.identification))
1305
+ subset = self._derive(
1306
+ filter_definitions=self.filter_definitions.copy(),
1307
+ exclude_definitions=self.exclude_definitions.copy(),
1308
+ sort_key=self.sort_key,
1309
+ reverse=self.reverse,
1310
+ )
1311
+ subset._allowed_identifications = [
1312
+ dict(identification) for identification in identifications
1313
+ ]
1314
+ subset._data = identifications
1315
+ return subset
@@ -1,7 +1,7 @@
1
1
  """Database-backed bucket implementation for GeneralManager collections."""
2
2
 
3
3
  from __future__ import annotations
4
- from collections.abc import Callable, Hashable, Mapping
4
+ from collections.abc import Callable, Hashable, Iterable, Mapping
5
5
  from datetime import date, datetime
6
6
  from typing import TYPE_CHECKING, Generator, TypeVar, cast
7
7
 
@@ -1533,3 +1533,19 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
1533
1533
  own._query_signature_cache = _QUERY_SIGNATURE_NOT_COMPUTED
1534
1534
  own._trusted_query_signature = None
1535
1535
  return own
1536
+
1537
+ def with_instances(
1538
+ self,
1539
+ instances: Iterable[GeneralManagerType],
1540
+ ) -> DatabaseBucket[GeneralManagerType]:
1541
+ """Return a source-ordered subset selected by manager primary keys."""
1542
+ self._ensure_as_of_compatible()
1543
+ ids: list[object] = []
1544
+ for instance in instances:
1545
+ if not isinstance(instance, GeneralManager) or (
1546
+ instance.__class__ != self._manager_class
1547
+ ):
1548
+ raise DatabaseBucketTypeMismatchError(self.__class__, type(instance))
1549
+ instance._ensure_as_of_compatible()
1550
+ ids.append(instance.identification["id"])
1551
+ return self.filter(id__in=ids) if ids else self.none()
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from collections.abc import Generator, Hashable, Mapping
5
+ from collections.abc import Generator, Hashable, Iterable, Mapping
6
6
  from typing import TYPE_CHECKING, Protocol, cast
7
7
 
8
8
  from general_manager.bucket.base_bucket import Bucket, GeneralManagerType
@@ -452,6 +452,17 @@ class RequestBucket(Bucket[GeneralManagerType]):
452
452
  """Return an empty materialized bucket preserving the operation name."""
453
453
  return self._from_items(tuple())
454
454
 
455
+ def with_instances(
456
+ self,
457
+ instances: Iterable[GeneralManagerType],
458
+ ) -> "RequestBucket[GeneralManagerType]":
459
+ """Return a materialized subset without re-executing the request plan."""
460
+ items = tuple(instances)
461
+ for instance in items:
462
+ if not isinstance(instance, self._manager_class):
463
+ raise RequestBucketTypeMismatchError(self.__class__, type(instance))
464
+ return self._from_items(items)
465
+
455
466
  @property
456
467
  def operation_name(self) -> str:
457
468
  """Return the query operation name preserved on this bucket.