GeneralManager 0.10.6__py3-none-any.whl → 0.11.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.
@@ -1,5 +1,14 @@
1
1
  import inspect
2
- from typing import get_type_hints, Optional, Union, List, Tuple, get_origin, get_args
2
+ from typing import (
3
+ get_type_hints,
4
+ Optional,
5
+ Union,
6
+ List,
7
+ Tuple,
8
+ get_origin,
9
+ get_args,
10
+ Type,
11
+ )
3
12
  import graphene
4
13
 
5
14
  from general_manager.api.graphql import GraphQL
@@ -7,9 +16,10 @@ from general_manager.manager.generalManager import GeneralManager
7
16
 
8
17
  from general_manager.utils.formatString import snake_to_camel
9
18
  from typing import TypeAliasType
19
+ from general_manager.permission.mutationPermission import MutationPermission
10
20
 
11
21
 
12
- def graphQlMutation(needs_role: Optional[str] = None, auth_required: bool = False):
22
+ def graphQlMutation(permission: Optional[Type[MutationPermission]] = None):
13
23
  """
14
24
  Decorator that transforms a function into a GraphQL mutation class and registers it for use in a Graphene-based API.
15
25
 
@@ -109,11 +119,9 @@ def graphQlMutation(needs_role: Optional[str] = None, auth_required: bool = Fals
109
119
 
110
120
  # Define mutate method
111
121
  def _mutate(root, info, **kwargs):
112
- # Auth check
113
- if auth_required and not getattr(info.context, "user", None):
114
- return mutation_class(
115
- **{"success": False, "errors": ["Authentication required"]}
116
- )
122
+
123
+ if permission:
124
+ permission.check(kwargs, info.context.user)
117
125
  try:
118
126
  result = fn(info, **kwargs)
119
127
  data = {}
@@ -47,7 +47,7 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
47
47
  ):
48
48
  """
49
49
  Initialize the interface and load the associated model instance by primary key.
50
-
50
+
51
51
  If a `search_date` is provided, retrieves the historical record as of that date; otherwise, loads the current record.
52
52
  """
53
53
  super().__init__(*args, **kwargs)
@@ -136,9 +136,9 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
136
136
  def getAttributeTypes(cls) -> dict[str, AttributeTypedDict]:
137
137
  """
138
138
  Return a dictionary mapping each attribute name of the model to its type information and metadata.
139
-
139
+
140
140
  The returned dictionary includes all standard model fields, custom fields, foreign keys, many-to-many, and reverse relation fields, excluding any GenericForeignKey fields. For each attribute, the metadata specifies its Python type (translated from Django field types when possible), whether it is required, editable, derived, and its default value. For related models with a general manager class, the type is set to that class.
141
-
141
+
142
142
  Returns:
143
143
  dict[str, AttributeTypedDict]: Mapping of attribute names to their type information and metadata.
144
144
  """
@@ -248,12 +248,14 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
248
248
  def getAttributes(cls) -> dict[str, Callable[[DBBasedInterface], Any]]:
249
249
  """
250
250
  Return a dictionary mapping attribute names to callables that retrieve values from a DBBasedInterface instance.
251
-
251
+
252
252
  The mapping includes accessors for custom fields, standard model fields, foreign keys, many-to-many relations, and reverse relations. For related models with a general manager class, the accessor returns an instance of that class; otherwise, it returns the related object or queryset. Raises a ValueError if a field name conflict is detected.
253
-
253
+
254
254
  Returns:
255
255
  dict: A dictionary where keys are attribute names and values are callables that extract the corresponding value from a DBBasedInterface instance.
