GeneralManager 0.51.0__tar.gz → 0.52.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.
- {generalmanager-0.51.0/src/GeneralManager.egg-info → generalmanager-0.52.0}/PKG-INFO +1 -1
- {generalmanager-0.51.0 → generalmanager-0.52.0}/pyproject.toml +1 -1
- {generalmanager-0.51.0 → generalmanager-0.52.0/src/GeneralManager.egg-info}/PKG-INFO +1 -1
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/GeneralManager.egg-info/SOURCES.txt +2 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/cache/dependency_index.py +237 -220
- generalmanager-0.52.0/src/general_manager/cache/dependency_matching.py +242 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/cache/dependency_publish.py +9 -16
- generalmanager-0.52.0/src/general_manager/cache/dependency_shards.py +414 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/LICENSE +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/README.md +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/setup.cfg +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/api.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/bucket.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/cache.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/factory.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/general_manager.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/interface.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/manager.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/measurement.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/permission.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/rule.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/search.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/_types/utils.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/graphql.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/graphql_errors.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/graphql_mutations.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/graphql_prefetch.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/graphql_resolvers.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/graphql_search.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/graphql_subscriptions.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/graphql_view.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/property.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/registry.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/remote_api.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/remote_invalidation.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/api/remote_invalidation_client.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/apps.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/bootstrap.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/bucket/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/bucket/base_bucket.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/bucket/calculation_bucket.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/bucket/database_bucket.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/bucket/group_bucket.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/bucket/indexing.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/bucket/request_bucket.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/cache/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/cache/cache_decorator.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/cache/cache_tracker.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/cache/dependency_cache.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/cache/model_dependency_collector.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/cache/run_context.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/cache/signals.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/conf.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/factory/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/factory/auto_factory.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/factory/factories.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/factory/factory_methods.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/base_interface.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/bundles/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/bundles/calculation.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/bundles/database.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/bundles/remote_manager.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/bundles/request.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/base.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/builtin.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/configuration.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/core/observability.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/core/utils.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/exceptions.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/factory.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/orm/history.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/orm/support.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/registry.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/infrastructure/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/interfaces/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/interfaces/calculation.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/interfaces/database.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/interfaces/existing_model.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/interfaces/read_only.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/interfaces/request.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/manifests/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/manifests/capability_builder.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/manifests/capability_models.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/orm_interface.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/requests.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/utils/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/utils/errors.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/interface/utils/models.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/logging.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/management/commands/search_index.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/management/commands/shell.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/manager/general_manager.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/manager/group_manager.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/manager/meta.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/measurement/measurement.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/measurement/measurement_field.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/metrics/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/metrics/graphql.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/migrations/0001_initial.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/migrations/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/models.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/permission/audit.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/permission/base_permission.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/permission/graphql_capabilities.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/permission/manager_based_permission.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/permission/mutation_permission.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/permission/permission_checks.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/permission/permission_data_manager.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/permission/utils.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/public_api_registry.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/py.typed +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/async_tasks.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/backend.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/backend_registry.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/backends/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/backends/dev.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/backends/meilisearch.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/backends/opensearch.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/backends/typesense.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/config.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/indexer.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/registry.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/search/utils.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/seeding/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/seeding/manager_landscape.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/utils/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/utils/args_to_kwargs.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/utils/filter_parser.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/utils/format_string.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/utils/json_encoder.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/utils/make_cache_key.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/utils/none_to_zero.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/utils/path_mapping.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/utils/public_api.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/utils/testing.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/utils/type_checks.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/actions.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/backend_registry.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/backends/__init__.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/backends/celery.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/backends/local.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/backends/n8n.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/config.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/engine.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/event_registry.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/events.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/models.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/signal_bridge.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/tasks.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/workflow/telemetry.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/tests/test_settings.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/tests/test_urls.py +0 -0
- {generalmanager-0.51.0 → generalmanager-0.52.0}/tests/testing_asgi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GeneralManager
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.52.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.
|
|
7
|
+
version = "0.52.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.
|
|
3
|
+
Version: 0.52.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
|
|
@@ -55,7 +55,9 @@ src/general_manager/cache/cache_decorator.py
|
|
|
55
55
|
src/general_manager/cache/cache_tracker.py
|
|
56
56
|
src/general_manager/cache/dependency_cache.py
|
|
57
57
|
src/general_manager/cache/dependency_index.py
|
|
58
|
+
src/general_manager/cache/dependency_matching.py
|
|
58
59
|
src/general_manager/cache/dependency_publish.py
|
|
60
|
+
src/general_manager/cache/dependency_shards.py
|
|
59
61
|
src/general_manager/cache/model_dependency_collector.py
|
|
60
62
|
src/general_manager/cache/run_context.py
|
|
61
63
|
src/general_manager/cache/signals.py
|
{generalmanager-0.51.0 → generalmanager-0.52.0}/src/general_manager/cache/dependency_index.py
RENAMED
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
import random
|
|
7
|
-
import re
|
|
8
7
|
import time
|
|
9
8
|
from datetime import date, datetime
|
|
10
9
|
from typing import TYPE_CHECKING, Any, Iterable, Literal, Tuple, Type, cast
|
|
@@ -12,6 +11,26 @@ from typing import TYPE_CHECKING, Any, Iterable, Literal, Tuple, Type, cast
|
|
|
12
11
|
from django.core.cache import cache
|
|
13
12
|
from django.dispatch import receiver
|
|
14
13
|
|
|
14
|
+
from general_manager.cache.dependency_matching import (
|
|
15
|
+
current_value_for_path as resolve_current_value_for_path,
|
|
16
|
+
lookup_spec_from_key,
|
|
17
|
+
matches_lookup_value,
|
|
18
|
+
normalize_dependency_value,
|
|
19
|
+
parse_dependency_identifier,
|
|
20
|
+
serialize_normalized_value,
|
|
21
|
+
)
|
|
22
|
+
from general_manager.cache.dependency_shards import (
|
|
23
|
+
ReverseDependencyMembership,
|
|
24
|
+
all_records_cache_keys,
|
|
25
|
+
candidate_cache_keys_for_lookup,
|
|
26
|
+
record_cache_dependencies,
|
|
27
|
+
record_many_cache_dependencies,
|
|
28
|
+
remove_cache_key_from_shards,
|
|
29
|
+
request_query_cache_keys,
|
|
30
|
+
reverse_membership_key,
|
|
31
|
+
reverse_memberships,
|
|
32
|
+
tracked_lookup_names,
|
|
33
|
+
)
|
|
15
34
|
from general_manager.cache.signals import post_data_change, pre_data_change
|
|
16
35
|
from general_manager.logging import get_logger
|
|
17
36
|
|
|
@@ -222,7 +241,10 @@ def get_full_index() -> dependency_index:
|
|
|
222
241
|
"request_query": {},
|
|
223
242
|
"all": {},
|
|
224
243
|
}
|
|
225
|
-
|
|
244
|
+
for reverse in reverse_memberships():
|
|
245
|
+
dependencies: set[Dependency] = set(reverse.simple_dependencies)
|
|
246
|
+
dependencies.update(reverse.composite_dependencies)
|
|
247
|
+
_record_dependencies_locked(idx, reverse.cache_key, dependencies)
|
|
226
248
|
return idx
|
|
227
249
|
idx = cast(dependency_index, cached_index)
|
|
228
250
|
changed = False
|
|
@@ -253,37 +275,12 @@ def set_full_index(idx: dependency_index) -> None:
|
|
|
253
275
|
|
|
254
276
|
def _normalize_dependency_identifier(value: Any) -> Any:
|
|
255
277
|
"""Return a JSON-serializable representation for dependency tracking."""
|
|
256
|
-
|
|
257
|
-
return {
|
|
258
|
-
str(key): _normalize_dependency_identifier(val)
|
|
259
|
-
for key, val in value.items()
|
|
260
|
-
}
|
|
261
|
-
if isinstance(value, (list, tuple)):
|
|
262
|
-
return [_normalize_dependency_identifier(item) for item in value]
|
|
263
|
-
if isinstance(value, set):
|
|
264
|
-
return [
|
|
265
|
-
_normalize_dependency_identifier(item) for item in sorted(value, key=str)
|
|
266
|
-
]
|
|
267
|
-
if isinstance(value, datetime):
|
|
268
|
-
return value.isoformat()
|
|
269
|
-
if isinstance(value, date):
|
|
270
|
-
return value.isoformat()
|
|
271
|
-
if isinstance(value, (str, int, float, bool)) or value is None:
|
|
272
|
-
return value
|
|
273
|
-
return {"__repr__": repr(value)}
|
|
278
|
+
return normalize_dependency_value(value)
|
|
274
279
|
|
|
275
280
|
|
|
276
281
|
def serialize_dependency_identifier(value: Any) -> str:
|
|
277
282
|
"""Serialize dependency payloads using a deterministic literal-safe format."""
|
|
278
|
-
return
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
def parse_dependency_identifier(identifier: str) -> Any:
|
|
282
|
-
"""Parse a JSON-serialized dependency identifier, returning None on failure."""
|
|
283
|
-
try:
|
|
284
|
-
return json.loads(identifier)
|
|
285
|
-
except (json.JSONDecodeError, ValueError):
|
|
286
|
-
return None
|
|
283
|
+
return serialize_normalized_value(value)
|
|
287
284
|
|
|
288
285
|
|
|
289
286
|
# -----------------------------------------------------------------------------
|
|
@@ -364,9 +361,7 @@ def record_dependencies(
|
|
|
364
361
|
"""
|
|
365
362
|
acquire_lock_with_retry("record_dependencies")
|
|
366
363
|
try:
|
|
367
|
-
|
|
368
|
-
_record_dependencies_locked(idx, cache_key, dependencies)
|
|
369
|
-
set_full_index(idx)
|
|
364
|
+
record_cache_dependencies(cache_key, dependencies)
|
|
370
365
|
finally:
|
|
371
366
|
release_lock()
|
|
372
367
|
|
|
@@ -387,10 +382,7 @@ def record_many_dependencies(
|
|
|
387
382
|
|
|
388
383
|
acquire_lock_with_retry("record_many_dependencies")
|
|
389
384
|
try:
|
|
390
|
-
|
|
391
|
-
for cache_key, dependencies in normalized.items():
|
|
392
|
-
_record_dependencies_locked(idx, cache_key, dependencies)
|
|
393
|
-
set_full_index(idx)
|
|
385
|
+
record_many_cache_dependencies(normalized.items())
|
|
394
386
|
finally:
|
|
395
387
|
release_lock()
|
|
396
388
|
|
|
@@ -412,50 +404,12 @@ def remove_cache_key_from_index(cache_key: str) -> None:
|
|
|
412
404
|
"""
|
|
413
405
|
acquire_lock_with_retry("remove_cache_key_from_index")
|
|
414
406
|
try:
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
del all_section[mname]
|
|
422
|
-
for action in ACTIONS:
|
|
423
|
-
action_section = cast(
|
|
424
|
-
dict[general_manager_name, manager_dependency_section],
|
|
425
|
-
idx[action],
|
|
426
|
-
)
|
|
427
|
-
for mname, model_section in list(action_section.items()):
|
|
428
|
-
cache_dependencies = model_section.get("__cache_dependencies__", {})
|
|
429
|
-
for lookup, lookup_map in list(model_section.items()):
|
|
430
|
-
if lookup == "__cache_dependencies__":
|
|
431
|
-
continue
|
|
432
|
-
lookup_map = cast(lookup_dependency_map, lookup_map)
|
|
433
|
-
for val_key, key_set in list(lookup_map.items()):
|
|
434
|
-
if cache_key in key_set:
|
|
435
|
-
key_set.remove(cache_key)
|
|
436
|
-
if not key_set:
|
|
437
|
-
del lookup_map[val_key]
|
|
438
|
-
if not lookup_map:
|
|
439
|
-
del model_section[lookup]
|
|
440
|
-
if cache_dependencies:
|
|
441
|
-
cache_dependencies.pop(cache_key, None)
|
|
442
|
-
if not cache_dependencies:
|
|
443
|
-
model_section.pop("__cache_dependencies__", None)
|
|
444
|
-
if not model_section:
|
|
445
|
-
del action_section[mname]
|
|
446
|
-
request_query_section = cast(
|
|
447
|
-
dict[str, dict[str, set[str]]],
|
|
448
|
-
idx.get("request_query", {}),
|
|
449
|
-
)
|
|
450
|
-
for mname, query_section in list(request_query_section.items()):
|
|
451
|
-
for identifier, key_set in list(query_section.items()):
|
|
452
|
-
if cache_key in key_set:
|
|
453
|
-
key_set.remove(cache_key)
|
|
454
|
-
if not key_set:
|
|
455
|
-
del query_section[identifier]
|
|
456
|
-
if not query_section:
|
|
457
|
-
del request_query_section[mname]
|
|
458
|
-
set_full_index(idx)
|
|
407
|
+
if cache.get(INDEX_KEY, None) is not None and not reverse_memberships():
|
|
408
|
+
idx = get_full_index()
|
|
409
|
+
_remove_cache_keys_from_index_locked(idx, (cache_key,))
|
|
410
|
+
set_full_index(idx)
|
|
411
|
+
return
|
|
412
|
+
remove_cache_key_from_shards(cache_key)
|
|
459
413
|
finally:
|
|
460
414
|
release_lock()
|
|
461
415
|
|
|
@@ -541,11 +495,16 @@ def invalidate_and_remove_cache_keys(cache_keys: Iterable[str]) -> None:
|
|
|
541
495
|
return
|
|
542
496
|
acquire_lock_with_retry("invalidate_and_remove_cache_keys")
|
|
543
497
|
try:
|
|
544
|
-
|
|
498
|
+
if cache.get(INDEX_KEY, None) is not None and not reverse_memberships():
|
|
499
|
+
idx = get_full_index()
|
|
500
|
+
for cache_key in keys:
|
|
501
|
+
cache.delete(cache_key)
|
|
502
|
+
_remove_cache_keys_from_index_locked(idx, keys)
|
|
503
|
+
set_full_index(idx)
|
|
504
|
+
return
|
|
545
505
|
for cache_key in keys:
|
|
546
506
|
cache.delete(cache_key)
|
|
547
|
-
|
|
548
|
-
set_full_index(idx)
|
|
507
|
+
remove_cache_key_from_shards(cache_key)
|
|
549
508
|
finally:
|
|
550
509
|
release_lock()
|
|
551
510
|
|
|
@@ -578,12 +537,19 @@ def invalidate_request_query_dependencies(manager_name: str) -> tuple[str, ...]:
|
|
|
578
537
|
"""
|
|
579
538
|
acquire_lock_with_retry("invalidate_request_query_dependencies")
|
|
580
539
|
try:
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
540
|
+
if cache.get(INDEX_KEY, None) is not None and not reverse_memberships():
|
|
541
|
+
idx = get_full_index()
|
|
542
|
+
invalidated_keys = _invalidate_request_query_dependencies_locked(
|
|
543
|
+
idx,
|
|
544
|
+
manager_name,
|
|
545
|
+
)
|
|
546
|
+
if invalidated_keys:
|
|
547
|
+
set_full_index(idx)
|
|
548
|
+
return invalidated_keys
|
|
549
|
+
invalidated_keys = tuple(dict.fromkeys(request_query_cache_keys(manager_name)))
|
|
550
|
+
for cache_key in invalidated_keys:
|
|
551
|
+
cache.delete(cache_key)
|
|
552
|
+
remove_cache_key_from_shards(cache_key)
|
|
587
553
|
return invalidated_keys
|
|
588
554
|
finally:
|
|
589
555
|
release_lock()
|
|
@@ -604,23 +570,23 @@ def capture_old_values(
|
|
|
604
570
|
if instance is None:
|
|
605
571
|
return
|
|
606
572
|
manager_name = sender.__name__
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
573
|
+
lookups = tracked_lookup_names(manager_name)
|
|
574
|
+
if not lookups and not reverse_memberships():
|
|
575
|
+
idx = get_full_index()
|
|
576
|
+
for action in ACTIONS:
|
|
577
|
+
model_section = idx[action].get(manager_name)
|
|
578
|
+
if isinstance(model_section, dict):
|
|
579
|
+
for lookup in model_section.keys():
|
|
580
|
+
if not isinstance(lookup, str):
|
|
581
|
+
continue
|
|
582
|
+
if lookup.startswith("__sort__"):
|
|
583
|
+
lookups.add(lookup.removeprefix("__sort__"))
|
|
584
|
+
continue
|
|
585
|
+
if lookup.startswith("__"):
|
|
586
|
+
continue
|
|
587
|
+
lookups.add(lookup)
|
|
588
|
+
elif isinstance(model_section, list):
|
|
589
|
+
lookups |= set(model_section)
|
|
624
590
|
if lookups and instance.identification:
|
|
625
591
|
# save old values for later comparison
|
|
626
592
|
vals: dict[str, object] = {}
|
|
@@ -764,116 +730,8 @@ def _generic_cache_invalidation_locked(
|
|
|
764
730
|
return None
|
|
765
731
|
|
|
766
732
|
def matches(op: str, value: Any, val_key: Any) -> bool:
|
|
767
|
-
"""
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
Supports operators:
|
|
771
|
-
- "eq": equality; attempts to interpret `val_key` as a literal and coerce it to `value`'s type before comparing.
|
|
772
|
-
- "in": membership; expects `val_key` to be a literal iterable and checks if any coerced element equals `value`.
|
|
773
|
-
- "gt", "gte", "lt", "lte": numeric/date comparisons; `val_key` is interpreted as a literal and coerced to `value`'s type.
|
|
774
|
-
- "contains", "startswith", "endswith": string containment/prefix/suffix checks; both sides are compared as strings.
|
|
775
|
-
- "regex": treats `val_key` as a regular expression pattern and tests it against the string form of `value`.
|
|
776
|
-
|
|
777
|
-
Behavior notes:
|
|
778
|
-
- If `value` is None the function only matches explicit null equality
|
|
779
|
-
or membership checks.
|
|
780
|
-
- ``val_key`` is a JSON-serialised string produced by
|
|
781
|
-
:func:`json.dumps`. Parsing is done with :func:`json.loads`; if
|
|
782
|
-
JSON parsing or type coercion fails, the function falls back to
|
|
783
|
-
conservative comparisons or returns ``False`` where appropriate.
|
|
784
|
-
- Regex patterns that fail to compile are treated as non-matching.
|
|
785
|
-
|
|
786
|
-
Parameters:
|
|
787
|
-
op (str): The lookup operator name (one of the supported operators above).
|
|
788
|
-
value (Any): The runtime value to test.
|
|
789
|
-
val_key (Any): The stored comparison key (often a string representation) to interpret for the comparison.
|
|
790
|
-
|
|
791
|
-
Returns:
|
|
792
|
-
bool: `True` if the comparison defined by `op` and `val_key` matches `value`, `False` otherwise.
|
|
793
|
-
"""
|
|
794
|
-
# eq
|
|
795
|
-
if op == "eq":
|
|
796
|
-
literal_val = _json_loads_val_key(val_key)
|
|
797
|
-
if literal_val is None:
|
|
798
|
-
return value is None
|
|
799
|
-
repr_marker = _repr_marker(literal_val)
|
|
800
|
-
if repr_marker is not None:
|
|
801
|
-
return repr(value) == repr_marker
|
|
802
|
-
comparable = _coerce_to_type(value, literal_val)
|
|
803
|
-
if comparable is None:
|
|
804
|
-
return repr(value) == val_key
|
|
805
|
-
return value == comparable
|
|
806
|
-
|
|
807
|
-
# in
|
|
808
|
-
if op == "in":
|
|
809
|
-
try:
|
|
810
|
-
seq = json.loads(val_key)
|
|
811
|
-
except (json.JSONDecodeError, ValueError):
|
|
812
|
-
return False
|
|
813
|
-
for item in seq:
|
|
814
|
-
if item is None:
|
|
815
|
-
if value is None:
|
|
816
|
-
return True
|
|
817
|
-
continue
|
|
818
|
-
repr_marker = _repr_marker(item)
|
|
819
|
-
if repr_marker is not None:
|
|
820
|
-
if repr(value) == repr_marker:
|
|
821
|
-
return True
|
|
822
|
-
continue
|
|
823
|
-
comparable = _coerce_to_type(value, item)
|
|
824
|
-
if comparable is not None:
|
|
825
|
-
if value == comparable:
|
|
826
|
-
return True
|
|
827
|
-
elif repr(value) == repr(item):
|
|
828
|
-
return True
|
|
829
|
-
return False
|
|
830
|
-
|
|
831
|
-
# range comparisons
|
|
832
|
-
if op in ("gt", "gte", "lt", "lte"):
|
|
833
|
-
if value is None:
|
|
834
|
-
return False
|
|
835
|
-
literal_val = _json_loads_val_key(val_key)
|
|
836
|
-
thr = _coerce_to_type(value, literal_val)
|
|
837
|
-
if thr is None:
|
|
838
|
-
return False
|
|
839
|
-
if op == "gt":
|
|
840
|
-
return value > thr
|
|
841
|
-
if op == "gte":
|
|
842
|
-
return value >= thr
|
|
843
|
-
if op == "lt":
|
|
844
|
-
return value < thr
|
|
845
|
-
if op == "lte":
|
|
846
|
-
return value <= thr
|
|
847
|
-
|
|
848
|
-
# wildcard / regex comparisons
|
|
849
|
-
if op in ("contains", "startswith", "endswith", "regex"):
|
|
850
|
-
if value is None:
|
|
851
|
-
return False
|
|
852
|
-
try:
|
|
853
|
-
literal = json.loads(val_key)
|
|
854
|
-
except (json.JSONDecodeError, ValueError):
|
|
855
|
-
literal = val_key
|
|
856
|
-
|
|
857
|
-
# ensure we always work with strings to avoid TypeErrors
|
|
858
|
-
text = "" if value is None else str(value)
|
|
859
|
-
if op == "contains":
|
|
860
|
-
return literal in text
|
|
861
|
-
if op == "startswith":
|
|
862
|
-
return text.startswith(literal)
|
|
863
|
-
if op == "endswith":
|
|
864
|
-
return text.endswith(literal)
|
|
865
|
-
# regex: treat the stored key as the regex pattern
|
|
866
|
-
if op == "regex":
|
|
867
|
-
try:
|
|
868
|
-
pattern_source = (
|
|
869
|
-
literal if isinstance(literal, str) else str(literal)
|
|
870
|
-
)
|
|
871
|
-
pattern = re.compile(pattern_source)
|
|
872
|
-
except re.error:
|
|
873
|
-
return False
|
|
874
|
-
return bool(pattern.search(text))
|
|
875
|
-
|
|
876
|
-
return False
|
|
733
|
+
"""Evaluate whether a runtime value matches a stored lookup value."""
|
|
734
|
+
return matches_lookup_value(op, value, val_key)
|
|
877
735
|
|
|
878
736
|
def current_value_for_path(path: list[str]) -> Any:
|
|
879
737
|
"""
|
|
@@ -1184,6 +1042,155 @@ def _generic_cache_invalidation_locked(
|
|
|
1184
1042
|
_remove_cache_keys_from_index_locked(idx, (ck,))
|
|
1185
1043
|
|
|
1186
1044
|
|
|
1045
|
+
def _generic_cache_invalidation_from_shards(
|
|
1046
|
+
manager_name: str,
|
|
1047
|
+
instance: GeneralManager,
|
|
1048
|
+
old_relevant_values: dict[str, Any],
|
|
1049
|
+
) -> None:
|
|
1050
|
+
def value_for_lookup(lookup: str, *, use_old_values: bool) -> Any:
|
|
1051
|
+
spec = lookup_spec_from_key(lookup)
|
|
1052
|
+
lookup_name = "__".join(spec.attr_path)
|
|
1053
|
+
if use_old_values:
|
|
1054
|
+
return old_relevant_values.get(lookup_name)
|
|
1055
|
+
return resolve_current_value_for_path(instance, spec.attr_path)
|
|
1056
|
+
|
|
1057
|
+
def params_match(params: dict[str, Any], *, use_old_values: bool) -> bool:
|
|
1058
|
+
for lookup, expected in params.items():
|
|
1059
|
+
spec = lookup_spec_from_key(str(lookup))
|
|
1060
|
+
expected_key = serialize_dependency_identifier(expected)
|
|
1061
|
+
if not matches_lookup_value(
|
|
1062
|
+
spec.operator,
|
|
1063
|
+
value_for_lookup(spec.lookup, use_old_values=use_old_values),
|
|
1064
|
+
expected_key,
|
|
1065
|
+
):
|
|
1066
|
+
return False
|
|
1067
|
+
return True
|
|
1068
|
+
|
|
1069
|
+
def dependency_matches(
|
|
1070
|
+
action: str,
|
|
1071
|
+
identifier: str,
|
|
1072
|
+
*,
|
|
1073
|
+
changed_lookup: str | None,
|
|
1074
|
+
) -> bool:
|
|
1075
|
+
if action in {"all", "request_query"}:
|
|
1076
|
+
return True
|
|
1077
|
+
if action == "identification":
|
|
1078
|
+
identification = getattr(instance, "identification", None)
|
|
1079
|
+
return bool(
|
|
1080
|
+
identification
|
|
1081
|
+
and serialize_dependency_identifier(identification) == identifier
|
|
1082
|
+
)
|
|
1083
|
+
if action not in ACTIONS:
|
|
1084
|
+
return False
|
|
1085
|
+
params = parse_dependency_identifier(identifier)
|
|
1086
|
+
if not isinstance(params, dict):
|
|
1087
|
+
return False
|
|
1088
|
+
if not params:
|
|
1089
|
+
return True
|
|
1090
|
+
|
|
1091
|
+
sort_payloads = [
|
|
1092
|
+
payload
|
|
1093
|
+
for lookup, payload in params.items()
|
|
1094
|
+
if str(lookup).startswith("__sort__")
|
|
1095
|
+
and (
|
|
1096
|
+
changed_lookup is None
|
|
1097
|
+
or str(lookup).removeprefix("__sort__") == changed_lookup
|
|
1098
|
+
)
|
|
1099
|
+
]
|
|
1100
|
+
if sort_payloads:
|
|
1101
|
+
for payload in sort_payloads:
|
|
1102
|
+
if not isinstance(payload, dict):
|
|
1103
|
+
continue
|
|
1104
|
+
filters = payload.get("filters", {})
|
|
1105
|
+
excludes = payload.get("excludes", {})
|
|
1106
|
+
if not isinstance(filters, dict) or not isinstance(excludes, dict):
|
|
1107
|
+
continue
|
|
1108
|
+
old_in_bucket = params_match(filters, use_old_values=True) and not (
|
|
1109
|
+
params_match(excludes, use_old_values=True) if excludes else False
|
|
1110
|
+
)
|
|
1111
|
+
new_in_bucket = params_match(filters, use_old_values=False) and not (
|
|
1112
|
+
params_match(excludes, use_old_values=False) if excludes else False
|
|
1113
|
+
)
|
|
1114
|
+
if old_in_bucket or new_in_bucket:
|
|
1115
|
+
return True
|
|
1116
|
+
return False
|
|
1117
|
+
|
|
1118
|
+
old_match = params_match(params, use_old_values=True)
|
|
1119
|
+
new_match = params_match(params, use_old_values=False)
|
|
1120
|
+
if action == "filter":
|
|
1121
|
+
return old_match or new_match
|
|
1122
|
+
return old_match != new_match
|
|
1123
|
+
|
|
1124
|
+
def candidate_should_invalidate(
|
|
1125
|
+
cache_key: str,
|
|
1126
|
+
action: str,
|
|
1127
|
+
changed_lookup: str | None,
|
|
1128
|
+
) -> bool:
|
|
1129
|
+
reverse = cache.get(reverse_membership_key(cache_key))
|
|
1130
|
+
if not isinstance(reverse, ReverseDependencyMembership):
|
|
1131
|
+
return True
|
|
1132
|
+
for manager, dependency_action, identifier in reverse.simple_dependencies:
|
|
1133
|
+
if manager == manager_name and dependency_matches(
|
|
1134
|
+
dependency_action,
|
|
1135
|
+
identifier,
|
|
1136
|
+
changed_lookup=changed_lookup,
|
|
1137
|
+
):
|
|
1138
|
+
return True
|
|
1139
|
+
for manager, dependency_action, identifier in reverse.composite_dependencies:
|
|
1140
|
+
if (
|
|
1141
|
+
manager == manager_name
|
|
1142
|
+
and dependency_action == action
|
|
1143
|
+
and dependency_matches(
|
|
1144
|
+
dependency_action,
|
|
1145
|
+
identifier,
|
|
1146
|
+
changed_lookup=changed_lookup,
|
|
1147
|
+
)
|
|
1148
|
+
):
|
|
1149
|
+
return True
|
|
1150
|
+
return False
|
|
1151
|
+
|
|
1152
|
+
invalidation_candidates: set[str] = set()
|
|
1153
|
+
|
|
1154
|
+
invalidation_candidates.update(request_query_cache_keys(manager_name))
|
|
1155
|
+
invalidation_candidates.update(all_records_cache_keys(manager_name))
|
|
1156
|
+
identification = getattr(instance, "identification", None)
|
|
1157
|
+
if identification:
|
|
1158
|
+
invalidation_candidates.update(
|
|
1159
|
+
candidate_cache_keys_for_lookup(
|
|
1160
|
+
manager_name,
|
|
1161
|
+
"filter",
|
|
1162
|
+
"identification",
|
|
1163
|
+
old_value=serialize_dependency_identifier(identification),
|
|
1164
|
+
new_value=serialize_dependency_identifier(identification),
|
|
1165
|
+
)
|
|
1166
|
+
)
|
|
1167
|
+
|
|
1168
|
+
for lookup in tracked_lookup_names(manager_name):
|
|
1169
|
+
old_value = old_relevant_values.get(lookup)
|
|
1170
|
+
new_value = resolve_current_value_for_path(instance, tuple(lookup.split("__")))
|
|
1171
|
+
for action in ACTIONS:
|
|
1172
|
+
for cache_key in candidate_cache_keys_for_lookup(
|
|
1173
|
+
manager_name,
|
|
1174
|
+
action,
|
|
1175
|
+
lookup,
|
|
1176
|
+
old_value=old_value,
|
|
1177
|
+
new_value=new_value,
|
|
1178
|
+
):
|
|
1179
|
+
if candidate_should_invalidate(cache_key, action, lookup):
|
|
1180
|
+
invalidation_candidates.add(cache_key)
|
|
1181
|
+
|
|
1182
|
+
for cache_key in tuple(dict.fromkeys(invalidation_candidates)):
|
|
1183
|
+
logger.info(
|
|
1184
|
+
"invalidating cache key",
|
|
1185
|
+
context={
|
|
1186
|
+
"manager": manager_name,
|
|
1187
|
+
"key": cache_key,
|
|
1188
|
+
},
|
|
1189
|
+
)
|
|
1190
|
+
cache.delete(cache_key)
|
|
1191
|
+
remove_cache_key_from_shards(cache_key)
|
|
1192
|
+
|
|
1193
|
+
|
|
1187
1194
|
@receiver(post_data_change)
|
|
1188
1195
|
def generic_cache_invalidation(
|
|
1189
1196
|
sender: type[GeneralManager],
|
|
@@ -1204,13 +1211,23 @@ def generic_cache_invalidation(
|
|
|
1204
1211
|
manager_name = sender.__name__
|
|
1205
1212
|
acquire_lock_with_retry("generic_cache_invalidation")
|
|
1206
1213
|
try:
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1214
|
+
if not reverse_memberships():
|
|
1215
|
+
idx = get_full_index()
|
|
1216
|
+
sections: tuple[Literal["filter", "exclude", "all", "request_query"], ...]
|
|
1217
|
+
sections = ("filter", "exclude", "all", "request_query")
|
|
1218
|
+
if any(idx.get(section) for section in sections):
|
|
1219
|
+
_generic_cache_invalidation_locked(
|
|
1220
|
+
idx,
|
|
1221
|
+
manager_name,
|
|
1222
|
+
instance,
|
|
1223
|
+
old_relevant_values,
|
|
1224
|
+
)
|
|
1225
|
+
set_full_index(idx)
|
|
1226
|
+
return
|
|
1227
|
+
_generic_cache_invalidation_from_shards(
|
|
1210
1228
|
manager_name,
|
|
1211
1229
|
instance,
|
|
1212
1230
|
old_relevant_values,
|
|
1213
1231
|
)
|
|
1214
|
-
set_full_index(idx)
|
|
1215
1232
|
finally:
|
|
1216
1233
|
release_lock()
|