GeneralManager 0.2.0__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. {generalmanager-0.2.0 → generalmanager-0.3.1}/GeneralManager.egg-info/PKG-INFO +1 -1
  2. {generalmanager-0.2.0 → generalmanager-0.3.1}/GeneralManager.egg-info/SOURCES.txt +9 -1
  3. {generalmanager-0.2.0 → generalmanager-0.3.1}/PKG-INFO +1 -1
  4. {generalmanager-0.2.0 → generalmanager-0.3.1}/pyproject.toml +1 -1
  5. generalmanager-0.3.1/src/general_manager/auxiliary/jsonEncoder.py +19 -0
  6. generalmanager-0.3.1/src/general_manager/auxiliary/makeCacheKey.py +33 -0
  7. {generalmanager-0.2.0/src/general_manager/cache → generalmanager-0.3.1/src/general_manager/auxiliary}/pathMapping.py +31 -6
  8. generalmanager-0.3.1/src/general_manager/cache/cacheDecorator.py +81 -0
  9. generalmanager-0.3.1/src/general_manager/cache/cacheTracker.py +74 -0
  10. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/cache/dependencyIndex.py +3 -1
  11. generalmanager-0.3.1/src/general_manager/cache/modelDependencyCollector.py +59 -0
  12. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/manager/generalManager.py +10 -4
  13. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/rule/rule.py +1 -1
  14. generalmanager-0.3.1/tests/test_cacheDecorator.py +396 -0
  15. generalmanager-0.3.1/tests/test_cacheTracker.py +76 -0
  16. generalmanager-0.3.1/tests/test_jsonEncoder.py +49 -0
  17. generalmanager-0.3.1/tests/test_makeCacheKey.py +372 -0
  18. generalmanager-0.3.1/tests/test_modelDependencyCollector.py +119 -0
  19. {generalmanager-0.2.0 → generalmanager-0.3.1}/tests/test_rule_handler.py +29 -29
  20. generalmanager-0.2.0/src/general_manager/cache/cacheDecorator.py +0 -72
  21. generalmanager-0.2.0/src/general_manager/cache/cacheTracker.py +0 -33
  22. {generalmanager-0.2.0 → generalmanager-0.3.1}/GeneralManager.egg-info/dependency_links.txt +0 -0
  23. {generalmanager-0.2.0 → generalmanager-0.3.1}/GeneralManager.egg-info/requires.txt +0 -0
  24. {generalmanager-0.2.0 → generalmanager-0.3.1}/GeneralManager.egg-info/top_level.txt +0 -0
  25. {generalmanager-0.2.0 → generalmanager-0.3.1}/LICENSE +0 -0
  26. {generalmanager-0.2.0 → generalmanager-0.3.1}/README.md +0 -0
  27. {generalmanager-0.2.0 → generalmanager-0.3.1}/setup.cfg +0 -0
  28. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/__init__.py +0 -0
  29. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/api/graphql.py +0 -0
  30. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/api/mutation.py +0 -0
  31. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/api/property.py +0 -0
  32. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/apps.py +0 -0
  33. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/auxiliary/__init__.py +0 -0
  34. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/auxiliary/argsToKwargs.py +0 -0
  35. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/auxiliary/filterParser.py +0 -0
  36. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/auxiliary/noneToZero.py +0 -0
  37. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/cache/signals.py +0 -0
  38. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/factory/__init__.py +0 -0
  39. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/factory/factories.py +0 -0
  40. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/factory/lazy_methods.py +0 -0
  41. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/interface/__init__.py +0 -0
  42. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/interface/baseInterface.py +0 -0
  43. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/interface/calculationInterface.py +0 -0
  44. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/interface/databaseInterface.py +0 -0
  45. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/manager/__init__.py +0 -0
  46. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/manager/groupManager.py +0 -0
  47. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/manager/input.py +0 -0
  48. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/manager/meta.py +0 -0
  49. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/measurement/__init__.py +0 -0
  50. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/measurement/measurement.py +0 -0
  51. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/measurement/measurementField.py +0 -0
  52. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/permission/__init__.py +0 -0
  53. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/permission/basePermission.py +0 -0
  54. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/permission/fileBasedPermission.py +0 -0
  55. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/permission/managerBasedPermission.py +0 -0
  56. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/permission/permissionChecks.py +0 -0
  57. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/permission/permissionDataManager.py +0 -0
  58. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/rule/__init__.py +0 -0
  59. {generalmanager-0.2.0 → generalmanager-0.3.1}/src/general_manager/rule/handler.py +0 -0
  60. {generalmanager-0.2.0 → generalmanager-0.3.1}/tests/test_argsToKwargs.py +0 -0
  61. {generalmanager-0.2.0 → generalmanager-0.3.1}/tests/test_basePermission.py +0 -0
  62. {generalmanager-0.2.0 → generalmanager-0.3.1}/tests/test_filterParser.py +0 -0
  63. {generalmanager-0.2.0 → generalmanager-0.3.1}/tests/test_graph_ql.py +0 -0
  64. {generalmanager-0.2.0 → generalmanager-0.3.1}/tests/test_input.py +0 -0
  65. {generalmanager-0.2.0 → generalmanager-0.3.1}/tests/test_managerBasedPermission.py +0 -0
  66. {generalmanager-0.2.0 → generalmanager-0.3.1}/tests/test_measurement.py +0 -0
  67. {generalmanager-0.2.0 → generalmanager-0.3.1}/tests/test_measurement_field.py +0 -0
  68. {generalmanager-0.2.0 → generalmanager-0.3.1}/tests/test_noneToZero.py +0 -0
  69. {generalmanager-0.2.0 → generalmanager-0.3.1}/tests/test_rules.py +0 -0
  70. {generalmanager-0.2.0 → generalmanager-0.3.1}/tests/test_settings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: Kurzbeschreibung deines Pakets
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: Non-Commercial MIT License
@@ -14,11 +14,14 @@ src/general_manager/api/property.py
14
14
  src/general_manager/auxiliary/__init__.py
