From 487246a9e82717e7929a700d9c103b73f57f16d0 Mon Sep 17 00:00:00 2001 From: Martin Lehmann Date: Mon, 25 Sep 2023 14:58:34 +0200 Subject: [PATCH 1/4] feat(model): Add a `get_children` method This new method helps with introspecting the model, and lets library clients more easily interact with objects of different types without having to constantly cross-reference the metamodel. --- capellambse/model/common/element.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/capellambse/model/common/element.py b/capellambse/model/common/element.py index 9a415074d..9d3281dc2 100644 --- a/capellambse/model/common/element.py +++ b/capellambse/model/common/element.py @@ -401,6 +401,31 @@ def _wrap_short_html(self, content: str) -> markupsafe.Markup: def _repr_html_(self) -> str: return self.__html__() + def get_children(self) -> MixedElementList: + """Return all model elements that are children of this one. + + The list returned from this method contains all model element + that are direct descendants of this element, with regard to the + XML hierarchy (usually referred to as "owned objects" in the + metamodel). + """ + elements: list[etree._Element] = [] + for attr in dir(self): + if attr.startswith("_"): + continue + acc = getattr(type(self), attr, None) + if ( + # pylint: disable-next=unidiomatic-typecheck + type(acc) is accessors.DirectProxyAccessor + and not acc.rootelem + or isinstance(acc, accessors.RoleTagAccessor) + ): + if acc.aslist is None: + elements.append(getattr(self, attr)._element) + else: + elements.extend(i._element for i in getattr(self, attr)) + return MixedElementList(self._model, elements) + if t.TYPE_CHECKING: def __getattr__(self, attr: str) -> t.Any: From 972a909f4914de2a8bde6646a23783e85dc54a4c Mon Sep 17 00:00:00 2001 From: Martin Lehmann Date: Fri, 29 Sep 2023 10:11:32 +0200 Subject: [PATCH 2/4] fix(model): EnumerationReference.name isn't mandatory --- capellambse/model/crosslayer/information/datavalue.py | 1 - 1 file changed, 1 deletion(-) diff --git a/capellambse/model/crosslayer/information/datavalue.py b/capellambse/model/crosslayer/information/datavalue.py index 61eaf1f8e..4d9ead795 100644 --- a/capellambse/model/crosslayer/information/datavalue.py +++ b/capellambse/model/crosslayer/information/datavalue.py @@ -67,6 +67,5 @@ def __eq__(self, other: object) -> bool: @c.xtype_handler(None) class EnumerationReference(c.GenericElement): - name = c.AttributeProperty("name", returntype=str) type = c.AttrProxyAccessor(c.GenericElement, "abstractType") value = c.AttrProxyAccessor(c.GenericElement, "referencedValue") From c843bc2ae89dc36ce4d7d08f32fc8b31fd0a0c35 Mon Sep 17 00:00:00 2001 From: Jamil RAICHOUNI Date: Fri, 29 Sep 2023 10:17:40 +0200 Subject: [PATCH 3/4] fix: Ignore None here --- capellambse/model/common/element.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/capellambse/model/common/element.py b/capellambse/model/common/element.py index 9d3281dc2..fa0431863 100644 --- a/capellambse/model/common/element.py +++ b/capellambse/model/common/element.py @@ -421,7 +421,8 @@ def get_children(self) -> MixedElementList: or isinstance(acc, accessors.RoleTagAccessor) ): if acc.aslist is None: - elements.append(getattr(self, attr)._element) + if getattr(self, attr) is not None: + elements.append(getattr(self, attr)._element) else: elements.extend(i._element for i in getattr(self, attr)) return MixedElementList(self._model, elements) From 070e82d4ab333418a3f7deea7ed8e27da1a31f02 Mon Sep 17 00:00:00 2001 From: Jamil RAICHOUNI Date: Fri, 20 Oct 2023 16:43:52 +0200 Subject: [PATCH 4/4] feat: Add `(has|iter)_children` methods This can improve performance when one just needs to know, if an item has children. --- capellambse/model/common/element.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/capellambse/model/common/element.py b/capellambse/model/common/element.py index fa0431863..2f32713b3 100644 --- a/capellambse/model/common/element.py +++ b/capellambse/model/common/element.py @@ -404,12 +404,24 @@ def _repr_html_(self) -> str: def get_children(self) -> MixedElementList: """Return all model elements that are children of this one. - The list returned from this method contains all model element - that are direct descendants of this element, with regard to the - XML hierarchy (usually referred to as "owned objects" in the - metamodel). + .... seealso:: + + :meth:`iter_children` + """ + return MixedElementList( + self._model, [ele._element for ele in self.iter_children()] + ) + + def has_children(self) -> bool: + return next(self.iter_children(), None) is not None + + def iter_children(self) -> t.Iterator[GenericElement]: + """Yield model elements that are children of this one. + + The elements yielded from this generator are the direct + descendants of this element, with regard to the XML hierarchy + (usually referred to as "owned objects" in the metamodel). """ - elements: list[etree._Element] = [] for attr in dir(self): if attr.startswith("_"): continue @@ -422,10 +434,9 @@ def get_children(self) -> MixedElementList: ): if acc.aslist is None: if getattr(self, attr) is not None: - elements.append(getattr(self, attr)._element) + yield getattr(self, attr) else: - elements.extend(i._element for i in getattr(self, attr)) - return MixedElementList(self._model, elements) + yield from getattr(self, attr) if t.TYPE_CHECKING: @@ -782,7 +793,7 @@ def __html__(self) -> markupsafe.Markup: return markupsafe.Markup("

(Empty list)

") fragments = ['
    '] - for i in self: + for i in [i for i in self if i is not None]: fragments.append(f"
  1. {i._short_html_()}
  2. ") fragments.append("
") return markupsafe.Markup("".join(fragments))