From 4ad0d3c3dd51c82da58c4ed8cb47a4f155a2a15d Mon Sep 17 00:00:00 2001 From: Leon Huang Date: Tue, 18 Nov 2025 09:42:19 +0100 Subject: [PATCH 1/9] Setter and getter for MultiLangType added --- sdk/basyx/aas/model/base.py | 63 +++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index 35ccad5a..609a2c4d 100644 --- a/sdk/basyx/aas/model/base.py +++ b/sdk/basyx/aas/model/base.py @@ -613,10 +613,10 @@ class Referable(HasExtension, metaclass=abc.ABCMeta): @abc.abstractmethod def __init__(self): super().__init__() - self._id_short: Optional[NameType] = None - self.display_name: Optional[MultiLanguageNameType] = dict() + self._id_short: Optional[NameType] = None + self.display_name: Optional[MultiLanguageNameType] = None self._category: Optional[NameType] = None - self.description: Optional[MultiLanguageTextType] = dict() + self.description: Optional[MultiLanguageTextType] = None # We use a Python reference to the parent Namespace instead of a Reference Object, as specified. This allows # simpler and faster navigation/checks and it has no effect in the serialized data formats anyway. self.parent: Optional[UniqueIdShortNamespace] = None @@ -827,6 +827,63 @@ def _set_id_short(self, id_short: Optional[NameType]): # Redundant to the line above. However, this way, we make sure that we really update the _id_short self._id_short = id_short + def _check_multiLanguageNameType(self, value): + """ + Check that the given type is either None or of type MultiLanguageNameType + + :param value: The display name to check + :raises TypeError: If the type is not :datatype:`MultiLanguageNameType` or `None` + """ + + if value is not None and not isinstance(value, MultiLanguageNameType): + raise TypeError( + f"display_name must be of type MultiLanguageNameType, but got {type(value)}" + ) + + def _get_display_name(self) -> Optional[MultiLanguageNameType]: + return self._display_name + + def _set_display_name(self, display_name: Optional[MultiLanguageNameType]): + """ + Check the input type and then set the display_name + + :param display_name: MultiLanguageNameType for the display name of the element + :raises TypeError: if the type is not correct + """ + self._check_multiLanguageNameType(display_name) + self._display_name = display_name + + display_name = property(_get_display_name, _set_display_name) + + + def _check_MultiLanguageTextType(self, value): + """ + Check that the given type is either None or of type MultiLanguageTextType + + :param value: The description to check + :raises TypeError: if the type is not :datatype:`MultiLanguageTextType` or `None` + """ + + if value is not None and not isinstance(value, MultiLanguageTextType): + raise TypeError( + f"description must be of type MultiLanguageTextType, but got {type(value)}" + ) + + def _get_description(self) -> Optional[MultiLanguageTextType]: + return self._description + + def _set_description(self, description: Optional[MultiLanguageTextType]): + """ + Check the input type and then set the description + + :param description: MultiLanguageTextType for the description of the element + :raises TypeError: if the type is not correct + """ + self._check_MultiLanguageTextType(description) + self._description = description + + description = property(_get_description, _set_description) + def update_from(self, other: "Referable"): """ Internal function to update the object's attributes from a different version of the exact same object. From 5ead6617e9305ca40659385179a472736584a3e0 Mon Sep 17 00:00:00 2001 From: Leon Huang Date: Tue, 18 Nov 2025 09:42:36 +0100 Subject: [PATCH 2/9] TypeErrors changed --- sdk/basyx/aas/model/base.py | 77 +++++++++++++++---------------------- 1 file changed, 30 insertions(+), 47 deletions(-) diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index 609a2c4d..cd2ba247 100644 --- a/sdk/basyx/aas/model/base.py +++ b/sdk/basyx/aas/model/base.py @@ -1,5 +1,5 @@ # Copyright (c) 2025 the Eclipse BaSyx Authors -# +# # This program and the accompanying materials are made available under the terms of the MIT License, available in # the LICENSE file of this project. # @@ -621,6 +621,10 @@ def __init__(self): # simpler and faster navigation/checks and it has no effect in the serialized data formats anyway. self.parent: Optional[UniqueIdShortNamespace] = None + # Initialize with empty objects to avoid ValueError + self.display_name = MultiLanguageNameType({}) + self.description = MultiLanguageTextType({}) + def __repr__(self) -> str: root = self.get_identifiable_root() try: @@ -825,64 +829,43 @@ def _set_id_short(self, id_short: Optional[NameType]): for set_ in set_add_list: set_.add(self) # Redundant to the line above. However, this way, we make sure that we really update the _id_short - self._id_short = id_short + self._id_short = id_short - def _check_multiLanguageNameType(self, value): - """ - Check that the given type is either None or of type MultiLanguageNameType - - :param value: The display name to check - :raises TypeError: If the type is not :datatype:`MultiLanguageNameType` or `None` - """ + def _check_multiLanguageNameType(self, value: Optional[MultiLanguageNameType]) -> None: + """Ensure value is None or a MultiLanguageNameType.""" if value is not None and not isinstance(value, MultiLanguageNameType): raise TypeError( f"display_name must be of type MultiLanguageNameType, but got {type(value)}" - ) - - def _get_display_name(self) -> Optional[MultiLanguageNameType]: - return self._display_name - - def _set_display_name(self, display_name: Optional[MultiLanguageNameType]): - """ - Check the input type and then set the display_name + ) - :param display_name: MultiLanguageNameType for the display name of the element - :raises TypeError: if the type is not correct - """ - self._check_multiLanguageNameType(display_name) - self._display_name = display_name + @property + def display_name(self) -> Optional[MultiLanguageNameType]: + """Display name of the element (MultiLanguageNameType).""" + return self._display_name - display_name = property(_get_display_name, _set_display_name) + @display_name.setter + def display_name(self, value: Optional[MultiLanguageNameType]) -> None: + self._check_multiLanguageNameType(value) + self._display_name = value - def _check_MultiLanguageTextType(self, value): - """ - Check that the given type is either None or of type MultiLanguageTextType - - :param value: The description to check - :raises TypeError: if the type is not :datatype:`MultiLanguageTextType` or `None` - """ - - if value is not None and not isinstance(value, MultiLanguageTextType): + def _check_MultiLanguageTextType(self, value: Optional[MultiLanguageTextType]) -> None: + """Ensure value is None or a MultiLanguageTextType.""" + if value is not None and not isinstance(value, MultiLanguageTextType): raise TypeError( - f"description must be of type MultiLanguageTextType, but got {type(value)}" + f"description must be of type MultiLanguageTextType, but got {type(value)}" ) - - def _get_description(self) -> Optional[MultiLanguageTextType]: - return self._description - - def _set_description(self, description: Optional[MultiLanguageTextType]): - """ - Check the input type and then set the description - :param description: MultiLanguageTextType for the description of the element - :raises TypeError: if the type is not correct - """ - self._check_MultiLanguageTextType(description) - self._description = description + @property + def description(self) -> Optional[MultiLanguageTextType]: + """Description of the element (MultiLanguageTextType).""" + return self._description - description = property(_get_description, _set_description) + @description.setter + def description(self, value: Optional[MultiLanguageTextType]) -> None: + self._check_MultiLanguageTextType(value) + self._description = value def update_from(self, other: "Referable"): """ @@ -895,7 +878,7 @@ def update_from(self, other: "Referable"): """ for name in dir(other): # Skip private and protected attributes - if name.startswith('_'): + if name.startswith('_'): continue # Do not update 'parent', 'namespace_element_sets' From b97f1b68f22f12929a921433ff6cde2589baf85a Mon Sep 17 00:00:00 2001 From: Leon Huang Date: Tue, 18 Nov 2025 09:42:43 +0100 Subject: [PATCH 3/9] TypeErrors changed refactor --- sdk/basyx/aas/model/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index cd2ba247..4ef4473a 100644 --- a/sdk/basyx/aas/model/base.py +++ b/sdk/basyx/aas/model/base.py @@ -614,9 +614,9 @@ class Referable(HasExtension, metaclass=abc.ABCMeta): def __init__(self): super().__init__() self._id_short: Optional[NameType] = None - self.display_name: Optional[MultiLanguageNameType] = None + self._display_name: Optional[MultiLanguageNameType] = None self._category: Optional[NameType] = None - self.description: Optional[MultiLanguageTextType] = None + self._description: Optional[MultiLanguageTextType] = None # We use a Python reference to the parent Namespace instead of a Reference Object, as specified. This allows # simpler and faster navigation/checks and it has no effect in the serialized data formats anyway. self.parent: Optional[UniqueIdShortNamespace] = None From 581ef6c418b79caf5899dda7cf880b2670f9f48a Mon Sep 17 00:00:00 2001 From: Leon Huang Date: Tue, 18 Nov 2025 09:42:46 +0100 Subject: [PATCH 4/9] whitespacing --- sdk/basyx/aas/model/base.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index 4ef4473a..633f25fe 100644 --- a/sdk/basyx/aas/model/base.py +++ b/sdk/basyx/aas/model/base.py @@ -1,5 +1,5 @@ # Copyright (c) 2025 the Eclipse BaSyx Authors -# +# # This program and the accompanying materials are made available under the terms of the MIT License, available in # the LICENSE file of this project. # @@ -613,7 +613,7 @@ class Referable(HasExtension, metaclass=abc.ABCMeta): @abc.abstractmethod def __init__(self): super().__init__() - self._id_short: Optional[NameType] = None + self._id_short: Optional[NameType] = None self._display_name: Optional[MultiLanguageNameType] = None self._category: Optional[NameType] = None self._description: Optional[MultiLanguageTextType] = None @@ -829,8 +829,7 @@ def _set_id_short(self, id_short: Optional[NameType]): for set_ in set_add_list: set_.add(self) # Redundant to the line above. However, this way, we make sure that we really update the _id_short - self._id_short = id_short - + self._id_short = id_short def _check_multiLanguageNameType(self, value: Optional[MultiLanguageNameType]) -> None: """Ensure value is None or a MultiLanguageNameType.""" @@ -848,7 +847,6 @@ def display_name(self) -> Optional[MultiLanguageNameType]: def display_name(self, value: Optional[MultiLanguageNameType]) -> None: self._check_multiLanguageNameType(value) self._display_name = value - def _check_MultiLanguageTextType(self, value: Optional[MultiLanguageTextType]) -> None: """Ensure value is None or a MultiLanguageTextType.""" @@ -878,7 +876,7 @@ def update_from(self, other: "Referable"): """ for name in dir(other): # Skip private and protected attributes - if name.startswith('_'): + if name.startswith('_'): continue # Do not update 'parent', 'namespace_element_sets' From aa532f3a38bbfcf899243f75a72c9e673dca2e5c Mon Sep 17 00:00:00 2001 From: Leon Huang Date: Tue, 18 Nov 2025 09:44:09 +0100 Subject: [PATCH 5/9] fix author for ECA --- sdk/basyx/aas/model/base.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index 633f25fe..c66baa74 100644 --- a/sdk/basyx/aas/model/base.py +++ b/sdk/basyx/aas/model/base.py @@ -621,10 +621,6 @@ def __init__(self): # simpler and faster navigation/checks and it has no effect in the serialized data formats anyway. self.parent: Optional[UniqueIdShortNamespace] = None - # Initialize with empty objects to avoid ValueError - self.display_name = MultiLanguageNameType({}) - self.description = MultiLanguageTextType({}) - def __repr__(self) -> str: root = self.get_identifiable_root() try: @@ -846,8 +842,8 @@ def display_name(self) -> Optional[MultiLanguageNameType]: @display_name.setter def display_name(self, value: Optional[MultiLanguageNameType]) -> None: self._check_multiLanguageNameType(value) - self._display_name = value - + self._display_name = value + def _check_MultiLanguageTextType(self, value: Optional[MultiLanguageTextType]) -> None: """Ensure value is None or a MultiLanguageTextType.""" if value is not None and not isinstance(value, MultiLanguageTextType): From 14dd6b3f5f5755fecbaf1c5cd5477da5b6994ad0 Mon Sep 17 00:00:00 2001 From: Leon Huang Date: Sun, 23 Nov 2025 16:10:38 +0100 Subject: [PATCH 6/9] Refactoring of type check to LangStringSet --- sdk/basyx/aas/model/base.py | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index c66baa74..358fd578 100644 --- a/sdk/basyx/aas/model/base.py +++ b/sdk/basyx/aas/model/base.py @@ -291,7 +291,9 @@ class LangStringSet(MutableMapping[str, str]): """ def __init__(self, dict_: Dict[str, str]): self._dict: Dict[str, str] = {} - + + if not isinstance(dict_, dict): + raise TypeError(f"A {self.__class__.__name__} must be initialized with a dict!, got {type(dict_)}") if len(dict_) < 1: raise ValueError(f"A {self.__class__.__name__} must not be empty!") for ltag in dict_: @@ -827,38 +829,26 @@ def _set_id_short(self, id_short: Optional[NameType]): # Redundant to the line above. However, this way, we make sure that we really update the _id_short self._id_short = id_short - def _check_multiLanguageNameType(self, value: Optional[MultiLanguageNameType]) -> None: - """Ensure value is None or a MultiLanguageNameType.""" - if value is not None and not isinstance(value, MultiLanguageNameType): - raise TypeError( - f"display_name must be of type MultiLanguageNameType, but got {type(value)}" - ) - @property - def display_name(self) -> Optional[MultiLanguageNameType]: + def display_name(self) -> MultiLanguageNameType | None: """Display name of the element (MultiLanguageNameType).""" return self._display_name @display_name.setter - def display_name(self, value: Optional[MultiLanguageNameType]) -> None: - self._check_multiLanguageNameType(value) + def display_name(self, value: MultiLanguageNameType | None) -> None: + if value is not None and not isinstance(value, MultiLanguageNameType): + value = MultiLanguageNameType(value) self._display_name = value - def _check_MultiLanguageTextType(self, value: Optional[MultiLanguageTextType]) -> None: - """Ensure value is None or a MultiLanguageTextType.""" - if value is not None and not isinstance(value, MultiLanguageTextType): - raise TypeError( - f"description must be of type MultiLanguageTextType, but got {type(value)}" - ) - @property - def description(self) -> Optional[MultiLanguageTextType]: + def description(self) -> MultiLanguageTextType | None: """Description of the element (MultiLanguageTextType).""" return self._description @description.setter - def description(self, value: Optional[MultiLanguageTextType]) -> None: - self._check_MultiLanguageTextType(value) + def description(self, value: MultiLanguageTextType | None) -> None: + if value is not None and not isinstance(value, MultiLanguageTextType): + value = MultiLanguageTextType(value) self._description = value def update_from(self, other: "Referable"): From 4dc5a71a43bab9ef2415de5a013a2291cc6846d7 Mon Sep 17 00:00:00 2001 From: Leon Huang Date: Mon, 24 Nov 2025 16:18:38 +0100 Subject: [PATCH 7/9] typehint fix --- sdk/basyx/aas/model/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index 358fd578..45f25291 100644 --- a/sdk/basyx/aas/model/base.py +++ b/sdk/basyx/aas/model/base.py @@ -835,7 +835,7 @@ def display_name(self) -> MultiLanguageNameType | None: return self._display_name @display_name.setter - def display_name(self, value: MultiLanguageNameType | None) -> None: + def display_name(self, value: MultiLanguageNameType | dict| None) -> None: if value is not None and not isinstance(value, MultiLanguageNameType): value = MultiLanguageNameType(value) self._display_name = value @@ -846,7 +846,7 @@ def description(self) -> MultiLanguageTextType | None: return self._description @description.setter - def description(self, value: MultiLanguageTextType | None) -> None: + def description(self, value: MultiLanguageTextType | dict | None) -> None: if value is not None and not isinstance(value, MultiLanguageTextType): value = MultiLanguageTextType(value) self._description = value From 6c0f6bd27331d4a6a8127ca9455da1d04100446a Mon Sep 17 00:00:00 2001 From: Leon Huang Date: Sat, 29 Nov 2025 15:12:04 +0100 Subject: [PATCH 8/9] Implemented same logic for MultiLanguageProperty.value --- sdk/basyx/aas/model/submodel.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sdk/basyx/aas/model/submodel.py b/sdk/basyx/aas/model/submodel.py index 9e7321c4..336ea385 100644 --- a/sdk/basyx/aas/model/submodel.py +++ b/sdk/basyx/aas/model/submodel.py @@ -342,8 +342,17 @@ def __init__(self, super().__init__(id_short, display_name, category, description, parent, semantic_id, qualifier, extension, supplemental_semantic_id, embedded_data_specifications) self.value: Optional[base.MultiLanguageTextType] = value - self.value_id: Optional[base.Reference] = value_id + self.value_id: Optional[base.Reference] = value_id + @property + def value(self) -> base.MultiLanguageTextType | None: + return self._value + + @value.setter + def value(self, value: base.MultiLanguageTextType | dict | None) -> None: + if value is not None and not isinstance(value, base.MultiLanguageTextType): + value = base.MultiLanguageTextType(value) + self._value = value class Range(DataElement): """ From 2a6fc94076fd1e72f60068fc334c0b960f27ac70 Mon Sep 17 00:00:00 2001 From: Leon Huang Date: Thu, 4 Dec 2025 14:26:03 +0100 Subject: [PATCH 9/9] Changed syntax from X | Y to Optional[]/Union[] --- sdk/basyx/aas/model/base.py | 9 ++++----- sdk/basyx/aas/model/submodel.py | 9 +++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py index 45f25291..aa9835c9 100644 --- a/sdk/basyx/aas/model/base.py +++ b/sdk/basyx/aas/model/base.py @@ -291,7 +291,6 @@ class LangStringSet(MutableMapping[str, str]): """ def __init__(self, dict_: Dict[str, str]): self._dict: Dict[str, str] = {} - if not isinstance(dict_, dict): raise TypeError(f"A {self.__class__.__name__} must be initialized with a dict!, got {type(dict_)}") if len(dict_) < 1: @@ -830,23 +829,23 @@ def _set_id_short(self, id_short: Optional[NameType]): self._id_short = id_short @property - def display_name(self) -> MultiLanguageNameType | None: + def display_name(self) -> Optional[MultiLanguageNameType]: """Display name of the element (MultiLanguageNameType).""" return self._display_name @display_name.setter - def display_name(self, value: MultiLanguageNameType | dict| None) -> None: + def display_name(self, value: Union[MultiLanguageNameType, dict, None]) -> None: if value is not None and not isinstance(value, MultiLanguageNameType): value = MultiLanguageNameType(value) self._display_name = value @property - def description(self) -> MultiLanguageTextType | None: + def description(self) -> Optional[MultiLanguageTextType]: """Description of the element (MultiLanguageTextType).""" return self._description @description.setter - def description(self, value: MultiLanguageTextType | dict | None) -> None: + def description(self, value: Union[MultiLanguageTextType, dict, None]) -> None: if value is not None and not isinstance(value, MultiLanguageTextType): value = MultiLanguageTextType(value) self._description = value diff --git a/sdk/basyx/aas/model/submodel.py b/sdk/basyx/aas/model/submodel.py index 336ea385..41031972 100644 --- a/sdk/basyx/aas/model/submodel.py +++ b/sdk/basyx/aas/model/submodel.py @@ -342,18 +342,19 @@ def __init__(self, super().__init__(id_short, display_name, category, description, parent, semantic_id, qualifier, extension, supplemental_semantic_id, embedded_data_specifications) self.value: Optional[base.MultiLanguageTextType] = value - self.value_id: Optional[base.Reference] = value_id + self.value_id: Optional[base.Reference] = value_id @property - def value(self) -> base.MultiLanguageTextType | None: + def value(self) -> Optional[base.MultiLanguageTextType]: return self._value - + @value.setter - def value(self, value: base.MultiLanguageTextType | dict | None) -> None: + def value(self, value: Union[base.MultiLanguageTextType, dict, None]) -> None: if value is not None and not isinstance(value, base.MultiLanguageTextType): value = base.MultiLanguageTextType(value) self._value = value + class Range(DataElement): """ A range is a :class:`~.DataElement` that has a range value.