GeneralManager 0.3.0__py3-none-any.whl → 0.3.1__py3-none-any.whl

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.
@@ -6,22 +6,25 @@ from hashlib import sha256
6
6
 
7
7
  def make_cache_key(func, args, kwargs):
8
8
  """
9
- Generate a deterministic cache key for a function call.
10
-
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
+
11
14
  Args:
12
- func: The function being called
13
- args: Positional arguments to the function
14
- kwargs: Keyword arguments to the function
15
-
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
+
16
19
  Returns:
17
- str: A hexadecimal SHA-256 hash that uniquely identifies this function call
20
+ A hexadecimal SHA-256 hash string uniquely representing the function call.
18
21
  """
19
22
  sig = inspect.signature(func)
20
23
  bound = sig.bind_partial(*args, **kwargs)
21
24
  bound.apply_defaults()
22
25
  payload = {
23
26
  "module": func.__module__,
24
- "name": func.__name__,
27
+ "qualname": func.__qualname__,
25
28
  "args": bound.arguments,
26
29
  }
27
30
  raw = json.dumps(
@@ -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
@@ -8,35 +8,71 @@ from general_manager.auxiliary.makeCacheKey import make_cache_key
8
8
 
9
9
 
10
10
  class CacheBackend(Protocol):
11
- def get(self, key: str) -> Any: ...
12
- def set(self, key: str, value: Any, timeout: Optional[int] = None) -> None: ...
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
+ ...
13
34
 
14
35
 
15
36
  RecordFn = Callable[[str, Set[Dependency]], None]
16
37
 
38
+ _SENTINEL = object()
39
+
17
40
 
18
41
  def cached(
19
42
  timeout: Optional[int] = None,
20
43
  cache_backend: CacheBackend = django_cache,
21
44
  record_fn: RecordFn = record_dependencies,
22
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
+
23
52
  def decorator(func: Callable) -> Callable:
24
53
  @wraps(func)
25
54
  def wrapper(*args, **kwargs):
26
55
  key = make_cache_key(func, args, kwargs)
56
+ deps_key = f"{key}:deps"
27
57
 
28
- result = cache_backend.get(key)
29
- if result is not None:
30
- return result
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
31
66
 
32
67
  with DependencyTracker() as dependencies:
33
68
  result = func(*args, **kwargs)
34
69
  ModelDependencyCollector.addArgs(dependencies, args, kwargs)
35
70
 
36
- cache_backend.set(key, result, timeout)
71
+ cache_backend.set(key, result, timeout)
72
+ cache_backend.set(deps_key, dependencies, timeout)
37
73
 
38
- if dependencies and timeout is None:
39
- record_fn(key, dependencies)
74
+ if dependencies and timeout is None:
75
+ record_fn(key, dependencies)
40
76
 
41
77
  return result
42
78
 
@@ -13,12 +13,36 @@ class DependencyTracker:
13
13
  def __enter__(
14
14
  self,
15
15
  ) -> set[Dependency]:
16
- _dependency_storage.dependencies = set()
17
- return _dependency_storage.dependencies
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]
18
31
 
19
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
+ """
20
38
  if hasattr(_dependency_storage, "dependencies"):
21
- del _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()
22
46
 
23
47
  @staticmethod
24
48
  def track(
@@ -27,8 +51,24 @@ class DependencyTracker:
27
51
  identifier: str,
28
52
  ) -> None:
29
53
  """
30
- Adds a dependency to the dependency storage.
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.
31
71
  """
32
72
  if hasattr(_dependency_storage, "dependencies"):
33
- dependencies: set[Dependency] = _dependency_storage.dependencies
34
- dependencies.add((class_name, operation, identifier))
73
+ del _dependency_storage.dependencies
74
+ del _dependency_storage._depth
@@ -9,15 +9,23 @@ from general_manager.cache.dependencyIndex import (
9
9
 
10
10
  class ModelDependencyCollector:
11
11
 
12
- def __init__(self, dependencies: set[Dependency]):
13
- """
14
- Initialize the ModelDependencyCollector with a set of dependencies.
15
- """
16
- self.dependencies = dependencies
17
-
18
12
  @staticmethod
19
13
  def collect(obj) -> Generator[tuple[general_manager_name, filter_type, str]]:
20
- """Recursively find Django model instances in the object."""
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
+ """
21
29
  if isinstance(obj, GeneralManager):
22
30
  yield (
23
31
  obj.__class__.__name__,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.3.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,13 +7,13 @@ general_manager/auxiliary/__init__.py,sha256=4IwKJzsNxGduF-Ej0u1BNHVaMhkql8PjHbV
7
7
  general_manager/auxiliary/argsToKwargs.py,sha256=kmp1xonpQp4X_y8ZJG6c5uOW7zQwo0HtPqsHWVzXRSM,921
8
8
  general_manager/auxiliary/filterParser.py,sha256=wmR4YzsnYgjI2Co5eyvCFROldotAraHx_GiCDJo79IY,5410
9
9
  general_manager/auxiliary/jsonEncoder.py,sha256=TDsgFQvheITHZgdmn-m8tk1_QCzpT0XwEHo7bY3Qe-M,638
10
- general_manager/auxiliary/makeCacheKey.py,sha256=o2ZPe5ZjiZhHxYgSESBSMnOmQutkTxw5DLeJM7tOw84,881
10
+ general_manager/auxiliary/makeCacheKey.py,sha256=lczutqxlofLSUnheKRi8nazhOyPa04TZOFNxNn5vDu4,1126
11
11
  general_manager/auxiliary/noneToZero.py,sha256=KfQtMQnrT6vsYST0K7lv6pVujkDcK3XL8czHYOhgqKQ,539
12
- general_manager/cache/cacheDecorator.py,sha256=7DbPMw-2Xm1-5eCMVWyjUl_jdVtTJUKNpLW2WNJEhI0,1471
13
- general_manager/cache/cacheTracker.py,sha256=RmUWwAXMS5LQT8-w7IiG67FTBSxF1V0w4IDH1WzgmuM,998
12
+ general_manager/auxiliary/pathMapping.py,sha256=nrz5owQg2a69Yig1eCXorR9U0NSw7NmBAk5OkeoHTdA,6842
13
+ general_manager/cache/cacheDecorator.py,sha256=DK2ANIJgPpMxazfMSiFrI9OuVE_7K9zlIZQRrgaC2Lw,3268
14
+ general_manager/cache/cacheTracker.py,sha256=rRw3OhBDf86hTC2Xbt1ocRgZqwu8_kXk4lczamcADFg,2955
14
15
  general_manager/cache/dependencyIndex.py,sha256=iuOjthmH5ehHCkWiM9iLbgzGlo4Cf2wRKkm-QhIQ024,10813
15
- general_manager/cache/modelDependencyCollector.py,sha256=Lt8mNpnp-AJaPCYrTu_UB1m_Hj1prfEXaPrxRwk3Qqs,1983
16
- general_manager/cache/pathMapping.py,sha256=WtECIek9fI-2_nqIYI4Ux9Lan6g8P9TMO_AfthkznX8,5656
16
+ general_manager/cache/modelDependencyCollector.py,sha256=wS2edbZsQ1aTfRlHj02lhuasZHCc2ucRGob-E7ejuoY,2433
17
17
  general_manager/cache/signals.py,sha256=ZHeXKFMN7tj9t0J-vSqf_05_NhGqEF2sZtbZO3vaRqI,1234
18
18
  general_manager/factory/__init__.py,sha256=DLSQbpSBpujPtDSZcruPc43OLWzKCCtf20gbalCDYRU,91
19
19
  general_manager/factory/factories.py,sha256=31NyVw2Ju5ZDPJ6sax6_seyZesCU5UrviNbk-2PaKnA,11559
@@ -39,8 +39,8 @@ general_manager/permission/permissionDataManager.py,sha256=Ji7fsnuaKTa6M8yzCGyzr
39
39
  general_manager/rule/__init__.py,sha256=4Har5cfPD1fmOsilTDod-ZUz3Com-tkl58jz7yY4fD0,23
40
40
  general_manager/rule/handler.py,sha256=z8SFHTIZ0LbLh3fV56Mud0V4_OvWkqJjlHvFqau7Qfk,7334
41
41
  general_manager/rule/rule.py,sha256=3FVCKGL7BTVoStdgOTdWQwuoVRIxAIAilV4VOzouDpc,10759
42
- generalmanager-0.3.0.dist-info/licenses/LICENSE,sha256=YGFm0ieb4KpkMRRt2qnWue6uFh0cUMtobwEBkHwajhc,1450
43
- generalmanager-0.3.0.dist-info/METADATA,sha256=sKfEf7wuzdUhszsnlCJ49iydcFKidlwV58863yosGlw,8188
44
- generalmanager-0.3.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
45
- generalmanager-0.3.0.dist-info/top_level.txt,sha256=sTDtExP9ga-YP3h3h42yivUY-A2Q23C2nw6LNKOho4I,16
46
- generalmanager-0.3.0.dist-info/RECORD,,
42
+ generalmanager-0.3.1.dist-info/licenses/LICENSE,sha256=YGFm0ieb4KpkMRRt2qnWue6uFh0cUMtobwEBkHwajhc,1450
43
+ generalmanager-0.3.1.dist-info/METADATA,sha256=uintWwJEHfXzzHYW1Gwi1BQwqycnwdQI_oQW-XUWLiM,8188
44
+ generalmanager-0.3.1.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
45
+ generalmanager-0.3.1.dist-info/top_level.txt,sha256=sTDtExP9ga-YP3h3h42yivUY-A2Q23C2nw6LNKOho4I,16
46
+ generalmanager-0.3.1.dist-info/RECORD,,