Skip to content

Commit 10ff4e4

Browse files
authored
sdk: Refactor AbstractObjectProvider (#430)
Previously, the `AbstractObjectProvider` only worked with `Identifiables`, which made it incompatible with the new version of the AAS metamodel. This renames and restructures both the `AbstractObjectProvider` and the `AbstractObjectStore`. In all non-abstract subclasses where `Object` appears in the class or method name, it has been replaced with `Identifiable`. Old classes are still available with a deprecation warning. Moreover, `AbstractObjectProvider` and `AbstractObjectStore` are now generic to be able to handle more classes than just `Identifiables`. In order to handle the new `AASDescriptor`, a new class `HasIdentifier` has been added. It is intended to be an abstract superclass for all classes that have an identifier, but are not `Identifiables`. As of now, this PR is intended to serve as a basis for discussion. Therefore, the documentation has not yet been adapted. Fixes #428
1 parent 6554e66 commit 10ff4e4

47 files changed

Lines changed: 785 additions & 644 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compliance_tool/aas_compliance_tool/compliance_check_aasx.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2025 the Eclipse BaSyx Authors
1+
# Copyright (c) 2026 the Eclipse BaSyx Authors
22
#
33
# This program and the accompanying materials are made available under the terms of the MIT License, available in
44
# the LICENSE file of this project.
@@ -31,7 +31,7 @@
3131

3232
def check_deserialization(file_path: str, state_manager: ComplianceToolStateManager,
3333
file_info: Optional[str] = None) \
34-
-> Tuple[model.DictObjectStore, aasx.DictSupplementaryFileContainer, pyecma376_2.OPCCoreProperties]:
34+
-> Tuple[model.DictIdentifiableStore, aasx.DictSupplementaryFileContainer, pyecma376_2.OPCCoreProperties]:
3535
"""
3636
Read a AASX file and reports any issues using the given
3737
:class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager`
@@ -68,24 +68,24 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
6868
state_manager.set_step_status_from_log()
6969
state_manager.add_step('Read file')
7070
state_manager.set_step_status(Status.NOT_EXECUTED)
71-
return model.DictObjectStore(), aasx.DictSupplementaryFileContainer(), pyecma376_2.OPCCoreProperties()
71+
return model.DictIdentifiableStore(), aasx.DictSupplementaryFileContainer(), pyecma376_2.OPCCoreProperties()
7272

7373
try:
7474
# read given file
7575
state_manager.add_step('Read file')
76-
obj_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
76+
identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
7777
files = aasx.DictSupplementaryFileContainer()
78-
reader.read_into(obj_store, files)
78+
reader.read_into(identifiable_store, files)
7979
new_cp = reader.get_core_properties()
8080
state_manager.set_step_status(Status.SUCCESS)
8181
except (ValueError, KeyError) as error:
8282
logger.error(error)
8383
state_manager.set_step_status(Status.FAILED)
84-
return model.DictObjectStore(), aasx.DictSupplementaryFileContainer(), pyecma376_2.OPCCoreProperties()
84+
return model.DictIdentifiableStore(), aasx.DictSupplementaryFileContainer(), pyecma376_2.OPCCoreProperties()
8585
finally:
8686
reader.close()
8787

88-
return obj_store, files, new_cp
88+
return identifiable_store, files, new_cp
8989

9090

9191
def check_schema(file_path: str, state_manager: ComplianceToolStateManager) -> None:
@@ -174,7 +174,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
174174
logger_example.propagate = False
175175
logger_example.setLevel(logging.INFO)
176176

177-
obj_store, files, cp_new = check_deserialization(file_path, state_manager)
177+
identifiable_store, files, cp_new = check_deserialization(file_path, state_manager)
178178

179179
if state_manager.status in (Status.FAILED, Status.NOT_EXECUTED):
180180
state_manager.add_step('Check if data is equal to example data')
@@ -187,7 +187,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
187187

188188
state_manager.add_step('Check if data is equal to example data')
189189
example_data = create_example_aas_binding()
190-
checker.check_object_store(obj_store, example_data)
190+
checker.check_identifiable_store(identifiable_store, example_data)
191191
state_manager.add_log_records_from_data_checker(checker)
192192

193193
if state_manager.status in (Status.FAILED, Status.NOT_EXECUTED):
@@ -238,22 +238,25 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
238238

239239
# Check if file in file object is the same
240240
list_of_id_shorts = ["ExampleSubmodelCollection", "ExampleFile"]
241-
obj = example_data.get_identifiable("https://acplt.org/Test_Submodel")
241+
identifiable = example_data.get_item("https://acplt.org/Test_Submodel")
242242
for id_short in list_of_id_shorts:
243-
obj = obj.get_referable(id_short)
244-
obj2 = obj_store.get_identifiable("https://acplt.org/Test_Submodel")
243+
identifiable = identifiable.get_referable(id_short)
244+
obj2 = identifiable_store.get_item("https://acplt.org/Test_Submodel")
245245
for id_short in list_of_id_shorts:
246246
obj2 = obj2.get_referable(id_short)
247247
try:
248-
sha_file = files.get_sha256(obj.value)
248+
sha_file = files.get_sha256(identifiable.value)
249249
except KeyError as error:
250250
state_manager.add_log_records_from_data_checker(checker2)
251251
logger.error(error)
252252
state_manager.set_step_status(Status.FAILED)
253253
return
254254

255-
checker2.check(sha_file == files.get_sha256(obj2.value), "File of {} must be {}.".format(obj.value, obj2.value),
256-
value=obj2.value)
255+
checker2.check(
256+
sha_file == files.get_sha256(obj2.value),
257+
"File of {} must be {}.".format(identifiable.value, obj2.value),
258+
value=obj2.value
259+
)
257260
state_manager.add_log_records_from_data_checker(checker2)
258261
if state_manager.status in (Status.FAILED, Status.NOT_EXECUTED):
259262
state_manager.set_step_status(Status.FAILED)
@@ -280,9 +283,9 @@ def check_aasx_files_equivalence(file_path_1: str, file_path_2: str, state_manag
280283
logger.propagate = False
281284
logger.setLevel(logging.INFO)
282285

283-
obj_store_1, files_1, cp_1 = check_deserialization(file_path_1, state_manager, 'first')
286+
identifiable_store_1, files_1, cp_1 = check_deserialization(file_path_1, state_manager, 'first')
284287

285-
obj_store_2, files_2, cp_2 = check_deserialization(file_path_2, state_manager, 'second')
288+
identifiable_store_2, files_2, cp_2 = check_deserialization(file_path_2, state_manager, 'second')
286289

287290
if state_manager.status is Status.FAILED:
288291
state_manager.add_step('Check if data in files are equal')
@@ -294,7 +297,7 @@ def check_aasx_files_equivalence(file_path_1: str, file_path_2: str, state_manag
294297
checker = AASDataChecker(raise_immediately=False, **kwargs)
295298
try:
296299
state_manager.add_step('Check if data in files are equal')
297-
checker.check_object_store(obj_store_1, obj_store_2)
300+
checker.check_identifiable_store(identifiable_store_1, identifiable_store_2)
298301
except (KeyError, AssertionError) as error:
299302
state_manager.set_step_status(Status.FAILED)
300303
logger.error(error)

compliance_tool/aas_compliance_tool/compliance_check_json.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2025 the Eclipse BaSyx Authors
1+
# Copyright (c) 2026 the Eclipse BaSyx Authors
22
#
33
# This program and the accompanying materials are made available under the terms of the MIT License, available in
44
# the LICENSE file of this project.
@@ -102,7 +102,7 @@ def _check_schema(file_to_be_checked: IO[str], state_manager: ComplianceToolStat
102102

103103

104104
def check_deserialization(file_path: str, state_manager: ComplianceToolStateManager,
105-
file_info: Optional[str] = None) -> model.DictObjectStore:
105+
file_info: Optional[str] = None) -> model.DictIdentifiableStore:
106106
"""
107107
Deserializes a JSON AAS file and reports any issues using the given
108108
:class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager`
@@ -112,7 +112,7 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
112112
:param file_path: Given file which should be deserialized
113113
:param state_manager: :class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager` to log the steps
114114
:param file_info: Additional information about the file for name of the steps
115-
:return: The deserialized :class:`~basyx.aas.model.provider.DictObjectStore`
115+
:return: The deserialized :class:`~basyx.aas.model.provider.DictIdentifiableStore`
116116
"""
117117
logger = logging.getLogger('compliance_check')
118118
logger.addHandler(state_manager)
@@ -140,7 +140,7 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
140140
else:
141141
state_manager.add_step('Read file and check if it is deserializable')
142142
state_manager.set_step_status(Status.NOT_EXECUTED)
143-
return model.DictObjectStore()
143+
return model.DictIdentifiableStore()
144144

145145
with file_to_be_checked:
146146
state_manager.set_step_status(Status.SUCCESS)
@@ -149,11 +149,11 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
149149
state_manager.add_step('Read file {} and check if it is deserializable'.format(file_info))
150150
else:
151151
state_manager.add_step('Read file and check if it is deserializable')
152-
obj_store = json_deserialization.read_aas_json_file(file_to_be_checked, failsafe=True)
152+
identifiable_store = json_deserialization.read_aas_json_file(file_to_be_checked, failsafe=True)
153153

154154
state_manager.set_step_status_from_log()
155155

156-
return obj_store
156+
return identifiable_store
157157

158158

159159
def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager, **kwargs) -> None:
@@ -174,7 +174,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
174174
logger_example.propagate = False
175175
logger_example.setLevel(logging.INFO)
176176

177-
obj_store = check_deserialization(file_path, state_manager)
177+
identifiable_store = check_deserialization(file_path, state_manager)
178178

179179
if state_manager.status in (Status.FAILED, Status.NOT_EXECUTED):
180180
state_manager.add_step('Check if data is equal to example data')
@@ -184,7 +184,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
184184
checker = AASDataChecker(raise_immediately=False, **kwargs)
185185

186186
state_manager.add_step('Check if data is equal to example data')
187-
checker.check_object_store(obj_store, create_example())
187+
checker.check_identifiable_store(identifiable_store, create_example())
188188

189189
state_manager.add_log_records_from_data_checker(checker)
190190

@@ -208,9 +208,9 @@ def check_json_files_equivalence(file_path_1: str, file_path_2: str, state_manag
208208
logger.propagate = False
209209
logger.setLevel(logging.INFO)
210210

211-
obj_store_1 = check_deserialization(file_path_1, state_manager, 'first')
211+
identifiable_store_1 = check_deserialization(file_path_1, state_manager, 'first')
212212

213-
obj_store_2 = check_deserialization(file_path_2, state_manager, 'second')
213+
identifiable_store_2 = check_deserialization(file_path_2, state_manager, 'second')
214214

215215
if state_manager.status is Status.FAILED:
216216
state_manager.add_step('Check if data in files are equal')
@@ -220,7 +220,7 @@ def check_json_files_equivalence(file_path_1: str, file_path_2: str, state_manag
220220
checker = AASDataChecker(raise_immediately=False, **kwargs)
221221
try:
222222
state_manager.add_step('Check if data in files are equal')
223-
checker.check_object_store(obj_store_1, obj_store_2)
223+
checker.check_identifiable_store(identifiable_store_1, identifiable_store_2)
224224
except (KeyError, AssertionError) as error:
225225
state_manager.set_step_status(Status.FAILED)
226226
logger.error(error)

compliance_tool/aas_compliance_tool/compliance_check_xml.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def _check_schema(file_to_be_checked, state_manager):
101101

102102

103103
def check_deserialization(file_path: str, state_manager: ComplianceToolStateManager,
104-
file_info: Optional[str] = None) -> model.DictObjectStore:
104+
file_info: Optional[str] = None) -> model.DictIdentifiableStore:
105105
"""
106106
Deserializes a XML AAS file and reports any issues using the given
107107
:class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager`
@@ -139,7 +139,7 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
139139
else:
140140
state_manager.add_step('Read file and check if it is deserializable')
141141
state_manager.set_step_status(Status.NOT_EXECUTED)
142-
return model.DictObjectStore()
142+
return model.DictIdentifiableStore()
143143

144144
with file_to_be_checked:
145145
state_manager.set_step_status(Status.SUCCESS)
@@ -148,11 +148,11 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
148148
state_manager.add_step('Read file {} and check if it is deserializable'.format(file_info))
149149
else:
150150
state_manager.add_step('Read file and check if it is deserializable')
151-
obj_store = xml_deserialization.read_aas_xml_file(file_to_be_checked, failsafe=True)
151+
identifiable_store = xml_deserialization.read_aas_xml_file(file_to_be_checked, failsafe=True)
152152

153153
state_manager.set_step_status_from_log()
154154

155-
return obj_store
155+
return identifiable_store
156156

157157

158158
def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager, **kwargs) -> None:
@@ -173,7 +173,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
173173
logger_example.propagate = False
174174
logger_example.setLevel(logging.INFO)
175175

176-
obj_store = check_deserialization(file_path, state_manager)
176+
identifiable_store = check_deserialization(file_path, state_manager)
177177

178178
if state_manager.status in (Status.FAILED, Status.NOT_EXECUTED):
179179
state_manager.add_step('Check if data is equal to example data')
@@ -183,7 +183,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
183183
checker = AASDataChecker(raise_immediately=False, **kwargs)
184184

185185
state_manager.add_step('Check if data is equal to example data')
186-
checker.check_object_store(obj_store, create_example())
186+
checker.check_identifiable_store(identifiable_store, create_example())
187187

188188
state_manager.add_log_records_from_data_checker(checker)
189189

@@ -207,9 +207,9 @@ def check_xml_files_equivalence(file_path_1: str, file_path_2: str, state_manage
207207
logger.propagate = False
208208
logger.setLevel(logging.INFO)
209209

210-
obj_store_1 = check_deserialization(file_path_1, state_manager, 'first')
210+
identifiable_store_1 = check_deserialization(file_path_1, state_manager, 'first')
211211

212-
obj_store_2 = check_deserialization(file_path_2, state_manager, 'second')
212+
identifiable_store_2 = check_deserialization(file_path_2, state_manager, 'second')
213213

214214
if state_manager.status is Status.FAILED:
215215
state_manager.add_step('Check if data in files are equal')
@@ -219,7 +219,7 @@ def check_xml_files_equivalence(file_path_1: str, file_path_2: str, state_manage
219219
checker = AASDataChecker(raise_immediately=False, **kwargs)
220220
try:
221221
state_manager.add_step('Check if data in files are equal')
222-
checker.check_object_store(obj_store_1, obj_store_2)
222+
checker.check_identifiable_store(identifiable_store_1, identifiable_store_2)
223223
except (KeyError, AssertionError) as error:
224224
state_manager.set_step_status(Status.FAILED)
225225
logger.error(error)

compliance_tool/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ requires-python = ">=3.10"
3838
dependencies = [
3939
"pyecma376-2>=0.2.4",
4040
"jsonschema>=4.21.1",
41-
"basyx-python-sdk>=1.0.0",
41+
"basyx-python-sdk @ file:../sdk",
4242
]
4343

4444
[project.optional-dependencies]

compliance_tool/test/test_aas_compliance_tool.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2025 the Eclipse BaSyx Authors
1+
# Copyright (c) 2026 the Eclipse BaSyx Authors
22
#
33
# This program and the accompanying materials are made available under the terms of the MIT License, available in
44
# the LICENSE file of this project.
@@ -138,10 +138,10 @@ def test_json_create_example(self) -> None:
138138
self.assertIn('SUCCESS: Write data to file', str(output.stdout))
139139

140140
with open(filename, "r", encoding='utf-8-sig') as f:
141-
json_object_store = read_aas_json_file(f, failsafe=False)
141+
json_identifiable_store = read_aas_json_file(f, failsafe=False)
142142
data = create_example()
143143
checker = AASDataChecker(raise_immediately=True)
144-
checker.check_object_store(json_object_store, data)
144+
checker.check_identifiable_store(json_identifiable_store, data)
145145
os.unlink(filename)
146146

147147
def test_json_deserialization(self) -> None:
@@ -184,10 +184,10 @@ def test_xml_create_example(self) -> None:
184184
self.assertIn('SUCCESS: Write data to file', str(output.stdout))
185185

186186
with open(filename, "rb") as f:
187-
xml_object_store = read_aas_xml_file(f, failsafe=False)
187+
xml_identifiable_store = read_aas_xml_file(f, failsafe=False)
188188
data = create_example()
189189
checker = AASDataChecker(raise_immediately=True)
190-
checker.check_object_store(xml_object_store, data)
190+
checker.check_identifiable_store(xml_identifiable_store, data)
191191
os.unlink(filename)
192192

193193
def test_xml_deseralization(self) -> None:
@@ -229,7 +229,7 @@ def test_aasx_create_example(self) -> None:
229229
self.assertIn('SUCCESS: Write data to file', str(output.stdout))
230230

231231
# Read AASX file
232-
new_data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
232+
new_data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
233233
new_files = aasx.DictSupplementaryFileContainer()
234234
with aasx.AASXReader(filename) as reader:
235235
reader.read_into(new_data, new_files)

etc/scripts/check_python_versions_supported.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ def main(min_version: str, max_version: str) -> None:
1414
response = requests.get("https://endoflife.date/api/python.json")
1515
response.raise_for_status()
1616
eol_data = response.json()
17-
eol_versions = {entry["cycle"]: {"eol": entry["eol"], "releaseDate": entry["releaseDate"]} for entry in eol_data}
17+
eol_versions = {
18+
entry["cycle"]: {"eol": entry["eol"], "releaseDate": entry["releaseDate"]} for entry in eol_data
19+
}
1820

1921
# Get current date to compare with EoL and release dates
2022
current_date = datetime.now().date()

sdk/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ Serialize the `Submodel` to XML:
113113
```python
114114
from basyx.aas.adapter.xml import write_aas_xml_file
115115

116-
data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
116+
data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
117117
data.add(submodel)
118118
write_aas_xml_file(file='Simple_Submodel.xml', data=data)
119119
```
@@ -124,10 +124,10 @@ write_aas_xml_file(file='Simple_Submodel.xml', data=data)
124124
For further examples and tutorials, check out the `basyx.aas.examples`-package. Here is a quick overview:
125125

126126
* [`tutorial_create_simple_aas`](./basyx/aas/examples/tutorial_create_simple_aas.py): Create an Asset Administration Shell, including an Asset object and a Submodel
127-
* [`tutorial_storage`](./basyx/aas/examples/tutorial_storage.py): Manage a larger number of Asset Administration Shells in an ObjectStore and resolve references
127+
* [`tutorial_storage`](./basyx/aas/examples/tutorial_storage.py): Manage a larger number of Asset Administration Shells in an IdentifiableStore and resolve references
128128
* [`tutorial_serialization_deserialization`](./basyx/aas/examples/tutorial_serialization_deserialization.py): Use the JSON and XML serialization/deserialization for single objects or full standard-compliant files
129129
* [`tutorial_aasx`](./basyx/aas/examples/tutorial_aasx.py): Export Asset Administration Shells with related objects and auxiliary files to AASX package files
130-
* [`tutorial_backend_couchdb`](./basyx/aas/examples/tutorial_backend_couchdb.py): Use the *CouchDBObjectStore* to manage and retrieve AAS objects in a CouchDB document database
130+
* [`tutorial_backend_couchdb`](./basyx/aas/examples/tutorial_backend_couchdb.py): Use the *CouchDBIdentifiableStore* to manage and retrieve AAS objects in a CouchDB document database
131131

132132

133133
### Documentation

0 commit comments

Comments
 (0)