256
256
  """
257
+ from general_manager.manager.generalManager import GeneralManager
258
+
257
259
  field_values: dict[str, Any] = {}
258
260
 
259
261
  field_name_list, to_ignore_list = cls.handleCustomFields(cls._model)
@@ -274,10 +276,14 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
274
276
  related_model,
275
277
  "_general_manager_class",
276
278
  ):
277
- generalManagerClass = related_model._general_manager_class # type: ignore
279
+ generalManagerClass = cast(
280
+ Type[GeneralManager], related_model._general_manager_class
281
+ )
278
282
  field_values[f"{field_name}"] = (
279
- lambda self, field_name=field_name, manager_class=generalManagerClass: manager_class(
280
- getattr(self._instance, field_name).pk
283
+ lambda self, field_name=field_name, manager_class=generalManagerClass: (
284
+ manager_class(getattr(self._instance, field_name).pk)
285
+ if getattr(self._instance, field_name)
286
+ else None
281
287
  )
282
288
  )
283
289
  else:
@@ -339,10 +345,10 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
339
345
  def _getCustomFields(model: Type[models.Model] | models.Model) -> list[str]:
340
346
  """
341
347
  Return a list of custom field names defined directly as class attributes on the given Django model.
342
-
348
+
343
349
  Parameters:
344
350
  model: The Django model class or instance to inspect.
345
-
351
+
346
352
  Returns:
347
353
  A list of field names for fields declared directly on the model class, excluding those defined via Django's meta system.
348
354
  """
@@ -356,7 +362,7 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
356
362
  def __getModelFields(cls) -> list[str]:
357
363
  """
358
364
  Return a list of field names for the model that are neither many-to-many nor related fields.
359
-
365
+
360
366
  Fields representing many-to-many relationships or relations to other models are excluded from the result.
361
367
  """
362
368
  return [
@@ -380,7 +386,7 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
380
386
  def __getManyToManyFields(cls) -> list[tuple[str, str]]:
381
387
  """
382
388
  Return a list of tuples representing all many-to-many fields on the model.
383
-
389
+
384
390
  Each tuple contains the field name twice. Fields that are generic foreign keys are excluded.
385
391
  """
386
392
  return [
@@ -393,7 +399,7 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
393
399
  def __getReverseRelations(cls) -> list[tuple[str, str]]:
394
400
  """
395
401
  Return a list of reverse one-to-many relations for the model, excluding generic foreign keys.
396
-
402
+
397
403
  Each tuple contains the related field's name and its default related accessor name (e.g., `fieldname_set`).
398
404
  """
399
405
  return [
@@ -502,7 +508,7 @@ class DBBasedInterface(InterfaceBase, Generic[MODEL_TYPE]):
502
508
  def getFieldType(cls, field_name: str) -> type:
503
509
  """
504
510
  Return the type associated with a given model field name.
505
-
511
+
506
512
  If the field is a relation and its related model has a `_general_manager_class` attribute, that class is returned; otherwise, returns the Django field type.
