GeneralManager 0.17.0__py3-none-any.whl → 0.19.0__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.
- general_manager/__init__.py +11 -1
- general_manager/_types/api.py +0 -1
- general_manager/_types/bucket.py +0 -1
- general_manager/_types/cache.py +0 -1
- general_manager/_types/factory.py +0 -1
- general_manager/_types/general_manager.py +0 -1
- general_manager/_types/interface.py +0 -1
- general_manager/_types/manager.py +0 -1
- general_manager/_types/measurement.py +0 -1
- general_manager/_types/permission.py +0 -1
- general_manager/_types/rule.py +0 -1
- general_manager/_types/utils.py +0 -1
- general_manager/api/__init__.py +13 -1
- general_manager/api/graphql.py +356 -221
- general_manager/api/graphql_subscription_consumer.py +81 -78
- general_manager/api/mutation.py +85 -23
- general_manager/api/property.py +39 -13
- general_manager/apps.py +188 -47
- general_manager/bucket/__init__.py +10 -1
- general_manager/bucket/calculationBucket.py +155 -53
- general_manager/bucket/databaseBucket.py +157 -45
- general_manager/bucket/groupBucket.py +133 -44
- general_manager/cache/__init__.py +10 -1
- general_manager/cache/cacheDecorator.py +3 -0
- general_manager/cache/dependencyIndex.py +143 -45
- general_manager/cache/signals.py +9 -2
- general_manager/factory/__init__.py +10 -1
- general_manager/factory/autoFactory.py +55 -13
- general_manager/factory/factories.py +110 -40
- general_manager/factory/factoryMethods.py +122 -34
- general_manager/interface/__init__.py +10 -1
- general_manager/interface/baseInterface.py +129 -36
- general_manager/interface/calculationInterface.py +35 -18
- general_manager/interface/databaseBasedInterface.py +71 -45
- general_manager/interface/databaseInterface.py +96 -38
- general_manager/interface/models.py +5 -5
- general_manager/interface/readOnlyInterface.py +94 -20
- general_manager/manager/__init__.py +10 -1
- general_manager/manager/generalManager.py +25 -16
- general_manager/manager/groupManager.py +20 -6
- general_manager/manager/meta.py +84 -16
- general_manager/measurement/__init__.py +10 -1
- general_manager/measurement/measurement.py +289 -95
- general_manager/measurement/measurementField.py +42 -31
- general_manager/permission/__init__.py +10 -1
- general_manager/permission/basePermission.py +120 -38
- general_manager/permission/managerBasedPermission.py +72 -21
- general_manager/permission/mutationPermission.py +14 -9
- general_manager/permission/permissionChecks.py +14 -12
- general_manager/permission/permissionDataManager.py +24 -11
- general_manager/permission/utils.py +34 -6
- general_manager/public_api_registry.py +36 -10
- general_manager/rule/__init__.py +10 -1
- general_manager/rule/handler.py +133 -44
- general_manager/rule/rule.py +178 -39
- general_manager/utils/__init__.py +10 -1
- general_manager/utils/argsToKwargs.py +34 -9
- general_manager/utils/filterParser.py +22 -7
- general_manager/utils/formatString.py +1 -0
- general_manager/utils/pathMapping.py +23 -15
- general_manager/utils/public_api.py +33 -2
- general_manager/utils/testing.py +31 -33
- {generalmanager-0.17.0.dist-info → generalmanager-0.19.0.dist-info}/METADATA +3 -1
- generalmanager-0.19.0.dist-info/RECORD +77 -0
- {generalmanager-0.17.0.dist-info → generalmanager-0.19.0.dist-info}/licenses/LICENSE +1 -1
- generalmanager-0.17.0.dist-info/RECORD +0 -77
- {generalmanager-0.17.0.dist-info → generalmanager-0.19.0.dist-info}/WHEEL +0 -0
- {generalmanager-0.17.0.dist-info → generalmanager-0.19.0.dist-info}/top_level.txt +0 -0
general_manager/manager/meta.py
CHANGED
|
@@ -13,6 +13,49 @@ if TYPE_CHECKING:
|
|
|
13
13
|
GeneralManagerType = TypeVar("GeneralManagerType", bound="GeneralManager")
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
class InvalidInterfaceTypeError(TypeError):
|
|
17
|
+
"""Raised when a GeneralManager is configured with an incompatible Interface class."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, interface_name: str) -> None:
|
|
20
|
+
"""
|
|
21
|
+
Initialize an InvalidInterfaceTypeError indicating a configured interface is not a subclass of InterfaceBase.
|
|
22
|
+
|
|
23
|
+
Parameters:
|
|
24
|
+
interface_name (str): Name of the configured interface class that is invalid; included in the exception message.
|
|
25
|
+
"""
|
|
26
|
+
super().__init__(f"{interface_name} must be a subclass of InterfaceBase.")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MissingAttributeError(AttributeError):
|
|
30
|
+
"""Raised when a dynamically generated descriptor cannot locate the attribute."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, attribute_name: str, class_name: str) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Initialize the MissingAttributeError with the missing attribute and its owning class.
|
|
35
|
+
|
|
36
|
+
Parameters:
|
|
37
|
+
attribute_name (str): Name of the attribute that was not found.
|
|
38
|
+
class_name (str): Name of the class where the attribute lookup occurred.
|
|
39
|
+
|
|
40
|
+
The exception message is set to "`{attribute_name} not found in {class_name}.`".
|
|
41
|
+
"""
|
|
42
|
+
super().__init__(f"{attribute_name} not found in {class_name}.")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class AttributeEvaluationError(AttributeError):
|
|
46
|
+
"""Raised when evaluating a callable attribute raises an exception."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, attribute_name: str, error: Exception) -> None:
|
|
49
|
+
"""
|
|
50
|
+
Initialize an AttributeEvaluationError that wraps an exception raised while evaluating a descriptor attribute.
|
|
51
|
+
|
|
52
|
+
Parameters:
|
|
53
|
+
attribute_name (str): Name of the attribute whose evaluation failed.
|
|
54
|
+
error (Exception): The original exception that was raised; retained for inspection.
|
|
55
|
+
"""
|
|
56
|
+
super().__init__(f"Error calling attribute {attribute_name}: {error}.")
|
|
57
|
+
|
|
58
|
+
|
|
16
59
|
class _nonExistent:
|
|
17
60
|
pass
|
|
18
61
|
|
|
@@ -33,15 +76,18 @@ class GeneralManagerMeta(type):
|
|
|
33
76
|
attrs: dict[str, Any],
|
|
34
77
|
) -> type:
|
|
35
78
|
"""
|
|
36
|
-
Create a
|
|
79
|
+
Create a GeneralManager subclass, integrate any declared Interface hooks, and register the class for pending initialization and GraphQL processing.
|
|
80
|
+
|
|
81
|
+
If the class body defines an `Interface`, validates it is a subclass of `InterfaceBase`, invokes the interface's `handleInterface()` pre-creation hook to allow modification of the class namespace, creates the class, then invokes the post-creation hook and registers the class for attribute initialization and global tracking. If `Interface` is not defined, creates the class directly. If `settings.AUTOCREATE_GRAPHQL` is true, registers the created class for GraphQL interface processing.
|
|
37
82
|
|
|
38
83
|
Parameters:
|
|
84
|
+
mcs (type): The metaclass creating the class.
|
|
39
85
|
name (str): Name of the class being created.
|
|
40
|
-
bases (tuple[type, ...]): Base classes
|
|
86
|
+
bases (tuple[type, ...]): Base classes for the new class.
|
|
41
87
|
attrs (dict[str, Any]): Class namespace supplied during creation.
|
|
42
88
|
|
|
43
89
|
Returns:
|
|
44
|
-
type:
|
|
90
|
+
type: The newly created subclass, possibly modified by Interface hooks.
|
|
45
91
|
"""
|
|
46
92
|
|
|
47
93
|
def createNewGeneralManagerClass(
|
|
@@ -56,9 +102,7 @@ class GeneralManagerMeta(type):
|
|
|
56
102
|
if "Interface" in attrs:
|
|
57
103
|
interface = attrs.pop("Interface")
|
|
58
104
|
if not issubclass(interface, InterfaceBase):
|
|
59
|
-
raise
|
|
60
|
-
f"{interface.__name__} must be a subclass of InterfaceBase"
|
|
61
|
-
)
|
|
105
|
+
raise InvalidInterfaceTypeError(interface.__name__)
|
|
62
106
|
preCreation, postCreation = interface.handleInterface()
|
|
63
107
|
attrs, interface_cls, model = preCreation(name, attrs, interface)
|
|
64
108
|
new_class = createNewGeneralManagerClass(mcs, name, bases, attrs)
|
|
@@ -79,18 +123,31 @@ class GeneralManagerMeta(type):
|
|
|
79
123
|
attributes: Iterable[str], new_class: Type[GeneralManager]
|
|
80
124
|
) -> None:
|
|
81
125
|
"""
|
|
82
|
-
Attach descriptor
|
|
126
|
+
Attach descriptor properties to new_class for each name in attributes.
|
|
127
|
+
|
|
128
|
+
Each generated descriptor returns the interface field type when accessed on the class and resolves the corresponding value from instance._attributes when accessed on an instance. If the stored value is callable it is invoked with instance._interface; a missing attribute raises MissingAttributeError and an exception raised while invoking a callable is wrapped in AttributeEvaluationError.
|
|
83
129
|
|
|
84
130
|
Parameters:
|
|
85
|
-
attributes (Iterable[str]): Names of attributes for which descriptors
|
|
86
|
-
new_class (Type[GeneralManager]): Class
|
|
131
|
+
attributes (Iterable[str]): Names of attributes for which descriptors will be created.
|
|
132
|
+
new_class (Type[GeneralManager]): Class that will receive the generated descriptor attributes.
|
|
87
133
|
"""
|
|
88
134
|
|
|
89
135
|
def descriptorMethod(
|
|
90
136
|
attr_name: str,
|
|
91
137
|
new_class: type,
|
|
92
138
|
) -> object:
|
|
93
|
-
"""
|
|
139
|
+
"""
|
|
140
|
+
Create a descriptor that provides attribute access backed by an instance's interface attributes.
|
|
141
|
+
|
|
142
|
+
When accessed on the class, the descriptor returns the field type by delegating to the class's `Interface.getFieldType` for the configured attribute name. When accessed on an instance, it returns the value stored in `instance._attributes[attr_name]`. If the stored value is callable, it is invoked with `instance._interface` and the resulting value is returned. If the attribute is not present on the instance, a `MissingAttributeError` is raised. If invoking a callable attribute raises an exception, that error is wrapped in `AttributeEvaluationError`.
|
|
143
|
+
|
|
144
|
+
Parameters:
|
|
145
|
+
attr_name (str): The name of the attribute the descriptor resolves.
|
|
146
|
+
new_class (type): The class that will receive the descriptor; used to access its `Interface`.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
descriptor (object): A descriptor object suitable for assigning as a class attribute.
|
|
150
|
+
"""
|
|
94
151
|
|
|
95
152
|
class Descriptor:
|
|
96
153
|
def __init__(
|
|
@@ -104,21 +161,32 @@ class GeneralManagerMeta(type):
|
|
|
104
161
|
instance: Any | None,
|
|
105
162
|
owner: type | None = None,
|
|
106
163
|
) -> Any:
|
|
107
|
-
"""
|
|
164
|
+
"""
|
|
165
|
+
Provide the class field type when accessed on the class, or resolve and return the stored attribute value for an instance.
|
|
166
|
+
|
|
167
|
+
When accessed on a class, returns the field type from the class's Interface via Interface.getFieldType.
|
|
168
|
+
When accessed on an instance, retrieves the value stored in instance._attributes for this descriptor's attribute name;
|
|
169
|
+
if the stored value is callable, it is invoked with instance._interface and the result is returned.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
The field type (when accessed on the class) or the resolved attribute value from the instance.
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
MissingAttributeError: If the attribute is not present in instance._attributes.
|
|
176
|
+
AttributeEvaluationError: If calling a callable attribute raises an exception; the original exception is wrapped.
|
|
177
|
+
"""
|
|
108
178
|
if instance is None:
|
|
109
179
|
return self._class.Interface.getFieldType(self._attr_name)
|
|
110
180
|
attribute = instance._attributes.get(self._attr_name, _nonExistent)
|
|
111
181
|
if attribute is _nonExistent:
|
|
112
|
-
raise
|
|
113
|
-
|
|
182
|
+
raise MissingAttributeError(
|
|
183
|
+
self._attr_name, instance.__class__.__name__
|
|
114
184
|
)
|
|
115
185
|
if callable(attribute):
|
|
116
186
|
try:
|
|
117
187
|
attribute = attribute(instance._interface)
|
|
118
188
|
except Exception as e:
|
|
119
|
-
raise
|
|
120
|
-
f"Error calling attribute {self._attr_name}: {e}"
|
|
121
|
-
) from e
|
|
189
|
+
raise AttributeEvaluationError(self._attr_name, e) from e
|
|
122
190
|
return attribute
|
|
123
191
|
|
|
124
192
|
return Descriptor(attr_name, cast(Type[Any], new_class))
|
|
@@ -12,10 +12,19 @@ __all__ = list(MEASUREMENT_EXPORTS)
|
|
|
12
12
|
_MODULE_MAP = MEASUREMENT_EXPORTS
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
|
-
from general_manager._types.measurement import * # noqa:
|
|
15
|
+
from general_manager._types.measurement import * # noqa: F403
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def __getattr__(name: str) -> Any:
|
|
19
|
+
"""
|
|
20
|
+
Dynamically resolve and return a public API attribute by name.
|
|
21
|
+
|
|
22
|
+
Parameters:
|
|
23
|
+
name (str): The attribute name requested from the module's public API.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Any: The object or submodule bound to `name` as defined by the module export mapping.
|
|
27
|
+
"""
|
|
19
28
|
return resolve_export(
|
|
20
29
|
name,
|
|
21
30
|
module_all=__all__,
|