15
15
  src/general_manager/auxiliary/argsToKwargs.py
16
16
  src/general_manager/auxiliary/filterParser.py
17
+ src/general_manager/auxiliary/jsonEncoder.py
18
+ src/general_manager/auxiliary/makeCacheKey.py
17
19
  src/general_manager/auxiliary/noneToZero.py
20
+ src/general_manager/auxiliary/pathMapping.py
18
21
  src/general_manager/cache/cacheDecorator.py
19
22
  src/general_manager/cache/cacheTracker.py
20
23
  src/general_manager/cache/dependencyIndex.py
21
- src/general_manager/cache/pathMapping.py
24
+ src/general_manager/cache/modelDependencyCollector.py
22
25
  src/general_manager/cache/signals.py
23
26
  src/general_manager/factory/__init__.py
24
27
  src/general_manager/factory/factories.py
@@ -46,12 +49,17 @@ src/general_manager/rule/handler.py
46
49
  src/general_manager/rule/rule.py
47
50
  tests/test_argsToKwargs.py
48
51
  tests/test_basePermission.py
52
+ tests/test_cacheDecorator.py
53
+ tests/test_cacheTracker.py
49
54
  tests/test_filterParser.py
50
55
  tests/test_graph_ql.py
51
56
  tests/test_input.py
57
+ tests/test_jsonEncoder.py
58
+ tests/test_makeCacheKey.py
52
59
  tests/test_managerBasedPermission.py
53
60
  tests/test_measurement.py
54
61
  tests/test_measurement_field.py
62
+ tests/test_modelDependencyCollector.py
55
63
  tests/test_noneToZero.py
56
64
  tests/test_rule_handler.py
57
65
  tests/test_rules.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: Kurzbeschreibung deines Pakets
5
5
  Author-email: Tim Kleindick <tkleindick@yahoo.de>
6
6
  License: Non-Commercial MIT License
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "GeneralManager"
10
- version = "0.2.0"
10
+ version = "0.3.1"
11
11
  description = "Kurzbeschreibung deines Pakets"
12
12
  readme = "README.md"
