GeneralManager 0.37.0__tar.gz → 0.37.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. {generalmanager-0.37.0/src/GeneralManager.egg-info → generalmanager-0.37.2}/PKG-INFO +1 -1
  2. {generalmanager-0.37.0 → generalmanager-0.37.2}/pyproject.toml +1 -1
  3. {generalmanager-0.37.0 → generalmanager-0.37.2/src/GeneralManager.egg-info}/PKG-INFO +1 -1
  4. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/bucket/database_bucket.py +100 -6
  5. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/cache/dependency_index.py +192 -12
  6. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/orm/support.py +170 -2
  7. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/manager/general_manager.py +0 -10
  8. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/utils/testing.py +1 -0
  9. {generalmanager-0.37.0 → generalmanager-0.37.2}/LICENSE +0 -0
  10. {generalmanager-0.37.0 → generalmanager-0.37.2}/README.md +0 -0
  11. {generalmanager-0.37.0 → generalmanager-0.37.2}/setup.cfg +0 -0
  12. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
  13. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
  14. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/GeneralManager.egg-info/requires.txt +0 -0
  15. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/GeneralManager.egg-info/top_level.txt +0 -0
  16. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/__init__.py +0 -0
  17. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/_types/__init__.py +0 -0
  18. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/_types/api.py +0 -0
  19. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/_types/bucket.py +0 -0
  20. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/_types/cache.py +0 -0
  21. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/_types/factory.py +0 -0
  22. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/_types/general_manager.py +0 -0
  23. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/_types/interface.py +0 -0
  24. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/_types/manager.py +0 -0
  25. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/_types/measurement.py +0 -0
  26. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/_types/permission.py +0 -0
  27. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/_types/rule.py +0 -0
  28. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/_types/utils.py +0 -0
  29. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/__init__.py +0 -0
  30. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/graphql.py +0 -0
  31. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/graphql_errors.py +0 -0
  32. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/graphql_mutations.py +0 -0
  33. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/graphql_resolvers.py +0 -0
  34. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/graphql_search.py +0 -0
  35. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
  36. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/graphql_subscriptions.py +0 -0
  37. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/graphql_view.py +0 -0
  38. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/mutation.py +0 -0
  39. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/property.py +0 -0
  40. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/registry.py +0 -0
  41. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/remote_api.py +0 -0
  42. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/remote_invalidation.py +0 -0
  43. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/api/remote_invalidation_client.py +0 -0
  44. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/apps.py +0 -0
  45. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/bootstrap.py +0 -0
  46. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/bucket/__init__.py +0 -0
  47. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/bucket/base_bucket.py +0 -0
  48. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/bucket/calculation_bucket.py +0 -0
  49. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/bucket/group_bucket.py +0 -0
  50. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/bucket/request_bucket.py +0 -0
  51. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/cache/__init__.py +0 -0
  52. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/cache/cache_decorator.py +0 -0
  53. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/cache/cache_tracker.py +0 -0
  54. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/cache/model_dependency_collector.py +0 -0
  55. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/cache/signals.py +0 -0
  56. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/conf.py +0 -0
  57. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/factory/__init__.py +0 -0
  58. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/factory/auto_factory.py +0 -0
  59. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/factory/factories.py +0 -0
  60. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/factory/factory_methods.py +0 -0
  61. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/__init__.py +0 -0
  62. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/base_interface.py +0 -0
  63. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/bundles/__init__.py +0 -0
  64. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/bundles/calculation.py +0 -0
  65. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/bundles/database.py +0 -0
  66. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/bundles/remote_manager.py +0 -0
  67. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/bundles/request.py +0 -0
  68. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/__init__.py +0 -0
  69. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/base.py +0 -0
  70. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/builtin.py +0 -0
  71. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
  72. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
  73. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
  74. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/configuration.py +0 -0
  75. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/core/observability.py +0 -0
  76. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/core/utils.py +0 -0
  77. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/exceptions.py +0 -0
  78. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
  79. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
  80. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
  81. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/factory.py +0 -0
  82. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
  83. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
  84. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/orm/history.py +0 -0
  85. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
  86. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
  87. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
  88. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
  89. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
  90. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
  91. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
  92. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
  93. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
  94. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
  95. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/registry.py +0 -0
  96. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
  97. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
  98. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
  99. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
  100. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/interfaces/__init__.py +0 -0
  101. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/interfaces/calculation.py +0 -0
  102. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/interfaces/database.py +0 -0
  103. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/interfaces/existing_model.py +0 -0
  104. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/interfaces/read_only.py +0 -0
  105. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
  106. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/interfaces/request.py +0 -0
  107. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/manifests/__init__.py +0 -0
  108. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/manifests/capability_builder.py +0 -0
  109. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
  110. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/manifests/capability_models.py +0 -0
  111. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/orm_interface.py +0 -0
  112. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/requests.py +0 -0
  113. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/utils/__init__.py +0 -0
  114. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
  115. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/utils/errors.py +0 -0
  116. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/interface/utils/models.py +0 -0
  117. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/logging.py +0 -0
  118. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/management/commands/search_index.py +0 -0
  119. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/management/commands/shell.py +0 -0
  120. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
  121. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
  122. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/manager/__init__.py +0 -0
  123. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/manager/group_manager.py +0 -0
  124. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/manager/input.py +0 -0
  125. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/manager/meta.py +0 -0
  126. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/measurement/__init__.py +0 -0
  127. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/measurement/measurement.py +0 -0
  128. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/measurement/measurement_field.py +0 -0
  129. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/metrics/__init__.py +0 -0
  130. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/metrics/graphql.py +0 -0
  131. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/migrations/0001_initial.py +0 -0
  132. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
  133. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
  134. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/migrations/__init__.py +0 -0
  135. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/models.py +0 -0
  136. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/permission/__init__.py +0 -0
  137. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/permission/audit.py +0 -0
  138. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/permission/base_permission.py +0 -0
  139. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/permission/manager_based_permission.py +0 -0
  140. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/permission/mutation_permission.py +0 -0
  141. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/permission/permission_checks.py +0 -0
  142. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/permission/permission_data_manager.py +0 -0
  143. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/permission/utils.py +0 -0
  144. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/public_api_registry.py +0 -0
  145. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/py.typed +0 -0
  146. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/rule/__init__.py +0 -0
  147. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/rule/handler.py +0 -0
  148. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/rule/rule.py +0 -0
  149. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/__init__.py +0 -0
  150. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/async_tasks.py +0 -0
  151. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/backend.py +0 -0
  152. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/backend_registry.py +0 -0
  153. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/backends/__init__.py +0 -0
  154. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/backends/dev.py +0 -0
  155. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/backends/meilisearch.py +0 -0
  156. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/backends/opensearch.py +0 -0
  157. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/backends/typesense.py +0 -0
  158. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/config.py +0 -0
  159. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/indexer.py +0 -0
  160. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/registry.py +0 -0
  161. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/search/utils.py +0 -0
  162. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/utils/__init__.py +0 -0
  163. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/utils/args_to_kwargs.py +0 -0
  164. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/utils/filter_parser.py +0 -0
  165. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/utils/format_string.py +0 -0
  166. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/utils/json_encoder.py +0 -0
  167. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/utils/make_cache_key.py +0 -0
  168. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/utils/none_to_zero.py +0 -0
  169. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/utils/path_mapping.py +0 -0
  170. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/utils/public_api.py +0 -0
  171. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/__init__.py +0 -0
  172. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/actions.py +0 -0
  173. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/backend_registry.py +0 -0
  174. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/backends/__init__.py +0 -0
  175. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/backends/celery.py +0 -0
  176. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/backends/local.py +0 -0
  177. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/backends/n8n.py +0 -0
  178. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/config.py +0 -0
  179. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/engine.py +0 -0
  180. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/event_registry.py +0 -0
  181. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/events.py +0 -0
  182. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/models.py +0 -0
  183. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/signal_bridge.py +0 -0
  184. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/tasks.py +0 -0
  185. {generalmanager-0.37.0 → generalmanager-0.37.2}/src/general_manager/workflow/telemetry.py +0 -0
  186. {generalmanager-0.37.0 → generalmanager-0.37.2}/tests/test_settings.py +0 -0
  187. {generalmanager-0.37.0 → generalmanager-0.37.2}/tests/test_urls.py +0 -0
  188. {generalmanager-0.37.0 → generalmanager-0.37.2}/tests/testing_asgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.37.0
