Coverage for /home/runner/work/viur-core/viur-core/viur/src/viur/core/skeleton/utils.py: 19%

60 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-09-25 15:02 +0000

1import typing as t 

2 

3from .meta import MetaBaseSkel, Skeleton_Cls 

4 

5if t.TYPE_CHECKING: 5 ↛ 6line 5 didn't jump to line 6 because the condition on line 5 was never true

6 from . import RefSkel, Skeleton, SkeletonInstance 

7 

8 

9def skeletonByKind(kindName: str) -> t.Type["Skeleton"]: 

10 """ 

11 Returns the Skeleton-Class for the given kindName. That skeleton must exist, otherwise an exception is raised. 

12 :param kindName: The kindname to retreive the skeleton for 

13 :return: The skeleton-class for that kind 

14 """ 

15 assert kindName in MetaBaseSkel._skelCache, f"Unknown skeleton {kindName=}" 

16 return MetaBaseSkel._skelCache[kindName] 

17 

18 

19def listKnownSkeletons() -> list[str]: 

20 """ 

21 :return: A list of all known kindnames (all kindnames for which a skeleton is defined) 

22 """ 

23 return sorted(MetaBaseSkel._skelCache.keys()) 

24 

25 

26def iterAllSkelClasses() -> t.Iterable["Skeleton"]: 

27 """ 

28 :return: An iterator that yields each Skeleton-Class once. (Only top-level skeletons are returned, so no 

29 RefSkel classes will be included) 

30 """ 

31 for cls in list(MetaBaseSkel._allSkelClasses): # We'll add new classes here during setSystemInitialized() 

32 yield cls 

33 

34 

35class SkelList(list, t.Generic[Skeleton_Cls]): 

36 """A typed list of :class:`SkeletonInstance` objects with query metadata. 

37 

38 Returned by ``Skel().all()...fetch()`` constructs. The generic parameter 

39 mirrors the one on :class:`SkeletonInstance` so that the element type flows 

40 through without manual casts:: 

41 

42 result: SkelList[ProductSkel] = ProductSkel().all().fetch(10) 

43 for skel in result: 

44 # skel is SkeletonInstance[ProductSkel] 

45 print(skel["price"]) 

46 

47 Without the type parameter the class behaves exactly as before. 

48 

49 :ivar baseSkel: The base skeleton instance used to construct this list. 

50 :ivar getCursor: Callable returning the datastore cursor for pagination. 

51 :ivar get_orders: Callable returning the active query ordering. 

52 :ivar renderPreparation: Render-preparation callback, set by renderers. 

53 :ivar customQueryInfo: Arbitrary extra metadata attached by query helpers. 

54 """ 

55 

56 __slots__ = ( 

57 "baseSkel", 

58 "customQueryInfo", 

59 "getCursor", 

60 "get_orders", 

61 "renderPreparation", 

62 ) 

63 

64 def __init__(self, skel: t.Optional["SkeletonInstance[Skeleton_Cls]"] = None, *items): 

65 """ 

66 :param baseSkel: The baseclass for all entries in this list 

67 """ 

68 super().__init__() 

69 self.baseSkel: "SkeletonInstance[Skeleton_Cls] | dict" = skel or {} 

70 self.getCursor = lambda: None 

71 self.get_orders = lambda: None 

72 self.renderPreparation = None 

73 self.customQueryInfo = {} 

74 

75 self.extend(items) 

76 

77 

78# FIXME: REMOVE WITH VIUR4 

79def remove_render_preparation_deep(skel: t.Any) -> t.Any: 

80 """Remove renderPreparation of nested skeletons 

81 

82 _refSkelCache can have renderPreparation too. 

83 """ 

84 from .instance import SkeletonInstance 

85 

86 if isinstance(skel, SkeletonInstance): 

87 skel.renderPreparation = None 

88 for _, value in skel.items(yieldBoneValues=True): 

89 remove_render_preparation_deep(value) 

90 elif isinstance(skel, dict): 

91 for value in skel.values(): 

92 remove_render_preparation_deep(value) 

93 elif isinstance(skel, (list, tuple, set)): 

94 for value in skel: 

95 remove_render_preparation_deep(value) 

96 

97 return skel 

98 

99 

100def without_render_preparation(skel: "SkeletonInstance", full_clone: bool = False) -> "SkeletonInstance": 

101 """Return the SkeletonInstance without renderPreparation. 

102 

103 This method is useful (and unfortunately necessary due to the ViUR design) 

104 if you call python methods from the jinja template that should work on the 

105 `SkeletonInstance.accessedValues` and not on the `SkeletonInstance.renderAccessedValues`. 

106 

107 If the SkeletonInstance does not have renderPreparation, it will be returned as is. 

108 If renderPreparation is enabled, a new SkeletonInstance is created. 

109 However, unless `full_clone` is True, the SkeletonInstance will use the 

110 identical objects as the source skeleton. It just "removes" the 

111 "renderPreparation mode" and keep it for the source skel enabled. 

112 """ 

113 from . import SkeletonInstance 

114 if skel.renderPreparation is not None: 114 ↛ 115line 114 didn't jump to line 115 because the condition on line 114 was never true

115 if full_clone: 

116 skel = skel.clone() 

117 else: 

118 src_skel = skel 

119 # Create a new SkeletonInstance with the same object, 

120 # but without enabled renderPreparation 

121 skel = SkeletonInstance(src_skel.skeletonCls, bone_map=src_skel.boneMap) 

122 skel.accessedValues = src_skel.accessedValues 

123 skel.dbEntity = src_skel.dbEntity 

124 skel.errors = src_skel.errors 

125 skel.is_cloned = src_skel.is_cloned 

126 assert skel.renderPreparation is None 

127 skel = remove_render_preparation_deep(skel) 

128 return skel 

129 

130 

131def is_skeletoninstance_of( 

132 obj: t.Any, 

133 skel_cls: type["Skeleton"], 

134 *, 

135 accept_ref_skel: bool = True, 

136) -> bool: 

137 """ 

138 Checks whether an object is an SkeletonInstance that belongs to a specific Skeleton class. 

139 

140 :param obj: The object to check. 

141 :param skel_cls: The skeleton class that will be checked against ``obj``. 

142 :param accept_ref_skel: If True, ``obj`` can also be just a RefSkelFor``skel_cls``. 

143 If False, no ``RefSkel`` is accepted. 

144 """ 

145 from . import RefSkel, Skeleton, SkeletonInstance 

146 

147 if not issubclass(skel_cls, Skeleton): 

148 raise TypeError(f"{skel_cls=} is not a Skeleton.") 

149 

150 if not isinstance(obj, SkeletonInstance): 

151 return False 

152 if issubclass(obj.skeletonCls, skel_cls): 

153 return True 

154 if accept_ref_skel and issubclass(obj.skeletonCls, RefSkel) and issubclass(obj.skeletonCls.skeletonCls, skel_cls): 

155 return True 

156 return False