13
13
  authors = [
@@ -0,0 +1,19 @@
1
+ from datetime import datetime, date, time
2
+ import json
3
+ from general_manager.manager.generalManager import GeneralManager
4
+
5
+
6
+ class CustomJSONEncoder(json.JSONEncoder):
7
+ def default(self, o):
8
+
9
+ # Serialize datetime objects as ISO strings
10
+ if isinstance(o, (datetime, date, time)):
11
+ return o.isoformat()
12
+ # Handle GeneralManager instances
13
+ if isinstance(o, GeneralManager):
14
+ return f"{o.__class__.__name__}(**{o.identification})"
15
+ try:
16
+ return super().default(o)
17
+ except TypeError:
18
+ # Fallback: convert all other objects to str
19
+ return str(o)
@@ -0,0 +1,33 @@
1
+ import inspect
2
+ import json
3
+ from general_manager.auxiliary.jsonEncoder import CustomJSONEncoder
4
+ from hashlib import sha256
5
+
6
+
7
+ def make_cache_key(func, args, kwargs):
8
+ """
9
+ Generates a unique, deterministic cache key for a specific function call.
10
+
11
+ The key is derived from the function's module, qualified name, and bound arguments,
12
+ serialized to JSON and hashed with SHA-256 to ensure uniqueness for each call signature.
13
+
14
+ Args:
15
+ func: The target function to be identified.
16
+ args: Positional arguments for the function call.
17
+ kwargs: Keyword arguments for the function call.
18
+
19
+ Returns:
20
+ A hexadecimal SHA-256 hash string uniquely representing the function call.
21
+ """
22
+ sig = inspect.signature(func)
23
+ bound = sig.bind_partial(*args, **kwargs)
24
+ bound.apply_defaults()
25
+ payload = {
26
+ "module": func.__module__,
27
+ "qualname": func.__qualname__,
28
+ "args": bound.arguments,
29
+ }
30
+ raw = json.dumps(
31
+ payload, sort_keys=True, default=str, cls=CustomJSONEncoder
32
+ ).encode()
33
+ return sha256(raw, usedforsecurity=False).hexdigest()
@@ -3,9 +3,8 @@ from typing import TYPE_CHECKING, cast, get_args
3
3
  from general_manager.manager.meta import GeneralManagerMeta
4
4
  from general_manager.api.property import GraphQLProperty
5
5
 
6
- if TYPE_CHECKING:
7
- from general_manager.interface.baseInterface import Bucket
8
- from general_manager.manager.generalManager import GeneralManager
6
+ from general_manager.interface.baseInterface import Bucket
7
+ from general_manager.manager.generalManager import GeneralManager
9
8
 
10
9
 
11
10
  type PathStart = str
@@ -25,6 +24,11 @@ class PathMap:
25
24
 
26
25
  @classmethod
27
26
  def createPathMapping(cls):
27
+ """
28
+ Builds the mapping of paths between all pairs of distinct managed classes.
29
+
30
+ Iterates over all registered managed classes and creates a PathTracer for each unique start and destination class pair, storing them in the mapping dictionary.
31
+ """
28
32
  all_managed_classes = GeneralManagerMeta.all_classes
29
33
  for start_class in all_managed_classes:
30
34
  for destination_class in all_managed_classes:
@@ -34,8 +38,12 @@ class PathMap:
34
38
  ] = PathTracer(start_class, destination_class)
35
39
 
36
40
  def __init__(self, path_start: PathStart | GeneralManager | type[GeneralManager]):
37
- from general_manager.manager.generalManager import GeneralManager
38
41
 
42
+ """
43
+ Initializes a PathMap with a specified starting point.
44
+
45
+ The starting point can be a class name (string), a GeneralManager instance, or a GeneralManager subclass. Sets internal attributes for the start instance, class, and class name based on the input.
46
+ """
39
47
  if isinstance(path_start, GeneralManager):
40
48
  self.start_instance = path_start
41
49
  self.start_class = path_start.__class__
@@ -99,8 +107,17 @@ class PathTracer:
99
107
  def createPath(
100
108
  self, current_manager: type[GeneralManager], path: list[str]
101
109
  ) -> list[str] | None:
102
- from general_manager.manager.generalManager import GeneralManager
103
110
 
