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

60 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-25 14:23 +0000

1import typing as t 

2 

3from .meta import MetaBaseSkel 

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): 

36 """ 

37 This class is used to hold multiple skeletons together with other, commonly used information. 

38 

39 SkelLists are returned by Skel().all()...fetch()-constructs and provide additional information 

40 about the data base query, for fetching additional entries. 

41 

42 :ivar cursor: Holds the cursor within a query. 

43 :vartype cursor: str 

44 """ 

45 

46 __slots__ = ( 

47 "baseSkel", 

48 "customQueryInfo", 

49 "getCursor", 

50 "get_orders", 

51 "renderPreparation", 

52 ) 

53 

54 def __init__(self, skel, *items): 

55 """ 

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

57 """ 

58 super().__init__() 

59 self.baseSkel = skel or {} 

60 self.getCursor = lambda: None 

61 self.get_orders = lambda: None 

62 self.renderPreparation = None 

63 self.customQueryInfo = {} 

64 

65 self.extend(items) 

66 

67 

68# FIXME: REMOVE WITH VIUR4 

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

70 """Remove renderPreparation of nested skeletons 

71 

72 _refSkelCache can have renderPreparation too. 

73 """ 

74 from .instance import SkeletonInstance 

75 

76 if isinstance(skel, SkeletonInstance): 

77 skel.renderPreparation = None 

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

79 remove_render_preparation_deep(value) 

80 elif isinstance(skel, dict): 

81 for value in skel.values(): 

82 remove_render_preparation_deep(value) 

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

84 for value in skel: 

85 remove_render_preparation_deep(value) 

86 

87 return skel 

88 

89 

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

91 """Return the SkeletonInstance without renderPreparation. 

92 

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

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

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

96 

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

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

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

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

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

102 """ 

103 from . import SkeletonInstance 

104 if skel.renderPreparation is not None: 

105 if full_clone: 

106 skel = skel.clone() 

107 else: 

108 src_skel = skel 

109 # Create a new SkeletonInstance with the same object, 

110 # but without enabled renderPreparation 

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

112 skel.accessedValues = src_skel.accessedValues 

113 skel.dbEntity = src_skel.dbEntity 

114 skel.errors = src_skel.errors 

115 skel.is_cloned = src_skel.is_cloned 

116 assert skel.renderPreparation is None 

117 skel = remove_render_preparation_deep(skel) 

118 return skel 

119 

120 

121def is_skeletoninstance_of( 

122 obj: t.Any, 

123 skel_cls: type["Skeleton"], 

124 *, 

125 accept_ref_skel: bool = True, 

126) -> bool: 

127 """ 

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

129 

130 :param obj: The object to check. 

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

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

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

134 """ 

135 from . import RefSkel, Skeleton, SkeletonInstance 

136 

137 if not issubclass(skel_cls, Skeleton): 

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

139 

140 if not isinstance(obj, SkeletonInstance): 

141 return False 

142 if issubclass(obj.skeletonCls, skel_cls): 

143 return True 

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

145 return True 

146 return False