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
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-25 15:02 +0000
1import typing as t
3from .meta import MetaBaseSkel, Skeleton_Cls
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
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]
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())
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
35class SkelList(list, t.Generic[Skeleton_Cls]):
36 """A typed list of :class:`SkeletonInstance` objects with query metadata.
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::
42 result: SkelList[ProductSkel] = ProductSkel().all().fetch(10)
43 for skel in result:
44 # skel is SkeletonInstance[ProductSkel]
45 print(skel["price"])
47 Without the type parameter the class behaves exactly as before.
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 """
56 __slots__ = (
57 "baseSkel",
58 "customQueryInfo",
59 "getCursor",
60 "get_orders",
61 "renderPreparation",
62 )
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 = {}
75 self.extend(items)
78# FIXME: REMOVE WITH VIUR4
79def remove_render_preparation_deep(skel: t.Any) -> t.Any:
80 """Remove renderPreparation of nested skeletons
82 _refSkelCache can have renderPreparation too.
83 """
84 from .instance import SkeletonInstance
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)
97 return skel
100def without_render_preparation(skel: "SkeletonInstance", full_clone: bool = False) -> "SkeletonInstance":
101 """Return the SkeletonInstance without renderPreparation.
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`.
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
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.
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
147 if not issubclass(skel_cls, Skeleton):
148 raise TypeError(f"{skel_cls=} is not a Skeleton.")
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