111
+ """
112
+ Recursively constructs a path of attribute names from the current manager class to the destination class.
113
+
114
+ Args:
115
+ current_manager: The current GeneralManager subclass being inspected.
116
+ path: The list of attribute names traversed so far.
117
+
118
+ Returns:
119
+ A list of attribute names representing the path to the destination class, or None if no path exists.
120
+ """
104
121
  current_connections = {
105
122
  attr_name: attr_value["type"]
106
123
  for attr_name, attr_value in current_manager.Interface.getAttributeTypes().items()
@@ -131,8 +148,16 @@ class PathTracer:
131
148
  def traversePath(
132
149
  self, start_instance: GeneralManager | Bucket
133
150
  ) -> GeneralManager | Bucket | None:
134
- from general_manager.interface.baseInterface import Bucket
135
151
 
152
+ """
153
+ Traverses the stored path from a starting instance to reach the destination instance or bucket.
154
+
155
+ Args:
156
+ start_instance: The initial GeneralManager or Bucket instance from which to begin traversal.
157
+
158
+ Returns:
159
+ The resulting GeneralManager or Bucket instance at the end of the path, or None if the path is empty.
160
+ """
136
161
  current_instance = start_instance
137
162
  if not self.path:
138
163
  return None
@@ -0,0 +1,81 @@
1
+ from typing import Any, Callable, Optional, Protocol, Set
2
+ from functools import wraps
3
+ from django.core.cache import cache as django_cache
4
+ from general_manager.cache.cacheTracker import DependencyTracker
5
+ from general_manager.cache.dependencyIndex import record_dependencies, Dependency
6
+ from general_manager.cache.modelDependencyCollector import ModelDependencyCollector
7
+ from general_manager.auxiliary.makeCacheKey import make_cache_key
8
+
9
+
10
+ class CacheBackend(Protocol):
11
+ def get(self, key: str, default: Optional[Any] = None) -> Any:
12
+ """
13
+ Retrieves a value from the cache by key, returning a default if the key is not found.
14
+
15
+ Args:
16
+ key: The cache key to look up.
17
+ default: Value to return if the key is not present in the cache.
18
+
19
+ Returns:
20
+ The cached value if found; otherwise, the provided default.
21
+ """
22
+ ...
23
+
24
+ def set(self, key: str, value: Any, timeout: Optional[int] = None) -> None:
25
+ """
26
+ Stores a value in the cache under the specified key with an optional expiration timeout.
27
+
28
+ Args:
29
+ key: The cache key to associate with the value.
30
+ value: The value to store in the cache.
31
+ timeout: Optional expiration time in seconds. If None, the value is cached indefinitely.
32
+ """
33
+ ...
34
+
35
+
36
+ RecordFn = Callable[[str, Set[Dependency]], None]
37
+
38
+ _SENTINEL = object()
39
+
40
+
41
+ def cached(
42
+ timeout: Optional[int] = None,
43
+ cache_backend: CacheBackend = django_cache,
44
+ record_fn: RecordFn = record_dependencies,
45
+ ) -> Callable:
46
+ """
47
+ Decorator that caches function results and tracks their dependencies.
48
+
49
+ When applied to a function, this decorator caches the function's output using a generated cache key based on its arguments. It also tracks dependencies accessed during the function's execution and stores them alongside the cached result. On cache hits, previously stored dependencies are re-tracked to maintain dependency tracking continuity. If dependencies exist and no timeout is set, an external recording function is invoked to persist the dependency information.
50
+ """
51
+
52
+ def decorator(func: Callable) -> Callable:
53
+ @wraps(func)
54
+ def wrapper(*args, **kwargs):
55
+ key = make_cache_key(func, args, kwargs)
56
+ deps_key = f"{key}:deps"
57
+
58
+ cached_result = cache_backend.get(key, _SENTINEL)
59
+ if cached_result is not _SENTINEL:
60
+ # saved dependencies are added to the current tracker
61
+ cached_deps = cache_backend.get(deps_key)
62
+ if cached_deps:
63
+ for class_name, operation, identifier in cached_deps:
64
+ DependencyTracker.track(class_name, operation, identifier)
65
+ return cached_result
66
+
67
+ with DependencyTracker() as dependencies:
68
+ result = func(*args, **kwargs)
69
+ ModelDependencyCollector.addArgs(dependencies, args, kwargs)
70
+
71
+ cache_backend.set(key, result, timeout)
72
+ cache_backend.set(deps_key, dependencies, timeout)
73
+
74
+ if dependencies and timeout is None:
75
+ record_fn(key, dependencies)
76
+
77
+ return result
78
+
79
+ return wrapper
80
+
81
+ return decorator
@@ -0,0 +1,74 @@
1
+ import threading
2
+ from general_manager.cache.dependencyIndex import (
3
+ general_manager_name,
4
+ Dependency,
5
+ filter_type,
6
+ )
7
+
8
+ # Thread-lokale Variable zur Speicherung der Abhängigkeiten
9
+ _dependency_storage = threading.local()
10
+
11
+
12
+ class DependencyTracker:
13
+ def __enter__(
14
+ self,
15
+ ) -> set[Dependency]:
16
+ """
17
+ Enters a new dependency tracking context and returns the set for collecting dependencies.
18
+
19
+ Initializes thread-local storage for dependency tracking if not already present, supports nested contexts, and provides a set to accumulate dependencies at the current nesting level.
20
+
21
+ Returns:
22
+ The set used to collect dependencies for the current context level.
23
+ """
24
+ if not hasattr(_dependency_storage, "dependencies"):
25
+ _dependency_storage._depth = 0
26
+ _dependency_storage.dependencies = list()
27
+ else:
28
+ _dependency_storage._depth += 1
29
+ _dependency_storage.dependencies.append(set())
30
+ return _dependency_storage.dependencies[_dependency_storage._depth]
31
+
32
+ def __exit__(self, exc_type, exc_val, exc_tb):
33
+ """
34
+ Exits the dependency tracking context, managing cleanup for nested scopes.
35
+
36
+ If exiting the outermost context, removes all dependency tracking data from thread-local storage. Otherwise, decrements the nesting depth and removes the most recent dependency set.
37
+ """
38
+ if hasattr(_dependency_storage, "dependencies"):
39
+ if _dependency_storage._depth == 0:
40
+ self.reset_thread_local_storage()
41
+
42
+ else:
43
+ # Ansonsten reduzieren wir nur die Tiefe
44
+ _dependency_storage._depth -= 1
45
+ _dependency_storage.dependencies.pop()
46
+
47
+ @staticmethod
48
+ def track(
49
+ class_name: general_manager_name,
50
+ operation: filter_type,
51
+ identifier: str,
52
+ ) -> None:
53
+ """
54
+ Records a dependency in all active dependency tracking contexts.
55
+
56
+ Adds the specified dependency tuple to each set in the current stack of dependency tracking scopes, ensuring it is tracked at all nested levels.
57
+ """
58
+ if hasattr(_dependency_storage, "dependencies"):
59
+ for dep_set in _dependency_storage.dependencies[
60
+ : _dependency_storage._depth + 1
61
+ ]:
62
+ dep_set: set[Dependency]
63
+ dep_set.add((class_name, operation, identifier))
64
+
65
+ @staticmethod
66
+ def reset_thread_local_storage() -> None:
67
+ """
68
+ Resets the thread-local storage for dependency tracking.
69
+
70
+ This method clears the thread-local storage, ensuring that all dependency tracking data is removed. It is useful for cleaning up after operations that may have modified the state of the tracker.
71
+ """
72
+ if hasattr(_dependency_storage, "dependencies"):
73
+ del _dependency_storage.dependencies
74
+ del _dependency_storage._depth
@@ -6,7 +6,7 @@ import re
6
6
  from django.core.cache import cache
7
7
  from general_manager.cache.signals import post_data_change, pre_data_change
8
8
  from django.dispatch import receiver
9
- from typing import Literal, Any, Iterable, TYPE_CHECKING, Type
9
+ from typing import Literal, Any, Iterable, TYPE_CHECKING, Type, Tuple
10
10
 
11
11
  if TYPE_CHECKING:
12
12
  from general_manager.manager.generalManager import GeneralManager
@@ -24,6 +24,8 @@ type dependency_index = dict[
24
24
  ],
25
25
  ]