507
513
  """
508
514
  field = cls._model._meta.get_field(field_name)
@@ -58,6 +58,19 @@ class GeneralManager(metaclass=GeneralManagerMeta):
58
58
  else:
59
59
  raise TypeError(f"Unsupported type for union: {type(other)}")
60
60
 
61
+ def __eq__(
62
+ self,
63
+ other: object,
64
+ ) -> bool:
65
+ """
66
+ Check equality based on the identification dictionary.
67
+
68
+ Returns True if the other object is a GeneralManager with the same identification, otherwise False.
69
+ """
70
+ if not isinstance(other, GeneralManager):
71
+ return False
72
+ return self.identification == other.identification
73
+
61
74
  @property
62
75
  def identification(self):
63
76
  return self.__id
@@ -8,6 +8,7 @@ from general_manager.permission.permissionChecks import (
8
8
 
9
9
  from django.contrib.auth.models import AnonymousUser, AbstractUser
10
10
  from general_manager.permission.permissionDataManager import PermissionDataManager
11
+ from general_manager.permission.utils import validatePermissionString
11
12
 
12
13
  if TYPE_CHECKING:
13
14
  from general_manager.manager.generalManager import GeneralManager
@@ -153,24 +154,9 @@ class BasePermission(ABC):
153
154
  self,
154
155
  permission: str,
155
156
  ) -> bool:
156
- # permission can be a combination of multiple permissions
157
- # separated by "&" (e.g. "isAuthenticated&isMatchingKeyAccount")
158
- # this means that all sub_permissions must be true
159
- return all(
160
- [
161
- self.__validateSinglePermission(sub_permission)
162
- for sub_permission in permission.split("&")
163
- ]
164
- )
165
-
166
- def __validateSinglePermission(
167
- self,
168
- permission: str,
169
- ) -> bool:
170
- permission_function, *config = permission.split(":")
171
- if permission_function not in permission_functions:
172
- raise ValueError(f"Permission {permission} not found")
173
-
174
- return permission_functions[permission_function]["permission_method"](
175
- self.instance, self.request_user, config
176
- )
157
+ """
158
+ Validates a permission string which can be a combination of multiple permissions
159
+ separated by "&" (e.g. "isAuthenticated&isMatchingKeyAccount").
160
+ This means that all sub_permissions must be true.
161
+ """
162
+ return validatePermissionString(permission, self.instance, self.request_user)
@@ -30,8 +30,7 @@ class ManagerBasedPermission(BasePermission):
30
30
  request_user: AbstractUser,
31
31
  ) -> None:
32
32
 
33
- self.__instance = instance
34
- self.__request_user = request_user
33
+ super().__init__(instance, request_user)
35
34
  self.__attribute_permissions = self.__getAttributePermissions()
36
35
  self.__based_on_permission = self.__getBasedOnPermission()
37
36
  self.__overall_results: Dict[permission_type, Optional[bool]] = {
@@ -51,7 +50,7 @@ class ManagerBasedPermission(BasePermission):
51
50
  basis_object = getattr(self.instance, __based_on__, None)
52
51
  if basis_object is None:
53
52
  raise ValueError(
54
- f"Based on object {__based_on__} not found in instance {self.__instance}"
53
+ f"Based on object {__based_on__} not found in instance {self.instance}"
55
54
  )
56
55
  if not isinstance(basis_object, GeneralManager) and not issubclass(
57
56
  basis_object, GeneralManager
@@ -71,14 +70,6 @@ class ManagerBasedPermission(BasePermission):
71
70
  request_user=self.request_user,
72
71
  )
73
72
 
74
- @property
75
- def instance(self) -> PermissionDataManager | GeneralManager:
76
- return self.__instance
77
-
78
- @property
79
- def request_user(self) -> AbstractUser:
80
- return self.__request_user
81
-
82
73
  def __getAttributePermissions(
83
74
  self,
84
75
  ) -> dict[str, dict[permission_type, list[str]]]:
@@ -0,0 +1,88 @@
1
+ from __future__ import annotations
2
+ from django.contrib.auth.models import AbstractUser, AnonymousUser
3
+ from typing import Any
4
+ from general_manager.permission.basePermission import BasePermission
5
+
6
+ from general_manager.permission.permissionDataManager import PermissionDataManager
7
+ from general_manager.permission.utils import validatePermissionString
8
+
9
+
10
+ class MutationPermission:
11
+ __mutate__: list[str]
12
+
13
+ def __init__(
14
+ self, data: dict[str, Any], request_user: AbstractUser | AnonymousUser
15
+ ) -> None:
16
+ self._data = PermissionDataManager(data)
17
+ self._request_user = request_user
18
+ self.__attribute_permissions = self.__getAttributePermissions()
19
+
20
+ self.__overall_result: bool | None = None
21
+
22
+ @property
23
+ def data(self) -> PermissionDataManager:
24
+ return self._data
25
+
26
+ @property
27
+ def request_user(self) -> AbstractUser | AnonymousUser:
28
+ return self._request_user
29
+
30
+ def __getAttributePermissions(
31
+ self,
32
+ ) -> dict[str, list[str]]:
33
+ attribute_permissions = {}
34
+ for attribute in self.__class__.__dict__:
35
+ if not attribute.startswith("__"):
36
+ attribute_permissions[attribute] = getattr(self.__class__, attribute)
37
+ return attribute_permissions
38
+
39
+ @classmethod
40
+ def check(
41
+ cls,
42
+ data: dict[str, Any],
43
+ request_user: AbstractUser | AnonymousUser | Any,
44
+ ) -> None:
45
+ """
46
+ Check if the user has permission to perform the mutation based on the provided data.
47
+ Raises:
48
+ PermissionError: If the user does not have permission.
49
+ """
50
+ errors = []
51
+ Permission = cls(data, BasePermission.getUserWithId(request_user))
52
+ for key in data:
53
+ if not Permission.checkPermission(key):
54
+ errors.append(
55
+ f"Permission denied for {key} with value {data[key]} for user {request_user}"
56
+ )
57
+ if errors:
58
+ raise PermissionError(f"Permission denied with errors: {errors}")
59
+
60
+ def checkPermission(
61
+ self,
62
+ attribute: str,
63
+ ) -> bool:
64
+
65
+ has_attribute_permissions = attribute in self.__attribute_permissions
66
+
67
+ if not has_attribute_permissions:
68
+ last_result = self.__overall_result
69
+ if last_result is not None:
70
+ return last_result
71
+ attribute_permission = True
72
+ else:
73
+ attribute_permission = self.__checkSpecificPermission(
74
+ self.__attribute_permissions[attribute]
75
+ )
76
+
77
+ permission = self.__checkSpecificPermission(self.__mutate__)
78
+ self.__overall_result = permission
79
+ return permission and attribute_permission
80
+
81
+ def __checkSpecificPermission(
82
+ self,
83
+ permissions: list[str],
84
+ ) -> bool:
85
+ for permission in permissions:
86
+ if validatePermissionString(permission, self.data, self.request_user):
87
+ return True
88
+ return False
@@ -24,10 +24,6 @@ class PermissionDataManager(Generic[GeneralManagerData]):
24
24
  self.getData = (
25
25
  lambda name, permission_data=permission_data: permission_data.get(name)
26
26
  )
27
- if manager is None:
28
- raise ValueError(
29
- "Manager must be provided if permission_data is a dict"
30
- )
31
27
  self._manager = manager
32
28
  else:
33
29
  raise TypeError(
@@ -48,7 +44,7 @@ class PermissionDataManager(Generic[GeneralManagerData]):
48
44
  return self._permission_data
49
45
 
50
46
  @property
51
- def manager(self) -> type[GeneralManagerData]:
47
+ def manager(self) -> type[GeneralManagerData] | None:
52
48
  return self._manager
53
49
 
54
50
  def __getattr__(self, name: str) -> Any:
@@ -0,0 +1,35 @@
1
+ from general_manager.permission.permissionChecks import (
2
+ permission_functions,
3
+ )
4
+ from general_manager.permission.permissionDataManager import PermissionDataManager
5
+ from django.contrib.auth.models import AbstractUser, AnonymousUser
6
+
7
+ from general_manager.manager.generalManager import GeneralManager
8
+ from general_manager.manager.meta import GeneralManagerMeta
9
+
10
+
11
+ def validatePermissionString(
12
+ permission: str,
13
+ data: PermissionDataManager | GeneralManager | GeneralManagerMeta,
14
+ request_user: AbstractUser | AnonymousUser,
15
+ ) -> bool:
16
+ # permission can be a combination of multiple permissions
17
+ # separated by "&" (e.g. "isAuthenticated&isMatchingKeyAccount")
18
+ # this means that all sub_permissions must be true
19
+ def _validateSinglePermission(
20
+ permission: str,
21
+ ) -> bool:
22
+ permission_function, *config = permission.split(":")
23
+ if permission_function not in permission_functions:
24
+ raise ValueError(f"Permission {permission} not found")
25
+
26
+ return permission_functions[permission_function]["permission_method"](
27
+ data, request_user, config
28
+ )
29
+
30
+ return all(
31
+ [
32
+ _validateSinglePermission(sub_permission)
33
+ for sub_permission in permission.split("&")
34
+ ]
35
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: GeneralManager
3
- Version: 0.10.6
3
+ Version: 0.11.1
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-Expression: MIT
@@ -1,7 +1,7 @@
1
1
  general_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  general_manager/apps.py,sha256=ii-48klC2kBRjxtdQU9-RtPq66lZ8fDa7BmqyH-mbzk,9904
3
3
  general_manager/api/graphql.py,sha256=jpdtaep_ql94krsVvGJRpuqT-MR6iZCFpezeJAl4Mlw,33382
4
- general_manager/api/mutation.py,sha256=RvKp4OnV70q4Dqmu407Cd0FXgD12WdbRgzYCS4qd_bg,5990
4
+ general_manager/api/mutation.py,sha256=41BvkY4qYU_Kq2R6GUK14AnU7sSTydw4tSw1f4CAlxI,5954
5
5
  general_manager/api/property.py,sha256=oc93p1P8dcIvrNorRuqD1EJVsd6eYttYhZuAS0s28gs,696
6
6
  general_manager/bucket/baseBucket.py,sha256=UjEai7cVxgDJysoVY2-7s3YULW7bi-sx2mPqRKFWyTw,7728
7
7
  general_manager/bucket/calculationBucket.py,sha256=a41YVdfhxKf_gpWlRKjXYmS1YuO-6VC0hn60RyLKByU,18777
@@ -19,12 +19,12 @@ general_manager/factory/factoryMethods.py,sha256=9Bag891j0XHe3dUBAFi7gUKcKeUwcBZ
19
19
  general_manager/interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  general_manager/interface/baseInterface.py,sha256=cFsDU-nhj_O6Gir3eO0ukGKNn9Pplhe6gEMccHNi4O0,8648
21
21
  general_manager/interface/calculationInterface.py,sha256=fTD3WQpsn3ImaxGW5S-JwVJyJJPoPp2mR6lAambdB8U,4755
22
- general_manager/interface/databaseBasedInterface.py,sha256=_E4HoHmU8A_5_voSpeGp8AkiWbHGF3ZY1zO9phIezh8,21647
22
+ general_manager/interface/databaseBasedInterface.py,sha256=F-a6P9rvPUmlcoZw1rKSWEAzbYGccQRL6uokv6C46qo,21782
23
23
  general_manager/interface/databaseInterface.py,sha256=rhKVXhg0ztdIxKikTWtgjrkA7cwZTOYlEsRh0RWajDQ,6732
24
24
  general_manager/interface/models.py,sha256=gGYW5f1AUBpBakV3O0qsZwqMiWxZGdKRYXWaCBjt1oI,3334
25
25
  general_manager/interface/readOnlyInterface.py,sha256=TkfbOeaa2wCq5kCv0a3IwJWcYOTVbtNsdNWmGAz0Mns,11217
26
26
  general_manager/manager/__init__.py,sha256=l3RYp62aEhj3Y975_XUTIzo35LUnkTJHkb_hgChnXXI,111
27
- general_manager/manager/generalManager.py,sha256=24eRdJW8BYPchYySxFNMywwk9LOhrab5Y4QiQWXZWT8,8730
27
+ general_manager/manager/generalManager.py,sha256=4Qn9TYpZpqh5qC95BEAQhpiZgDrRXrAJjO2BbbXUdNg,9129
28
28
  general_manager/manager/groupManager.py,sha256=8dpZUfm7aFL4lraUWv4qbbDRClQZaYxw4prclhBZYZs,4367
29
29
  general_manager/manager/input.py,sha256=-pJXGJ-g2-OxZfl4Buj3mQkf05fN4p8MsR2Lh9BQcEo,3208
30
30
  general_manager/manager/meta.py,sha256=-9celpo-oZmkTb8TnHfvcd_4XWTy1cn2UO-jp13NFmQ,6387
@@ -32,11 +32,13 @@ general_manager/measurement/__init__.py,sha256=X97meFujBldE5v0WMF7SmKeGpC5R0JTcz
32
32
  general_manager/measurement/measurement.py,sha256=T3R2qqDkqPY_91DzN0zJF4ug62aCNyN5-75WE9Tm7ko,16067
33
33
  general_manager/measurement/measurementField.py,sha256=ixkZR_t--HGckK2iYi6sXOOa_Vbni4QRxK5Ngmy5YKc,6863
34
34
  general_manager/permission/__init__.py,sha256=5UlDERN60Vn8obGVkT-cOM8kHjzmoxgK5w5FgTCDhGE,59
35
- general_manager/permission/basePermission.py,sha256=14iKo6qVmaUdg1sAz-gSZyNtpVKAAapIhutVAMDf93c,6056
35
+ general_manager/permission/basePermission.py,sha256=DeiAX2sQQhtdquO13jys2MSkp0kPdg2oo7PSqB9q5Bw,5653
36
36
  general_manager/permission/fileBasedPermission.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- general_manager/permission/managerBasedPermission.py,sha256=VgZJVgkXWdaLk6K7c0kYxTbELnTYK6UY9mMuKH5u64w,5728
37
+ general_manager/permission/managerBasedPermission.py,sha256=DmoSLZ5m9zOyqAotKFDUbr7dahhsvS3K63kI8ChURN0,5491
38
+ general_manager/permission/mutationPermission.py,sha256=tYeunZQOJiUkQIqW0lU0KafwJKaTkrHTYKi7zV0wqHI,2972
38
39
  general_manager/permission/permissionChecks.py,sha256=T-9khBqiwM4ASBdey9p07sC_xgzceIU9EAE0reukguM,1655
39
- general_manager/permission/permissionDataManager.py,sha256=Ji7fsnuaKTa6M8yzCGyzrIHyGa_ZvqJM7sXR97-uTrA,1937
40
+ general_manager/permission/permissionDataManager.py,sha256=EzF-XKJZKvKdNpF77ATYJ_VXSF09L-HTFe8h8p-JdB4,1784
41
+ general_manager/permission/utils.py,sha256=_cznQLk-wmK35pqs3dtgY6I94Ahyz_bTSmpkT0-MQnI,1277
40
42
  general_manager/rule/__init__.py,sha256=4Har5cfPD1fmOsilTDod-ZUz3Com-tkl58jz7yY4fD0,23
41
43
  general_manager/rule/handler.py,sha256=z8SFHTIZ0LbLh3fV56Mud0V4_OvWkqJjlHvFqau7Qfk,7334
42
44
  general_manager/rule/rule.py,sha256=3FVCKGL7BTVoStdgOTdWQwuoVRIxAIAilV4VOzouDpc,10759
@@ -49,8 +51,8 @@ general_manager/utils/makeCacheKey.py,sha256=UlFsxHXgsYy69AAelkF6GDvY4h7AImT2bBn
49
51
  general_manager/utils/noneToZero.py,sha256=KfQtMQnrT6vsYST0K7lv6pVujkDcK3XL8czHYOhgqKQ,539
50
52
  general_manager/utils/pathMapping.py,sha256=nrz5owQg2a69Yig1eCXorR9U0NSw7NmBAk5OkeoHTdA,6842
51
53
  general_manager/utils/testing.py,sha256=ElZ8p4iZHxsHjDN8Lm5TmI6527CW747ltDOmtY6gAhk,11872
52
- generalmanager-0.10.6.dist-info/licenses/LICENSE,sha256=YGFm0ieb4KpkMRRt2qnWue6uFh0cUMtobwEBkHwajhc,1450
53
- generalmanager-0.10.6.dist-info/METADATA,sha256=SrGKXDLzzs69odSsBd3t7pE4DIuVS9lmlkyhbnAYgus,6206
54
- generalmanager-0.10.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
55
- generalmanager-0.10.6.dist-info/top_level.txt,sha256=sTDtExP9ga-YP3h3h42yivUY-A2Q23C2nw6LNKOho4I,16
56
- generalmanager-0.10.6.dist-info/RECORD,,
54
+ generalmanager-0.11.1.dist-info/licenses/LICENSE,sha256=YGFm0ieb4KpkMRRt2qnWue6uFh0cUMtobwEBkHwajhc,1450
55
+ generalmanager-0.11.1.dist-info/METADATA,sha256=Sam_MBu9NlrJgwolfC2jmDM5eq8kHOMXy-bhdyiEXkE,6206
56
+ generalmanager-0.11.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ generalmanager-0.11.1.dist-info/top_level.txt,sha256=sTDtExP9ga-YP3h3h42yivUY-A2Q23C2nw6LNKOho4I,16
58
+ generalmanager-0.11.1.dist-info/RECORD,,