GeneralManager 0.86.6__tar.gz → 0.86.8__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.86.6/src/GeneralManager.egg-info → generalmanager-0.86.8}/PKG-INFO +1 -1
- {generalmanager-0.86.6 → generalmanager-0.86.8}/pyproject.toml +1 -1
- {generalmanager-0.86.6 → generalmanager-0.86.8/src/GeneralManager.egg-info}/PKG-INFO +1 -1
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/remote_invalidation.py +210 -103
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm/support.py +43 -51
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/interfaces/existing_model.py +2 -2
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/orm_interface.py +3 -3
- {generalmanager-0.86.6 → generalmanager-0.86.8}/LICENSE +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/README.md +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/setup.cfg +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/api.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/bucket.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/cache.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/dataframes.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/factory.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/general_manager.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/interface.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/manager.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/measurement.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/permission.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/rule.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/search.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/_types/utils.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_as_of.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_errors.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_groups.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_identifiers.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_mutations.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_ordering.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_output.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_prefetch.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_relations.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_resolvers.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_search.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_subscriptions.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_type.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_view.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_warmup.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_warmup_registry.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/mutation_controls.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/notification_batching.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/property.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/registry.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/remote_api.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/remote_invalidation_client.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/apps.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/as_of.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/bootstrap.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/bucket/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/bucket/_materialized_bucket.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/bucket/_ordering.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/bucket/base_bucket.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/bucket/calculation_bucket.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/bucket/database_bucket.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/bucket/excel_bucket.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/bucket/group_bucket.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/bucket/indexing.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/bucket/projection.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/bucket/request_bucket.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/_dependency_graph.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/batch_refresh.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/cache_decorator.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/cache_tracker.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/data_change_context.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/dependency_cache.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/dependency_index.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/dependency_matching.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/dependency_publish.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/dependency_shards.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/model_dependency_collector.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/run_context.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/run_context_lru.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/signals.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/cache/transaction_barrier.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/audit.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/bootstrap.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/checks.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/consumer.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/context.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/errors.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/__main__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/baseline.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/datasets/basic_queries.yaml +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/datasets/demo_readiness.yaml +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/datasets/edge_cases.yaml +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/datasets/follow_ups.yaml +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/datasets/large_schema.yaml +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/datasets/multi_hop.yaml +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/datasets/planned_orchestration.yaml +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/diagnostics.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/fingerprints.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/fixtures.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/judges/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/judges/contract.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/readiness.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/runner.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/evals/traces.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/grounding.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/models.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/budget.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/calculations.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/catalog.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/config.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/events.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/evidence.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/models.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/planner.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/provider_calls.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/resolver.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/routing.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/scheduler.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/synthesis.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/planned/validation.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/providers/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/providers/_shared.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/providers/anthropic.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/providers/base.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/providers/google.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/providers/ollama.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/providers/openai.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/rate_limits.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/routing.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/schema_index.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/settings.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/signals.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/system_prompt.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/tool_metadata.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/tools.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/turns.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/chat/views.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/conf.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/dataframes/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/dataframes/measurements.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/factory/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/factory/auto_factory.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/factory/factories.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/factory/factory_methods.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/base_interface.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/bundles/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/bundles/calculation.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/bundles/database.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/bundles/excel.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/bundles/remote_manager.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/bundles/request.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/base.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/builtin.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/calculation/input_resolution.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/configuration.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/core/observability.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/core/utils.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/excel/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/exceptions.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/factory.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm/bulk.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm/create_many.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm/history.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/orm_utils/update_state.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/registry.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/excel.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/excel_store.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/excel_workbook.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/infrastructure/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/interfaces/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/interfaces/calculation.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/interfaces/database.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/interfaces/excel.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/interfaces/read_only.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/interfaces/request.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/manifests/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/manifests/capability_builder.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/manifests/capability_models.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/requests.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/utils/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/utils/errors.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/utils/history.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/utils/models.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/logging.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/management/commands/chat_cleanup.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/management/commands/cleanup_upload_intents.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/management/commands/graphql_warmup.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/management/commands/search_index.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/management/commands/search_reconcile.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/management/commands/shell.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/management/commands/sync_read_only.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/manager/bulk_create.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/manager/general_manager.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/manager/group_manager.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/manager/meta.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/measurement/measurement.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/measurement/measurement_field.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/metrics/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/metrics/graphql.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0001_initial.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0004_search_index_state.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0007_upload_intent.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0008_upload_finalization_versions.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0009_upload_cleanup_state.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0010_chat_pending_confirmation_portable_uniqueness.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0011_search_index_state_dirty_generation.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/0012_chat_context_watermarks.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/migrations/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/models.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/permission/audit.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/permission/base_permission.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/permission/graphql_capabilities.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/permission/manager_based_permission.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/permission/mutation_permission.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/permission/permission_checks.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/permission/permission_data_manager.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/permission/utils.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/public_api_registry.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/py.typed +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/async_tasks.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/backend.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/backend_registry.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/backends/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/backends/dev.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/backends/meilisearch.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/backends/opensearch.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/backends/typesense.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/checks.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/config.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/identity.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/indexer.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/invalidation.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/m2m_invalidation.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/models.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/reconciliation.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/registry.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/tasks.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/search/utils.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/seeding/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/seeding/manager_landscape.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/adapters.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/checks.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/config.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/errors.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/finalization.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/graphql.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/graphql_types.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/metrics.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/models.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/public.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/s3.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/services.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/tokens.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/types.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/urls.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/uploads/views.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/utils/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/utils/_args_to_kwargs.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/utils/_cache_key_encoder.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/utils/_make_cache_key.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/utils/_none_to_zero.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/utils/filter_parser.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/utils/format_string.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/utils/json_encoder.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/utils/path_mapping.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/utils/public_api.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/utils/testing.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/utils/type_checks.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/actions.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/backend_registry.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/backends/__init__.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/backends/celery.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/backends/local.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/backends/n8n.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/config.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/engine.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/event_registry.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/events.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/models.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/signal_bridge.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/tasks.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/workflow/telemetry.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/tests/test_settings.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/tests/test_urls.py +0 -0
- {generalmanager-0.86.6 → generalmanager-0.86.8}/tests/testing_asgi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GeneralManager
|
|
3
|
-
Version: 0.86.
|
|
3
|
+
Version: 0.86.8
|
|
4
4
|
Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
|
|
5
5
|
Author-email: Tim Kleindick <tkleindick@yahoo.de>
|
|
6
6
|
License: MIT License
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "GeneralManager"
|
|
7
|
-
version = "0.86.
|
|
7
|
+
version = "0.86.8"
|
|
8
8
|
description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GeneralManager
|
|
3
|
-
Version: 0.86.
|
|
3
|
+
Version: 0.86.8
|
|
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
|
{generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/api/remote_invalidation.py
RENAMED
|
@@ -5,18 +5,21 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
from copy import deepcopy
|
|
7
7
|
from datetime import date, datetime
|
|
8
|
+
from dataclasses import replace
|
|
8
9
|
import json
|
|
9
10
|
from importlib import import_module
|
|
10
11
|
import re
|
|
11
12
|
from time import perf_counter
|
|
12
13
|
from uuid import UUID, uuid4
|
|
13
14
|
from collections.abc import Callable, Coroutine, Mapping, MutableSequence, Sequence
|
|
14
|
-
from types import ModuleType
|
|
15
|
+
from types import ModuleType, SimpleNamespace
|
|
15
16
|
from typing import TYPE_CHECKING, Protocol, TypeAlias, cast
|
|
16
17
|
from urllib.parse import parse_qs
|
|
17
18
|
|
|
18
19
|
from asgiref.sync import async_to_sync
|
|
20
|
+
from django.contrib.auth import get_user_model
|
|
19
21
|
from django.conf import settings
|
|
22
|
+
from django.core.handlers.asgi import ASGIHandler
|
|
20
23
|
from django.db import DEFAULT_DB_ALIAS, transaction
|
|
21
24
|
from django.urls import re_path
|
|
22
25
|
|
|
@@ -241,6 +244,85 @@ def emit_remote_invalidation(
|
|
|
241
244
|
post_data_change.connect(emit_remote_invalidation, weak=False)
|
|
242
245
|
|
|
243
246
|
|
|
247
|
+
def _fresh_remote_principal(principal: object) -> object | None:
|
|
248
|
+
"""Reload the authenticated account, without reusing cached permission state."""
|
|
249
|
+
if not getattr(principal, "is_authenticated", False):
|
|
250
|
+
return None
|
|
251
|
+
identity = getattr(principal, "pk", None)
|
|
252
|
+
if identity is None:
|
|
253
|
+
return None
|
|
254
|
+
user = get_user_model()._default_manager.filter(pk=identity).first()
|
|
255
|
+
if user is None or not getattr(user, "is_active", True):
|
|
256
|
+
return None
|
|
257
|
+
return user
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _can_receive_remote_event(
|
|
261
|
+
config: RemoteAPIConfig, principal: object, event: Mapping[str, object]
|
|
262
|
+
) -> bool:
|
|
263
|
+
"""Apply the native current-instance read gate; never disclose deleted IDs."""
|
|
264
|
+
if (
|
|
265
|
+
event.get("action") not in {"create", "update"}
|
|
266
|
+
or event.get("protocol_version") != config.protocol_version
|
|
267
|
+
or event.get("base_path") != config.base_path
|
|
268
|
+
or event.get("resource_name") != config.resource_name
|
|
269
|
+
):
|
|
270
|
+
return False
|
|
271
|
+
identification = event.get("identification")
|
|
272
|
+
if not isinstance(identification, Mapping) or not identification:
|
|
273
|
+
return False
|
|
274
|
+
from general_manager.interface.interfaces.request import RequestInterface
|
|
275
|
+
from general_manager.api.graphql_errors import get_read_permission_filter
|
|
276
|
+
from general_manager.api.graphql_resolvers import (
|
|
277
|
+
prepare_read_authorization,
|
|
278
|
+
finalize_read_authorization,
|
|
279
|
+
)
|
|
280
|
+
from general_manager.bucket._materialized_bucket import MaterializedBucket
|
|
281
|
+
from general_manager.bucket.base_bucket import Bucket
|
|
282
|
+
|
|
283
|
+
try:
|
|
284
|
+
user = _fresh_remote_principal(principal)
|
|
285
|
+
if user is None:
|
|
286
|
+
return False
|
|
287
|
+
instance = config.manager_cls(**dict(identification))
|
|
288
|
+
if isinstance(instance._interface, RequestInterface):
|
|
289
|
+
# Request constructors are lazy. Even a user-only read rule must
|
|
290
|
+
# not authorize an ID whose current detail cannot be loaded.
|
|
291
|
+
instance._interface.set_request_payload_cache(None)
|
|
292
|
+
instance._interface.get_data()
|
|
293
|
+
info = SimpleNamespace(context=SimpleNamespace(user=user))
|
|
294
|
+
plan = get_read_permission_filter(config.manager_cls, info)
|
|
295
|
+
source: Bucket[GeneralManager] = MaterializedBucket(
|
|
296
|
+
config.manager_cls, [instance]
|
|
297
|
+
)
|
|
298
|
+
if plan.decision == "conditional" and any(
|
|
299
|
+
constraint.get("filter") or constraint.get("exclude")
|
|
300
|
+
for constraint in plan.filters
|
|
301
|
+
):
|
|
302
|
+
# Keep native ORM filter/exclude semantics for this one candidate.
|
|
303
|
+
source = config.manager_cls.all().with_instances([instance])
|
|
304
|
+
preparation = prepare_read_authorization(
|
|
305
|
+
source,
|
|
306
|
+
config.manager_cls,
|
|
307
|
+
info,
|
|
308
|
+
permission_plan=replace(plan, requires_instance_check=False),
|
|
309
|
+
)
|
|
310
|
+
authorized = finalize_read_authorization(
|
|
311
|
+
preparation, config.manager_cls, info, source="remote_invalidation"
|
|
312
|
+
).queryset
|
|
313
|
+
# Run the native instance gate unconditionally, even for allow-all plans.
|
|
314
|
+
return any(
|
|
315
|
+
item.Permission(item, user).can_read_instance() for item in authorized
|
|
316
|
+
)
|
|
317
|
+
except MemoryError:
|
|
318
|
+
raise
|
|
319
|
+
except Exception: # noqa: BLE001
|
|
320
|
+
# Missing instances and failed authorization lookups have the same
|
|
321
|
+
# externally silent result. Never send a fallback action or identity.
|
|
322
|
+
logger.warning("remote invalidation authorization failed")
|
|
323
|
+
return False
|
|
324
|
+
|
|
325
|
+
|
|
244
326
|
class RemoteInvalidationConsumer:
|
|
245
327
|
"""Minimal ASGI websocket consumer for remote invalidation events."""
|
|
246
328
|
|
|
@@ -269,28 +351,15 @@ class RemoteInvalidationConsumer:
|
|
|
269
351
|
receive: AsgiReceive,
|
|
270
352
|
send: AsgiSend,
|
|
271
353
|
) -> None:
|
|
354
|
+
"""Forward identified changes only under fresh native instance read rights.
|
|
355
|
+
|
|
356
|
+
Admission requires an active authenticated Django account (4401).
|
|
357
|
+
Unknown resources close with 4404, incompatible versions with 4406.
|
|
358
|
+
Deleted, unavailable and identity-free batch events are suppressed.
|
|
359
|
+
Existing client data is not evicted when access is lost.
|
|
272
360
|
"""
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
The consumer closes with 4404 for unknown or disabled resources, 4406
|
|
276
|
-
for protocol-version mismatches, and 1011 when no channel layer is
|
|
277
|
-
available. It accepts matching connections, subscribes to the resource
|
|
278
|
-
group, sends JSON invalidation payloads, and discards the channel in a
|
|
279
|
-
`finally` block. The generated route supplies `scope["url_route"]
|
|
280
|
-
["kwargs"]["base_path"]` and `["resource_name"]`; the optional
|
|
281
|
-
`version` query string parameter is compared with the resource protocol
|
|
282
|
-
version. A missing version is accepted, multiple values use the first
|
|
283
|
-
parsed value, and invalid UTF-8 query strings propagate decoding errors.
|
|
284
|
-
Non-disconnect inbound websocket messages are ignored while the consumer
|
|
285
|
-
keeps listening.
|
|
286
|
-
|
|
287
|
-
Outbound JSON messages include `protocol_version`, `base_path`,
|
|
288
|
-
`resource_name`, `action`, `identification`, and `event_id` in a
|
|
289
|
-
`websocket.send` frame with a JSON `text` payload. Extra channel-layer
|
|
290
|
-
event fields are ignored; missing required event fields raise `KeyError`.
|
|
291
|
-
Client disconnect exits the loop and cleans up without raising. Missing
|
|
292
|
-
route kwargs, channel-layer, receive, send, and JSON errors propagate.
|
|
293
|
-
"""
|
|
361
|
+
from channels.db import database_sync_to_async
|
|
362
|
+
|
|
294
363
|
from general_manager.manager.meta import GeneralManagerMeta
|
|
295
364
|
|
|
296
365
|
url_route = scope.get("url_route", {})
|
|
@@ -314,6 +383,12 @@ class RemoteInvalidationConsumer:
|
|
|
314
383
|
if protocol_version and protocol_version != config.protocol_version:
|
|
315
384
|
await send({"type": "websocket.close", "code": 4406})
|
|
316
385
|
return
|
|
386
|
+
principal = await database_sync_to_async(_fresh_remote_principal)(
|
|
387
|
+
scope.get("user")
|
|
388
|
+
)
|
|
389
|
+
if principal is None:
|
|
390
|
+
await send({"type": "websocket.close", "code": 4401})
|
|
391
|
+
return
|
|
317
392
|
channel_layer = _get_channel_layer_safe()
|
|
318
393
|
if channel_layer is None:
|
|
319
394
|
await send({"type": "websocket.close", "code": 1011})
|
|
@@ -321,13 +396,13 @@ class RemoteInvalidationConsumer:
|
|
|
321
396
|
self._channel_layer = channel_layer
|
|
322
397
|
self._group_name = remote_invalidation_group_name(config)
|
|
323
398
|
self._channel_name = await channel_layer.new_channel()
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
receive_task: asyncio.Task[AsgiMessage] = asyncio.create_task(receive())
|
|
327
|
-
event_task: asyncio.Task[object] = asyncio.create_task(
|
|
328
|
-
channel_layer.receive(self._channel_name)
|
|
329
|
-
)
|
|
399
|
+
receive_task: asyncio.Task[AsgiMessage] | None = None
|
|
400
|
+
event_task: asyncio.Task[object] | None = None
|
|
330
401
|
try:
|
|
402
|
+
await channel_layer.group_add(self._group_name, self._channel_name)
|
|
403
|
+
await send({"type": "websocket.accept"})
|
|
404
|
+
receive_task = asyncio.create_task(receive())
|
|
405
|
+
event_task = asyncio.create_task(channel_layer.receive(self._channel_name))
|
|
331
406
|
while True:
|
|
332
407
|
done, _ = await asyncio.wait(
|
|
333
408
|
{receive_task, event_task},
|
|
@@ -347,6 +422,9 @@ class RemoteInvalidationConsumer:
|
|
|
347
422
|
not disconnect
|
|
348
423
|
and isinstance(event, Mapping)
|
|
349
424
|
and event.get("type") == "gm.remote.invalidation"
|
|
425
|
+
and await database_sync_to_async(_can_receive_remote_event)(
|
|
426
|
+
config, principal, event
|
|
427
|
+
)
|
|
350
428
|
):
|
|
351
429
|
await send(
|
|
352
430
|
{
|
|
@@ -372,9 +450,12 @@ class RemoteInvalidationConsumer:
|
|
|
372
450
|
await asyncio.gather(event_task, return_exceptions=True)
|
|
373
451
|
break
|
|
374
452
|
finally:
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
453
|
+
pending_tasks = [
|
|
454
|
+
task for task in (receive_task, event_task) if task is not None
|
|
455
|
+
]
|
|
456
|
+
for task in pending_tasks:
|
|
457
|
+
task.cancel()
|
|
458
|
+
await asyncio.gather(*pending_tasks, return_exceptions=True)
|
|
378
459
|
if self._group_name is not None and self._channel_name is not None:
|
|
379
460
|
await channel_layer.group_discard(self._group_name, self._channel_name)
|
|
380
461
|
|
|
@@ -396,11 +477,19 @@ def ensure_remote_invalidation_route(
|
|
|
396
477
|
trailing slash. The helper is idempotent for the same `(base_path,
|
|
397
478
|
resource_name)` pair using the resolved config values without additional
|
|
398
479
|
normalization; changed config for an existing key is preserved unchanged.
|
|
399
|
-
|
|
480
|
+
Existing middleware wrappers and the live URLRouter are preserved; opaque
|
|
481
|
+
websocket applications are left unchanged with a warning. Routes append
|
|
482
|
+
after existing websocket patterns. Missing or non-mutable
|
|
400
483
|
`websocket_urlpatterns` are replaced with a new list. The helper supports
|
|
401
484
|
ASGI modules that expose either a Channels `application_mapping` router or a
|
|
402
|
-
|
|
485
|
+
unwrapped Django ASGIHandler attribute named by `ASGI_APPLICATION`. Unknown
|
|
486
|
+
root callables (including closures and custom HTTP wrappers) are left unchanged
|
|
487
|
+
because treating them as HTTP-only could bypass their websocket security.
|
|
403
488
|
"""
|
|
489
|
+
registry = build_remote_api_registry(manager_classes)
|
|
490
|
+
enabled = [config for config in registry.values() if config.websocket_invalidation]
|
|
491
|
+
if not enabled:
|
|
492
|
+
return
|
|
404
493
|
asgi_path = getattr(settings, "ASGI_APPLICATION", None)
|
|
405
494
|
if not asgi_path:
|
|
406
495
|
return
|
|
@@ -419,26 +508,34 @@ def ensure_remote_invalidation_route(
|
|
|
419
508
|
if "populate() isn't reentrant" in str(exc):
|
|
420
509
|
return
|
|
421
510
|
raise
|
|
422
|
-
websocket_patterns = _websocket_patterns(asgi_module)
|
|
423
|
-
if websocket_patterns is None:
|
|
424
|
-
websocket_patterns = []
|
|
425
|
-
cast(_AsgiModuleRoutes, asgi_module).websocket_urlpatterns = websocket_patterns
|
|
426
511
|
application = getattr(asgi_module, attr_name, None)
|
|
427
512
|
if application is None:
|
|
428
513
|
return
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
514
|
+
mapped_application = _protocol_application(application)
|
|
515
|
+
websocket_app = (
|
|
516
|
+
mapped_application.application_mapping.get("websocket")
|
|
517
|
+
if mapped_application is not None
|
|
518
|
+
else None
|
|
519
|
+
)
|
|
520
|
+
router = _websocket_router(websocket_app)
|
|
521
|
+
if (websocket_app is not None and router is None) or (
|
|
522
|
+
mapped_application is None and type(application) is not ASGIHandler
|
|
523
|
+
):
|
|
524
|
+
logger.warning("remote invalidation routes require an accessible URLRouter")
|
|
525
|
+
return
|
|
526
|
+
websocket_patterns = _websocket_patterns(asgi_module)
|
|
527
|
+
patterns = list(websocket_patterns or [])
|
|
528
|
+
if router is not None:
|
|
529
|
+
for route in router.routes:
|
|
530
|
+
if route not in patterns:
|
|
531
|
+
patterns.append(route)
|
|
532
|
+
for config in enabled:
|
|
436
533
|
normalized = f"{config.base_path.strip('/')}/ws/{config.resource_name}"
|
|
437
534
|
pattern = rf"^{re.escape(normalized)}/?$"
|
|
438
535
|
route_exists = any(
|
|
439
536
|
getattr(route, "_general_manager_remote_ws_key", None)
|
|
440
537
|
== (config.base_path, config.resource_name)
|
|
441
|
-
for route in
|
|
538
|
+
for route in patterns
|
|
442
539
|
)
|
|
443
540
|
if route_exists:
|
|
444
541
|
continue
|
|
@@ -459,11 +556,16 @@ def ensure_remote_invalidation_route(
|
|
|
459
556
|
config.base_path,
|
|
460
557
|
config.resource_name,
|
|
461
558
|
)
|
|
462
|
-
|
|
463
|
-
if
|
|
464
|
-
|
|
559
|
+
patterns.append(websocket_route)
|
|
560
|
+
if websocket_patterns is None:
|
|
561
|
+
cast(_AsgiModuleRoutes, asgi_module).websocket_urlpatterns = patterns
|
|
562
|
+
else:
|
|
563
|
+
websocket_patterns[:] = patterns
|
|
564
|
+
if router is not None:
|
|
565
|
+
router.routes = list(patterns)
|
|
566
|
+
elif mapped_application is not None:
|
|
465
567
|
mapped_application.application_mapping["websocket"] = AuthMiddlewareStack(
|
|
466
|
-
URLRouter(list(
|
|
568
|
+
URLRouter(list(patterns))
|
|
467
569
|
)
|
|
468
570
|
else:
|
|
469
571
|
setattr(
|
|
@@ -472,9 +574,7 @@ def ensure_remote_invalidation_route(
|
|
|
472
574
|
ProtocolTypeRouter(
|
|
473
575
|
{
|
|
474
576
|
"http": application,
|
|
475
|
-
"websocket": AuthMiddlewareStack(
|
|
476
|
-
URLRouter(list(websocket_patterns))
|
|
477
|
-
),
|
|
577
|
+
"websocket": AuthMiddlewareStack(URLRouter(list(patterns))),
|
|
478
578
|
}
|
|
479
579
|
),
|
|
480
580
|
)
|
|
@@ -491,8 +591,8 @@ def clear_remote_invalidation_routes() -> None:
|
|
|
491
591
|
routes in the configured ASGI module are removed regardless of path or
|
|
492
592
|
resource shape while preserving the order of remaining routes. Returns
|
|
493
593
|
without changes when ASGI settings, Channels, the ASGI module, or mutable
|
|
494
|
-
sequence websocket patterns are unavailable.
|
|
495
|
-
|
|
594
|
+
sequence websocket patterns are unavailable. The live URLRouter is updated in place through existing middleware wrappers.
|
|
595
|
+
A plain HTTP application is never wrapped merely to clear routes.
|
|
496
596
|
"""
|
|
497
597
|
asgi_path = getattr(settings, "ASGI_APPLICATION", None)
|
|
498
598
|
if not asgi_path:
|
|
@@ -501,48 +601,31 @@ def clear_remote_invalidation_routes() -> None:
|
|
|
501
601
|
module_path, attr_name = asgi_path.rsplit(".", 1)
|
|
502
602
|
except ValueError:
|
|
503
603
|
return
|
|
504
|
-
try:
|
|
505
|
-
from channels.auth import AuthMiddlewareStack
|
|
506
|
-
from channels.routing import ProtocolTypeRouter, URLRouter
|
|
507
|
-
except ImportError: # pragma: no cover - optional dependency
|
|
508
|
-
return
|
|
509
604
|
try:
|
|
510
605
|
asgi_module = import_module(module_path)
|
|
511
606
|
except ImportError:
|
|
512
607
|
return
|
|
513
608
|
websocket_patterns = _websocket_patterns(asgi_module)
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
609
|
+
application = getattr(asgi_module, attr_name, None)
|
|
610
|
+
mapped_application = _protocol_application(application)
|
|
611
|
+
router = _websocket_router(
|
|
612
|
+
mapped_application.application_mapping.get("websocket")
|
|
613
|
+
if mapped_application is not None
|
|
614
|
+
else None
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
def retained(route: object) -> bool:
|
|
618
|
+
return not (
|
|
520
619
|
getattr(route, "_general_manager_remote_ws", False)
|
|
521
620
|
or getattr(route, "_general_manager_remote_ws_key", None) is not None
|
|
522
621
|
)
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if
|
|
529
|
-
|
|
530
|
-
mapped_application.application_mapping["websocket"] = AuthMiddlewareStack(
|
|
531
|
-
URLRouter(list(filtered_patterns))
|
|
532
|
-
)
|
|
533
|
-
else:
|
|
534
|
-
setattr(
|
|
535
|
-
asgi_module,
|
|
536
|
-
attr_name,
|
|
537
|
-
ProtocolTypeRouter(
|
|
538
|
-
{
|
|
539
|
-
"http": application,
|
|
540
|
-
"websocket": AuthMiddlewareStack(
|
|
541
|
-
URLRouter(list(filtered_patterns))
|
|
542
|
-
),
|
|
543
|
-
}
|
|
544
|
-
),
|
|
545
|
-
)
|
|
622
|
+
|
|
623
|
+
if websocket_patterns is not None:
|
|
624
|
+
websocket_patterns[:] = [
|
|
625
|
+
route for route in websocket_patterns if retained(route)
|
|
626
|
+
]
|
|
627
|
+
if router is not None:
|
|
628
|
+
router.routes = [route for route in router.routes if retained(route)]
|
|
546
629
|
|
|
547
630
|
|
|
548
631
|
def _websocket_patterns(
|
|
@@ -557,19 +640,43 @@ def _websocket_patterns(
|
|
|
557
640
|
return value
|
|
558
641
|
|
|
559
642
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
)
|
|
567
|
-
|
|
643
|
+
class _RouteCollection(Protocol):
|
|
644
|
+
routes: Sequence[object]
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def _protocol_application(application: object) -> _ApplicationMapping | None:
|
|
648
|
+
"""Find the protocol router without discarding wrappers around it."""
|
|
649
|
+
seen: set[int] = set()
|
|
650
|
+
while application is not None and id(application) not in seen:
|
|
651
|
+
seen.add(id(application))
|
|
652
|
+
if isinstance(getattr(application, "application_mapping", None), dict):
|
|
653
|
+
return cast(_ApplicationMapping, application)
|
|
654
|
+
application = _middleware_inner(application)
|
|
655
|
+
return None
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def _websocket_router(application: object) -> _RouteCollection | None:
|
|
659
|
+
"""Locate an actual URLRouter through the existing middleware chain."""
|
|
660
|
+
try:
|
|
661
|
+
from channels.routing import URLRouter
|
|
662
|
+
except ImportError: # pragma: no cover - optional dependency
|
|
663
|
+
return None
|
|
664
|
+
|
|
568
665
|
seen: set[int] = set()
|
|
569
|
-
while
|
|
570
|
-
seen.add(id(
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
666
|
+
while application is not None and id(application) not in seen:
|
|
667
|
+
seen.add(id(application))
|
|
668
|
+
if isinstance(application, URLRouter):
|
|
669
|
+
return cast(_RouteCollection, application)
|
|
670
|
+
application = _middleware_inner(application)
|
|
671
|
+
return None
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def _middleware_inner(application: object) -> object:
|
|
675
|
+
"""Traverse standard inner links and Channels' known origin wrapper."""
|
|
676
|
+
try:
|
|
677
|
+
from channels.security.websocket import OriginValidator
|
|
678
|
+
except ImportError: # pragma: no cover - optional dependency
|
|
679
|
+
return getattr(application, "inner", None)
|
|
680
|
+
if isinstance(application, OriginValidator):
|
|
681
|
+
return application.application
|
|
682
|
+
return getattr(application, "inner", None)
|
|
@@ -4,14 +4,13 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
6
|
from collections.abc import Hashable
|
|
7
|
-
from datetime import date, datetime
|
|
7
|
+
from datetime import date, datetime
|
|
8
8
|
from functools import lru_cache
|
|
9
9
|
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Type, cast
|
|
10
10
|
|
|
11
11
|
from django.core.exceptions import FieldDoesNotExist
|
|
12
12
|
from django.db import DEFAULT_DB_ALIAS, connections, models
|
|
13
13
|
from django.db.models import Subquery
|
|
14
|
-
from django.utils import timezone
|
|
15
14
|
from general_manager.as_of import (
|
|
16
15
|
HistoricalReadNotSupportedError,
|
|
17
16
|
InvalidSearchDateError,
|
|
@@ -347,54 +346,51 @@ class OrmReadCapability(BaseCapability):
|
|
|
347
346
|
instance = None
|
|
348
347
|
missing_error = error
|
|
349
348
|
if search_date is not None:
|
|
350
|
-
|
|
351
|
-
seconds=interface_cls.historical_lookup_buffer_seconds
|
|
352
|
-
):
|
|
353
|
-
from .history import HistoryNotSupportedError
|
|
349
|
+
from .history import HistoryNotSupportedError
|
|
354
350
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
351
|
+
if current_as_of_date() is not None and not hasattr(
|
|
352
|
+
model_cls, "history"
|
|
353
|
+
):
|
|
354
|
+
history_unavailable = HistoryNotSupportedError(
|
|
355
|
+
interface_cls.__name__
|
|
356
|
+
)
|
|
357
|
+
raise HistoricalReadNotSupportedError(
|
|
358
|
+
interface_cls.__name__
|
|
359
|
+
) from history_unavailable
|
|
360
|
+
historical: models.Model | None
|
|
361
|
+
try:
|
|
362
|
+
history_handler = _history_capability_for(interface_cls)
|
|
363
|
+
except (NotImplementedError, TypeError) as error:
|
|
364
|
+
if current_as_of_date() is not None:
|
|
365
|
+
raise HistoricalReadNotSupportedError(
|
|
359
366
|
interface_cls.__name__
|
|
367
|
+
) from error
|
|
368
|
+
history_error = HistoryNotSupportedError(interface_cls.__name__)
|
|
369
|
+
raise history_error from error
|
|
370
|
+
try:
|
|
371
|
+
if instance is not None:
|
|
372
|
+
historical = history_handler.get_historical_record(
|
|
373
|
+
interface_cls,
|
|
374
|
+
instance,
|
|
375
|
+
search_date,
|
|
360
376
|
)
|
|
377
|
+
else:
|
|
378
|
+
historical = history_handler.get_historical_record_by_pk(
|
|
379
|
+
interface_cls,
|
|
380
|
+
pk,
|
|
381
|
+
search_date,
|
|
382
|
+
)
|
|
383
|
+
except HistoryNotSupportedError as error:
|
|
384
|
+
if current_as_of_date() is not None:
|
|
361
385
|
raise HistoricalReadNotSupportedError(
|
|
362
386
|
interface_cls.__name__
|
|
363
|
-
) from
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
interface_cls.__name__
|
|
371
|
-
) from error
|
|
372
|
-
history_error = HistoryNotSupportedError(interface_cls.__name__)
|
|
373
|
-
raise history_error from error
|
|
374
|
-
try:
|
|
375
|
-
if instance is not None:
|
|
376
|
-
historical = history_handler.get_historical_record(
|
|
377
|
-
interface_cls,
|
|
378
|
-
instance,
|
|
379
|
-
search_date,
|
|
380
|
-
)
|
|
381
|
-
else:
|
|
382
|
-
historical = history_handler.get_historical_record_by_pk(
|
|
383
|
-
interface_cls,
|
|
384
|
-
pk,
|
|
385
|
-
search_date,
|
|
386
|
-
)
|
|
387
|
-
except HistoryNotSupportedError as error:
|
|
388
|
-
if current_as_of_date() is not None:
|
|
389
|
-
raise HistoricalReadNotSupportedError(
|
|
390
|
-
interface_cls.__name__
|
|
391
|
-
) from error
|
|
392
|
-
raise
|
|
393
|
-
if historical is not None:
|
|
394
|
-
return historical
|
|
395
|
-
if missing_error is not None:
|
|
396
|
-
raise missing_error
|
|
397
|
-
raise model_cls.DoesNotExist
|
|
387
|
+
) from error
|
|
388
|
+
raise
|
|
389
|
+
if historical is not None:
|
|
390
|
+
return historical
|
|
391
|
+
if missing_error is not None:
|
|
392
|
+
raise missing_error
|
|
393
|
+
raise model_cls.DoesNotExist
|
|
398
394
|
if instance is not None:
|
|
399
395
|
return instance
|
|
400
396
|
if missing_error is not None:
|
|
@@ -899,9 +895,7 @@ class OrmQueryCapability(BaseCapability):
|
|
|
899
895
|
support = get_support_capability(interface_cls)
|
|
900
896
|
if support.__class__ is not OrmPersistenceSupportCapability:
|
|
901
897
|
return None
|
|
902
|
-
if search_date is not None
|
|
903
|
-
seconds=interface_cls.historical_lookup_buffer_seconds
|
|
904
|
-
):
|
|
898
|
+
if search_date is not None:
|
|
905
899
|
return None
|
|
906
900
|
queryset_base = (
|
|
907
901
|
support.get_manager(interface_cls, only_active=False).all()
|
|
@@ -993,9 +987,7 @@ class OrmQueryCapability(BaseCapability):
|
|
|
993
987
|
only_active=False,
|
|
994
988
|
).all(),
|
|
995
989
|
)
|
|
996
|
-
if search_date is not None
|
|
997
|
-
seconds=interface_cls.historical_lookup_buffer_seconds
|
|
998
|
-
):
|
|
990
|
+
if search_date is not None:
|
|
999
991
|
from .history import HistoryNotSupportedError
|
|
1000
992
|
|
|
1001
993
|
try:
|
|
@@ -41,8 +41,8 @@ class ExistingModelInterface(OrmInterfaceBase[ExistingModelT]):
|
|
|
41
41
|
Construction and row loading are inherited from ``OrmInterfaceBase``: the
|
|
42
42
|
default public input is the wrapped row ``id`` parsed by ``Input(int)`` plus
|
|
43
43
|
optional ``search_date: datetime | None``. Naive search dates are made aware
|
|
44
|
-
with Django's current timezone, and
|
|
45
|
-
|
|
44
|
+
with Django's current timezone, and every supplied search date selects
|
|
45
|
+
history without a recency buffer. Missing rows
|
|
46
46
|
propagate the wrapped model's ``DoesNotExist`` exception. Invalid or missing
|
|
47
47
|
model declarations raise the existing-model configuration errors from the
|
|
48
48
|
resolution capability. Creation, update, delete, query, history, and
|
{generalmanager-0.86.6 → generalmanager-0.86.8}/src/general_manager/interface/orm_interface.py
RENAMED
|
@@ -41,9 +41,9 @@ class OrmInterfaceBase(InterfaceBase, Generic[HistoryModelT]):
|
|
|
41
41
|
lifecycle capability. Subclasses that override :attr:`input_fields` must keep
|
|
42
42
|
an ``"id"`` field unless they also override initialization and row loading.
|
|
43
43
|
|
|
44
|
-
``
|
|
45
|
-
``
|
|
46
|
-
|
|
44
|
+
Every non-null ``search_date`` reads the exact historical snapshot, including
|
|
45
|
+
recent timestamps. ``historical_lookup_buffer_seconds`` is retained only for
|
|
46
|
+
source compatibility and no longer permits a fallback to current rows.
|
|
47
47
|
"""
|
|
48
48
|
|
|
49
49
|
_interface_type: ClassVar[str] = "database"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generalmanager-0.86.6 → generalmanager-0.86.8}/src/GeneralManager.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|