26
26
 
27
+ type filter_type = Literal["filter", "exclude", "identification"]
28
+ type Dependency = Tuple[general_manager_name, filter_type, str]
27
29
 
28
30
  # -----------------------------------------------------------------------------
29
31
  # CONFIG
@@ -0,0 +1,59 @@
1
+ from typing import Generator
2
+ from general_manager.manager.generalManager import GeneralManager, Bucket
3
+ from general_manager.cache.dependencyIndex import (
4
+ general_manager_name,
5
+ Dependency,
6
+ filter_type,
7
+ )
8
+
9
+
10
+ class ModelDependencyCollector:
11
+
12
+ @staticmethod
13
+ def collect(obj) -> Generator[tuple[general_manager_name, filter_type, str]]:
14
+ """
15
+ Recursively yields dependency information from objects related to Django models.
16
+
17
+ Traverses the input object and its nested structures to identify instances of
18
+ GeneralManager and Bucket, yielding tuples that describe their dependencies.
19
+ Yields a tuple of (manager class name, dependency type, dependency value) for
20
+ each dependency found.
21
+
22
+ Args:
23
+ obj: The object or collection to inspect for model dependencies.
24
+
25
+ Yields:
26
+ Tuples containing the manager class name, dependency type ("identification",
27
+ "filter", or "exclude"), and the string representation of the dependency value.
28
+ """
29
+ if isinstance(obj, GeneralManager):
30
+ yield (
31
+ obj.__class__.__name__,
32
+ "identification",
33
+ f"{obj.identification}",
34
+ )
35
+ elif isinstance(obj, Bucket):
36
+ yield (obj._manager_class.__name__, "filter", f"{obj.filters}")
37
+ yield (obj._manager_class.__name__, "exclude", f"{obj.excludes}")
38
+ elif isinstance(obj, dict):
39
+ for v in obj.values():
40
+ yield from ModelDependencyCollector.collect(v)
41
+ elif isinstance(obj, (list, tuple, set)):
42
+ for item in obj:
43
+ yield from ModelDependencyCollector.collect(item)
44
+
45
+ @staticmethod
46
+ def addArgs(dependencies: set[Dependency], args: tuple, kwargs: dict) -> None:
47
+ """
48
+ Add dependencies to the dependency set.
49
+ """
50
+ if args and isinstance(args[0], GeneralManager):
51
+ inner_self = args[0]
52
+ for attr_val in inner_self.__dict__.values():
53
+ for dependency_tuple in ModelDependencyCollector.collect(attr_val):
54
+ dependencies.add(dependency_tuple)
55
+
56
+ for dependency_tuple in ModelDependencyCollector.collect(args):
57
+ dependencies.add(dependency_tuple)
58
+ for dependency_tuple in ModelDependencyCollector.collect(kwargs):
59
+ dependencies.add(dependency_tuple)
@@ -7,7 +7,7 @@ from general_manager.interface.baseInterface import (
7
7
  GeneralManagerType,
8
8
  )