3
+ Version: 0.37.2
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "GeneralManager"
7
- version = "0.37.0"
7
+ version = "0.37.2"
8
8
  description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.37.0
3
+ Version: 0.37.2
4
4
  Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: MIT License
@@ -8,6 +8,8 @@ from django.core.exceptions import FieldError
8
8
  from django.db import models
9
9
 
10
10
  from general_manager.bucket.base_bucket import Bucket
11
+ from general_manager.cache.cache_tracker import DependencyTracker
12
+ from general_manager.cache.dependency_index import serialize_dependency_identifier
11
13
  from general_manager.manager.general_manager import GeneralManager
12
14
  from general_manager.utils.filter_parser import create_filter_function
13
15
 
@@ -149,6 +151,8 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
149
151
  exclude_definitions: dict[str, list[Any]] | None = None,
150
152
  *,
151
153
  search_date: datetime | date | None = None,
154
+ sort_keys: tuple[str, ...] | None = None,
155
+ sort_reverse: bool = False,
152
156
  ) -> None:
153
157
  """
154
158
  Instantiate a database-backed bucket with optional filter state.
@@ -165,15 +169,77 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
165
169
  """
166
170
  self._data = data
167
171
  self._manager_class = manager_class
168
- self.filters = {**(filter_definitions or {})}
169
- self.excludes = {**(exclude_definitions or {})}
172
+ self.filters = self._copy_filter_definitions(filter_definitions)
173
+ self.excludes = self._copy_filter_definitions(exclude_definitions)
170
174
  self._search_date = search_date