9
9
  from general_manager.api.property import GraphQLProperty
10
- from general_manager.cache.cacheTracker import addDependency
10
+ from general_manager.cache.cacheTracker import DependencyTracker
11
11
  from general_manager.cache.signals import dataChange
12
12
 
13
13
  if TYPE_CHECKING:
@@ -21,7 +21,9 @@ class GeneralManager(Generic[GeneralManagerType], metaclass=GeneralManagerMeta):
21
21
  def __init__(self, *args: Any, **kwargs: Any):
22
22
  self._interface = self.Interface(*args, **kwargs)
23
23
  self.__id: dict[str, Any] = self._interface.identification
24
- addDependency(self.__class__.__name__, "identification", f"{self.__id}")
24
+ DependencyTracker.track(
25
+ self.__class__.__name__, "identification", f"{self.__id}"
26
+ )
25
27
 
26
28
  def __str__(self):
27
29
  return f"{self.__class__.__name__}(**{self.__id})"
@@ -108,12 +110,16 @@ class GeneralManager(Generic[GeneralManagerType], metaclass=GeneralManagerMeta):
108
110
 
109
111
  @classmethod
110
112
  def filter(cls, **kwargs: Any) -> Bucket[GeneralManagerType]:
111
- addDependency(cls.__name__, "filter", f"{cls.__parse_identification(kwargs)}")
113
+ DependencyTracker.track(
114
+ cls.__name__, "filter", f"{cls.__parse_identification(kwargs)}"
115
+ )
112
116
  return cls.Interface.filter(**kwargs)
113
117
 
114
118
  @classmethod
115
119
  def exclude(cls, **kwargs: Any) -> Bucket[GeneralManagerType]:
116
- addDependency(cls.__name__, "exclude", f"{cls.__parse_identification(kwargs)}")
120
+ DependencyTracker.track(
121
+ cls.__name__, "exclude", f"{cls.__parse_identification(kwargs)}"
122
+ )
117
123
  return cls.Interface.exclude(**kwargs)
118
124
 
119
125
  @classmethod
@@ -81,7 +81,7 @@ class Rule(Generic[GeneralManagerType]):
81
81
  inst = cls()
82
82
  self._handlers[inst.function_name] = inst
83
83
  for path in getattr(settings, "RULE_HANDLERS", []):
84
- handler_cls = import_string(path)
84
+ handler_cls: type[BaseRuleHandler] = import_string(path)
85
85
  inst = handler_cls()
86
86
  self._handlers[inst.function_name] = inst
87
87