175
+ self._sort_keys = sort_keys
176
+ self._sort_reverse = sort_reverse
171
177
 
172
178
  def _build_manager(self, pk: Any) -> GeneralManagerType:
173
179
  if self._search_date is None:
174
180
  return self._manager_class(pk)
175
181
  return self._manager_class(pk, search_date=self._search_date)
176
182
 
183
+ @staticmethod
184
+ def _copy_filter_definitions(
185
+ definitions: dict[str, Any] | None,
186
+ ) -> dict[str, list[Any]]:
187
+ """
188
+ Return a copy of filter/exclude definitions without sharing nested lists.
189
+ """
190
+ copied: dict[str, list[Any]] = {}
191
+ for key, values in (definitions or {}).items():
192
+ if isinstance(values, list):
193
+ copied[key] = list(values)
194
+ elif isinstance(values, tuple):
195
+ copied[key] = list(values)
196
+ else:
197
+ copied[key] = [values]
198
+ return copied
199
+
200
+ @staticmethod
201
+ def _normalize_dependency_mapping(
202
+ definitions: dict[str, Any],
203
+ ) -> dict[str, Any]:
204
+ return {
205
+ key: values[0]
206
+ if isinstance(values, (list, tuple)) and len(values) == 1
207
+ else values
208
+ for key, values in definitions.items()
209
+ }
210
+
211
+ def _track_effective_dependencies(self) -> None:
212
+ """Record the bucket's effective filter/exclude state when it is evaluated."""
213
+ manager_name = self._manager_class.__name__
214
+ normalized_filters = self._normalize_dependency_mapping(self.filters)
215
+ normalized_excludes = self._normalize_dependency_mapping(self.excludes)
216
+ if self.filters:
217
+ DependencyTracker.track(
218
+ manager_name,
219
+ "filter",
220
+ serialize_dependency_identifier(normalized_filters),
221
+ )
222
+ else:
223
+ DependencyTracker.track(manager_name, "all", "")
224
+ if self.excludes:
225
+ DependencyTracker.track(
226
+ manager_name,
227
+ "exclude",
228
+ serialize_dependency_identifier(normalized_excludes),
229
+ )
230
+ if self._sort_keys:
231
+ for sort_key in self._sort_keys:
232
+ payload = {
233
+ "filters": normalized_filters,
234
+ "excludes": normalized_excludes,
235
+ "reverse": self._sort_reverse,
236
+ }
237
+ DependencyTracker.track(
238
+ manager_name,
239
+ "filter",
240
+ serialize_dependency_identifier({f"__sort__{sort_key}": payload}),
241
+ )
242
+
177
243
  def __iter__(self) -> Generator[GeneralManagerType, None, None]:
178
244
  """
179
245
  Iterate over manager instances corresponding to the queryset rows.
@@ -181,6 +247,7 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
181
247
  Yields:
182
248
  GeneralManagerType: Manager instance for each primary key in the queryset.
183
249
  """
250
+ self._track_effective_dependencies()
184
251
  for item in self._data:
185
252
  yield self._build_manager(item.pk)
186
253
 
@@ -223,6 +290,8 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
223
290
  self._manager_class,
224
291
  {},
225
292
  search_date=self._search_date,
293
+ sort_keys=self._sort_keys,
294
+ sort_reverse=self._sort_reverse,
226
295
  )
227
296
 
228
297
  def __merge_filter_definitions(
@@ -238,9 +307,7 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
238
307
  Returns:
239
308
  dict[str, list[Any]]: Combined mapping of lookups to value lists.
240
309
  """
241
- kwarg_filter: dict[str, list[Any]] = {}
242
- for key, value in basis.items():
243
- kwarg_filter[key] = value
310
+ kwarg_filter = self._copy_filter_definitions(basis)
244
311
  for key, value in kwargs.items():
245
312
  if key not in kwarg_filter:
246
313
  kwarg_filter[key] = []
@@ -360,6 +427,8 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
360
427
  merged_filter,
361
428
  self.excludes,
362
429
  search_date=search_date,
430
+ sort_keys=self._sort_keys,
431
+ sort_reverse=self._sort_reverse,
363
432
  )
364
433
 
365
434
  def exclude(self, **kwargs: Any) -> DatabaseBucket[GeneralManagerType]:
@@ -405,6 +474,8 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
405
474
  self.filters,
406
475
  merged_exclude,
407
476
  search_date=search_date,
477
+ sort_keys=self._sort_keys,
478
+ sort_reverse=self._sort_reverse,
408
479
  )
409
480
 
410
481
  def first(self) -> GeneralManagerType | None:
@@ -414,6 +485,7 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
414
485
  Returns:
415
486
  GeneralManagerType | None: First manager instance if available.
416
487
  """
488
+ self._track_effective_dependencies()
417
489
  first_element = self._data.first()
418
490
  if first_element is None:
419
491
  return None
@@ -426,6 +498,7 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
426
498
  Returns:
427
499
  GeneralManagerType | None: Last manager instance if available.
428
500
  """
501
+ self._track_effective_dependencies()
429
502
  first_element = self._data.last()
430
503
  if first_element is None:
431
504
  return None
@@ -438,6 +511,7 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
438
511
  Returns:
439
512
  int: Number of queryset rows.
440
513
  """
514
+ self._track_effective_dependencies()
441
515
  return self._data.count()
442
516
 
443
517
  def all(self) -> DatabaseBucket[GeneralManagerType]:
@@ -450,7 +524,11 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
450
524
  return self.__class__(
451
525
  self._data.all(),
452
526
  self._manager_class,
527
+ self.filters,
528
+ self.excludes,
453
529
  search_date=self._search_date,
530
+ sort_keys=self._sort_keys,
531
+ sort_reverse=self._sort_reverse,
454
532
  )
455
533
 
456
534
  def get(self, **kwargs: Any) -> GeneralManagerType:
@@ -467,6 +545,7 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
467
545
  models.ObjectDoesNotExist: Propagated from the underlying queryset when no row matches.
468
546
  models.MultipleObjectsReturned: Propagated when multiple rows satisfy the lookup.
469
547
  """
548
+ self._track_effective_dependencies()
470
549
  element = self._data.get(**kwargs)
471
550
  return self._build_manager(element.pk)
472
551
 
@@ -486,8 +565,13 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
486
565
  return self.__class__(
487
566
  self._data[item],
488
567
  self._manager_class,
568
+ self.filters,
569
+ self.excludes,
489
570
  search_date=self._search_date,
571
+ sort_keys=self._sort_keys,
572
+ sort_reverse=self._sort_reverse,
490
573
  )
574
+ self._track_effective_dependencies()
491
575
  return self._build_manager(self._data[item].pk)
492
576
 
493
577
  def __len__(self) -> int:
@@ -497,6 +581,7 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
497
581
  Returns:
498
582
  int: Size of the queryset.
499
583
  """
584
+ self._track_effective_dependencies()
500
585
  return self._data.count()
501
586
 
502
587
  def __str__(self) -> str:
@@ -529,6 +614,7 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
529
614
  """
530
615
  from general_manager.manager.general_manager import GeneralManager
531
616
 
617
+ self._track_effective_dependencies()
532
618
  if isinstance(item, GeneralManager):
533
619
  return item.identification.get("id", None) in self._data.values_list(
534
620
  "pk", flat=True
@@ -610,7 +696,15 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
610
696
  except (FieldError, TypeError, ValueError) as error:
611
697
  raise QuerysetOrderingError(error) from error
612
698
 
613
- return self.__class__(qs, self._manager_class, search_date=self._search_date)
699
+ return self.__class__(
700
+ qs,
701
+ self._manager_class,
702
+ self.filters,
703
+ self.excludes,
704
+ search_date=self._search_date,
705
+ sort_keys=key,
706
+ sort_reverse=reverse,
707
+ )
614
708
 
615
709
  def none(self) -> DatabaseBucket[GeneralManagerType]:
616
710
  """
@@ -24,7 +24,7 @@ type lookup = str # e.g. "field__gt", "field__in", "field__contains", "field"
24
24
  type cache_keys = set[str] # e.g. "cache_key_1", "cache_key_2"
25
25
  type identifier = str # e.g. "{'id': 1}"", "{'project': Project(**{'id': 1})}", ...
26
26
  type dependency_index = dict[
27
- Literal["filter", "exclude", "request_query"],
27
+ Literal["filter", "exclude", "request_query", "all"],
28
28
  dict[
29
29
  general_manager_name,
30
30
  dict[attribute, dict[lookup, cache_keys]] | dict[identifier, cache_keys],
@@ -34,7 +34,9 @@ type lookup_dependency_map = dict[lookup, cache_keys]
34
34
  type manager_dependency_section = dict[attribute, lookup_dependency_map]
35
35
  type request_query_manager_section = dict[identifier, cache_keys]
36
36
 
37
- type filter_type = Literal["filter", "exclude", "identification", "request_query"]
37
+ type filter_type = Literal[
38
+ "filter", "exclude", "identification", "request_query", "all"
39
+ ]
38
40
  type Dependency = Tuple[general_manager_name, filter_type, str]
39
41
 
40
42
  logger = get_logger("cache.dependency_index")
@@ -64,6 +66,8 @@ LOCK_TIMEOUT = 5 # Lock TTL in seconds
64
66
  UNDEFINED = object() # Sentinel for undefined values
65
67
  ACTIONS: tuple[Literal["filter"], Literal["exclude"]] = ("filter", "exclude")
66
68
  REQUEST_QUERY_ACTION: Literal["request_query"] = "request_query"
69
+ ALL_RECORDS_LOOKUP = "__all__"
70
+ ALL_RECORDS_VALUE = "__all__"
67
71
 
68
72
 
69
73
  # -----------------------------------------------------------------------------
@@ -130,15 +134,31 @@ def get_full_index() -> dependency_index:
130
134
  Fetch the dependency index from cache, initialising it on first access.
131
135
 
132
136
  Returns:
133
- dependency_index: Mapping of tracked `filter`, `exclude`, and
137
+ dependency_index: Mapping of tracked `filter`, `exclude`, `all`, and
134
138
  `request_query` dependencies keyed by manager name.
135
139
  """
136
140
  cached_index = cache.get(INDEX_KEY, None)
137
141
  if cached_index is None:
138
- idx: dependency_index = {"filter": {}, "exclude": {}, "request_query": {}}
142
+ idx: dependency_index = {
143
+ "filter": {},
144
+ "exclude": {},
145
+ "request_query": {},
146
+ "all": {},
147
+ }
139
148
  cache.set(INDEX_KEY, idx, None)
140
149
  return idx
141
- return cast(dependency_index, cached_index)
150
+ idx = cast(dependency_index, cached_index)
151
+ changed = False
152
+ for key in cast(
153
+ tuple[Literal["filter", "exclude", "request_query", "all"], ...],
154
+ ("filter", "exclude", "request_query", "all"),
155
+ ):
156
+ if key not in idx:
157
+ idx[key] = {}
158
+ changed = True
159
+ if changed:
160
+ cache.set(INDEX_KEY, idx, None)
161
+ return idx
142
162
 
143
163
 
144
164
  def set_full_index(idx: dependency_index) -> None:
@@ -223,6 +243,10 @@ def record_dependencies(
223
243
  idx[action_key],
224
244
  )
225
245
  section = action_section.setdefault(model_name, {})
246
+ if not params:
247
+ lookup_map = section.setdefault(ALL_RECORDS_LOOKUP, {})
248
+ lookup_map.setdefault(ALL_RECORDS_VALUE, set()).add(cache_key)
249
+ continue
226
250
  if len(params) > 1:
227
251
  cache_dependencies = section.setdefault(
228
252
  "__cache_dependencies__", {}
@@ -243,6 +267,13 @@ def record_dependencies(
243
267
  request_section = request_index.setdefault(model_name, {})
244
268
  request_section.setdefault(identifier, set()).add(cache_key)
245
269
 
270
+ elif action == "all":
271
+ all_index = cast(
272
+ dict[str, set[str]],
273
+ idx.setdefault("all", {}),
274
+ )
275
+ all_index.setdefault(model_name, set()).add(cache_key)
276
+
246
277
  else:
247
278
  # Treat identification lookups as a simple filter on `id`
248
279
  filter_section = cast(
@@ -278,6 +309,12 @@ def remove_cache_key_from_index(cache_key: str) -> None:
278
309
  acquire_lock_with_retry("remove_cache_key_from_index")
279
310
  try:
280
311
  idx = get_full_index()
312
+ all_section = cast(dict[str, set[str]], idx.get("all", {}))
313
+ for mname, key_set in list(all_section.items()):
314
+ if cache_key in key_set:
315
+ key_set.remove(cache_key)
316
+ if not key_set:
317
+ del all_section[mname]
281
318
  for action in ACTIONS:
282
319
  action_section = cast(
283
320
  dict[general_manager_name, manager_dependency_section],
@@ -286,7 +323,7 @@ def remove_cache_key_from_index(cache_key: str) -> None:
286
323
  for mname, model_section in list(action_section.items()):
287
324
  cache_dependencies = model_section.get("__cache_dependencies__", {})
288
325
  for lookup, lookup_map in list(model_section.items()):
289
- if lookup.startswith("__"):
326
+ if lookup == "__cache_dependencies__":
290
327
  continue
291
328
  lookup_map = cast(lookup_dependency_map, lookup_map)
292
329
  for val_key, key_set in list(lookup_map.items()):
@@ -340,6 +377,12 @@ def _remove_cache_keys_from_index_locked(
340
377
  cache_keys: tuple[str, ...],
341
378
  ) -> None:
342
379
  """Remove cache keys from all dependency-index sections while the lock is held."""
380
+ all_section = cast(dict[str, set[str]], idx.get("all", {}))
381
+ for mname, key_set in list(all_section.items()):
382
+ for cache_key in cache_keys:
383
+ key_set.discard(cache_key)
384
+ if not key_set:
385
+ del all_section[mname]
343
386
  for action in ACTIONS:
344
387
  action_section = cast(
345
388
  dict[general_manager_name, manager_dependency_section],
@@ -348,7 +391,7 @@ def _remove_cache_keys_from_index_locked(
348
391
  for mname, model_section in list(action_section.items()):
349
392
  cache_dependencies = model_section.get("__cache_dependencies__", {})
350
393
  for lookup, lookup_map in list(model_section.items()):
351
- if lookup.startswith("__"):
394
+ if lookup == "__cache_dependencies__":
352
395
  continue
353
396
  lookup_map = cast(lookup_dependency_map, lookup_map)
354
397
  for val_key, key_set in list(lookup_map.items()):
@@ -453,11 +496,15 @@ def capture_old_values(
453
496
  for action in ACTIONS:
454
497
  model_section = idx[action].get(manager_name)
455
498
  if isinstance(model_section, dict):
456
- lookups |= {
457
- lookup
458
- for lookup in model_section.keys()
459
- if isinstance(lookup, str) and not lookup.startswith("__")
460
- }
499
+ for lookup in model_section.keys():
500
+ if not isinstance(lookup, str):
501
+ continue
502
+ if lookup.startswith("__sort__"):
503
+ lookups.add(lookup.removeprefix("__sort__"))
504
+ continue
505
+ if lookup.startswith("__"):
506
+ continue
507
+ lookups.add(lookup)
461
508
  elif isinstance(model_section, list):
462
509
  lookups |= set(model_section)
463
510
  if lookups and instance.identification:
@@ -504,6 +551,20 @@ def generic_cache_invalidation(
504
551
  },
505
552
  )
506
553
  idx = get_full_index()
554
+ all_cache_keys = tuple(
555
+ cast(dict[str, set[str]], idx.get("all", {})).get(manager_name, set())
556
+ )
557
+ for cache_key in all_cache_keys:
558
+ logger.info(
559
+ "invalidating cache key",
560
+ context={
561
+ "manager": manager_name,
562
+ "key": cache_key,
563
+ "action": "all",
564
+ },
565
+ )
566
+ invalidate_cache_key(cache_key)
567
+ remove_cache_key_from_index(cache_key)
507
568
 
508
569
  def _json_loads_val_key(val_key: Any) -> Any:
509
570
  if isinstance(val_key, str):
@@ -783,6 +844,74 @@ def generic_cache_invalidation(
783
844
  return True
784
845
  return False
785
846
 
847
+ def bucket_membership_matches(
848
+ params: dict[str, Any],
849
+ *,
850
+ use_old_values: bool,
851
+ ) -> bool:
852
+ """
853
+ Check whether the changed row belongs to a bucket described by filters/excludes.
854
+ """
855
+ filters = params.get("filters", {})
856
+ excludes = params.get("excludes", {})
857
+ if not isinstance(filters, dict) or not isinstance(excludes, dict):
858
+ return False
859
+
860
+ def value_for_lookup(attr_path: list[str]) -> Any:
861
+ if use_old_values:
862
+ return old_relevant_values.get("__".join(attr_path))
863
+ return current_value_for_path(attr_path)
864
+
865
+ for lookup, expected in filters.items():
866
+ parts = lookup.split("__")
867
+ if parts[-1] in (
868
+ "gt",
869
+ "gte",
870
+ "lt",
871
+ "lte",
872
+ "in",
873
+ "contains",
874
+ "startswith",
875
+ "endswith",
876
+ "regex",
877
+ ):
878
+ op = parts[-1]
879
+ attr_path = parts[:-1]
880
+ else:
881
+ op = "eq"
882
+ attr_path = parts
883
+ expected_key = json.dumps(
884
+ _normalize_dependency_identifier(expected), sort_keys=True
885
+ )
886
+ if not matches(op, value_for_lookup(attr_path), expected_key):
887
+ return False
888
+
889
+ for lookup, expected in excludes.items():
890
+ parts = lookup.split("__")
891
+ if parts[-1] in (
892
+ "gt",
893
+ "gte",
894
+ "lt",
895
+ "lte",
896
+ "in",
897
+ "contains",
898
+ "startswith",
899
+ "endswith",
900
+ "regex",
901
+ ):
902
+ op = parts[-1]
903
+ attr_path = parts[:-1]
904
+ else:
905
+ op = "eq"
906
+ attr_path = parts
907
+ expected_key = json.dumps(
908
+ _normalize_dependency_identifier(expected), sort_keys=True
909
+ )
910
+ if matches(op, value_for_lookup(attr_path), expected_key):
911
+ return False
912
+
913
+ return True
914
+
786
915
  for action in ACTIONS:
787
916
  action_section = cast(
788
917
  dict[general_manager_name, manager_dependency_section],
@@ -793,6 +922,57 @@ def generic_cache_invalidation(
793
922
  continue
794
923
  for lookup, lookup_map in model_section.items():
795
924
  if lookup.startswith("__"):
925
+ if lookup == ALL_RECORDS_LOOKUP:
926
+ for cache_keys in cast(lookup_dependency_map, lookup_map).values():
927
+ for ck in list(cache_keys):
928
+ logger.info(
929
+ "invalidating cache key",
930
+ context={
931
+ "manager": manager_name,
932
+ "key": ck,
933
+ "lookup": lookup,
934
+ "action": action,
935
+ "value": ALL_RECORDS_VALUE,
936
+ },
937
+ )
938
+ invalidate_cache_key(ck)
939
+ remove_cache_key_from_index(ck)
940
+ elif lookup.startswith("__sort__"):
941
+ sort_lookup = lookup.removeprefix("__sort__")
942
+ attr_path = sort_lookup.split("__")
943
+ old_sort_value = old_relevant_values.get(sort_lookup)
944
+ new_sort_value = current_value_for_path(attr_path)
945
+ if old_sort_value == new_sort_value:
946
+ continue
947
+ for val_key, cache_keys in list(
948
+ cast(lookup_dependency_map, lookup_map).items()
949
+ ):
950
+ payload = _json_loads_val_key(val_key)
951
+ if not isinstance(payload, dict):
952
+ continue
953
+ old_in_bucket = bucket_membership_matches(
954
+ payload,
955
+ use_old_values=True,
956
+ )
957
+ new_in_bucket = bucket_membership_matches(
958
+ payload,
959
+ use_old_values=False,
960
+ )
961
+ if not (old_in_bucket or new_in_bucket):
962
+ continue
963
+ for ck in list(cache_keys):
964
+ logger.info(
965
+ "invalidating cache key",
966
+ context={
967
+ "manager": manager_name,
968
+ "key": ck,
969
+ "lookup": lookup,
970
+ "action": action,
971
+ "value": val_key,
972
+ },
973
+ )
974
+ invalidate_cache_key(ck)
975
+ remove_cache_key_from_index(ck)
796
976
  continue
797
977
  lookup_map = cast(lookup_dependency_map, lookup_map)
798
978
  # 1) get operator and attribute path