Skip to content

Commit 2bbb0bd

Browse files
committed
Merge branch 'develop' into fix/pagination
2 parents 62db5ba + f2ef2c1 commit 2bbb0bd

27 files changed

Lines changed: 709 additions & 259 deletions

.github/workflows/ci.yml

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -203,36 +203,37 @@ jobs:
203203
chmod +x ./etc/scripts/set_copyright_year.sh
204204
./etc/scripts/set_copyright_year.sh --check
205205
206+
# Todo: reset when the compliance-tool was updated (#485)
206207

207-
compliance-tool-test:
208-
# This job runs the unittests on the python versions specified down at the matrix
209-
runs-on: ubuntu-latest
210-
strategy:
211-
matrix:
212-
python-version: ["3.10", "3.12"]
213-
defaults:
214-
run:
215-
working-directory: ./compliance_tool
216-
217-
steps:
218-
- uses: actions/checkout@v4
219-
- name: Set up Python ${{ matrix.python-version }}
220-
uses: actions/setup-python@v5
221-
with:
222-
python-version: ${{ matrix.python-version }}
223-
- name: Install Python dependencies
224-
# install the local sdk in editable mode so it does not get overwritten
225-
run: |
226-
python -m pip install --upgrade pip
227-
python -m pip install ../sdk
228-
python -m pip install .[dev]
229-
- name: Test with coverage + unittest
230-
run: |
231-
python -m coverage run --source=aas_compliance_tool -m unittest
232-
- name: Report test coverage
233-
if: ${{ always() }}
234-
run: |
235-
python -m coverage report -m
208+
# compliance-tool-test:
209+
# # This job runs the unittests on the python versions specified down at the matrix
210+
# runs-on: ubuntu-latest
211+
# strategy:
212+
# matrix:
213+
# python-version: ["3.10", "3.12"]
214+
# defaults:
215+
# run:
216+
# working-directory: ./compliance_tool
217+
#
218+
# steps:
219+
# - uses: actions/checkout@v4
220+
# - name: Set up Python ${{ matrix.python-version }}
221+
# uses: actions/setup-python@v5
222+
# with:
223+
# python-version: ${{ matrix.python-version }}
224+
# - name: Install Python dependencies
225+
# # install the local sdk in editable mode so it does not get overwritten
226+
# run: |
227+
# python -m pip install --upgrade pip
228+
# python -m pip install ../sdk
229+
# python -m pip install .[dev]
230+
# - name: Test with coverage + unittest
231+
# run: |
232+
# python -m coverage run --source=aas_compliance_tool -m unittest
233+
# - name: Report test coverage
234+
# if: ${{ always() }}
235+
# run: |
236+
# python -m coverage report -m
236237

237238
compliance-tool-static-analysis:
238239
# This job runs static code analysis, namely pycodestyle and mypy

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ for Industry 4.0 Systems.
77

88
These are the implemented AAS specifications of the [current SDK release](https://github.com/eclipse-basyx/basyx-python-sdk/releases/latest), which can be also found on [PyPI](https://pypi.org/project/basyx-python-sdk/):
99

10-
| Specification | Version |
11-
|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
12-
| Part 1: Metamodel | [v3.0.1 (01001)](https://industrialdigitaltwin.org/wp-content/uploads/2024/06/IDTA-01001-3-0-1_SpecificationAssetAdministrationShell_Part1_Metamodel.pdf) |
13-
| Schemata (JSONSchema, XSD) | [v3.0.8 (IDTA-01001-3-0-1_schemasV3.0.8)](https://github.com/admin-shell-io/aas-specs/releases/tag/IDTA-01001-3-0-1_schemasV3.0.8) |
14-
| Part 2: API | [v3.1.1 (01002)](https://industrialdigitaltwin.org/en/wp-content/uploads/sites/2/2025/08/IDTA-01002-3-1-1_AAS-Specification_Part2_API.pdf) |
10+
| Specification | Version |
11+
|---------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
12+
| Part 1: Metamodel | [v3.1.2 (01001-3-1-2)](https://industrialdigitaltwin.io/aas-specifications/IDTA-01001/v3.1.2/index.html) |
13+
| Schemata (JSONSchema, XSD) | [v3.1.2 (IDTA-01001-3-1-2)](https://github.com/admin-shell-io/aas-specs-metamodel/releases/tag/v3.1.2) |
14+
| Part 2: API | [v3.1.1 (01002)](https://industrialdigitaltwin.org/en/wp-content/uploads/sites/2/2025/08/IDTA-01002-3-1-1_AAS-Specification_Part2_API.pdf) |
1515
| Part 3a: Data Specification IEC 61360 | [v3.1.1 (01003-a)](https://industrialdigitaltwin.org/wp-content/uploads/2025/08/IDTA-01003-a-3-1-1_AAS-Specification_Part3a_DataSpecification.pdf) |
16-
| Part 5: Package File Format (AASX) | [v3.1 (01005)](https://industrialdigitaltwin.org/wp-content/uploads/2025/06/IDTA_01005-25-01_AAS-Specification_Part5_AASXPackageFileFormat.pdf) |
16+
| Part 5: Package File Format (AASX) | [v3.1 (01005)](https://industrialdigitaltwin.org/wp-content/uploads/2025/06/IDTA_01005-25-01_AAS-Specification_Part5_AASXPackageFileFormat.pdf) |
1717

1818

1919
If you need support to an older version of the specifications, please refer to our [prior releases](https://github.com/eclipse-basyx/basyx-python-sdk/releases).

compliance_tool/aas_compliance_tool/cli.py

Lines changed: 3 additions & 4 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.
@@ -76,7 +76,7 @@ def parse_cli_arguments() -> argparse.ArgumentParser:
7676
parser.add_argument('-v', '--verbose', help="Print detailed information for each check. Multiple -v options "
7777
"increase the verbosity. 1: Detailed error information, 2: Additional "
7878
"detailed success information", action='count', default=0)
79-
parser.add_argument('-q', '--quite', help="no information output if successful", action='store_true')
79+
parser.add_argument('-q', '--quiet', help="no information output if successful", action='store_true')
8080
group = parser.add_mutually_exclusive_group(required=True)
8181
group.add_argument('--json', help="Use AAS json format when checking or creating files", action='store_true')
8282
group.add_argument('--xml', help="Use AAS xml format when checking or creating files", action='store_true')
@@ -177,9 +177,8 @@ def main():
177177
**data_checker_kwargs)
178178
else:
179179
parser.error("f or files requires two file path.")
180-
exit()
181180

182-
if manager.status is Status.SUCCESS and args.quite:
181+
if manager.status is Status.SUCCESS and args.quiet:
183182
exit()
184183

185184
print(manager.format_state_manager(args.verbose))

compliance_tool/test/test_aas_compliance_tool.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,17 @@ def test_parse_args(self) -> None:
115115
self.assertNotIn('ERROR', str(output.stderr))
116116
self.assertIn('INFO', str(output.stdout))
117117

118-
# test quite
118+
# test quiet (short form)
119119
output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example.json"), "--json", "-q")
120120
self.assertEqual(0, output.returncode)
121121
self.assertEqual("b''", str(output.stdout))
122122

123+
# test quiet (long form -- previously misspelled as --quite)
124+
output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example.json"), "--json",
125+
"--quiet")
126+
self.assertEqual(0, output.returncode)
127+
self.assertEqual("b''", str(output.stdout))
128+
123129
# test logfile
124130
output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example.json"), "--json", "-l")
125131
self.assertNotEqual(0, output.returncode)

sdk/basyx/aas/adapter/_generic.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
ASSET_KIND: Dict[model.AssetKind, str] = {
3838
model.AssetKind.TYPE: 'Type',
3939
model.AssetKind.INSTANCE: 'Instance',
40-
model.AssetKind.NOT_APPLICABLE: 'NotApplicable'}
40+
model.AssetKind.NOT_APPLICABLE: 'NotApplicable',
41+
model.AssetKind.ROLE: 'Role'}
4142

4243
QUALIFIER_KIND: Dict[model.QualifierKind, str] = {
4344
model.QualifierKind.CONCEPT_QUALIFIER: 'ConceptQualifier',

sdk/basyx/aas/adapter/json/json_deserialization.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,8 +511,6 @@ def _construct_data_specification_iec61360(cls, dct: Dict[str, object],
511511
ret.value_list = cls._construct_value_list(_get_ts(dct, 'valueList', dict))
512512
if 'value' in dct:
513513
ret.value = _get_ts(dct, 'value', str)
514-
if 'valueId' in dct:
515-
ret.value_id = cls._construct_reference(_get_ts(dct, 'valueId', dict))
516514
if 'levelType' in dct:
517515
for k, v in _get_ts(dct, 'levelType', dict).items():
518516
if v:
@@ -528,9 +526,12 @@ def _construct_entity(cls, dct: Dict[str, object], object_class=model.Entity) ->
528526
if 'specificAssetIds' in dct:
529527
for desc_data in _get_ts(dct, "specificAssetIds", list):
530528
specific_asset_id.add(cls._construct_specific_asset_id(desc_data, model.SpecificAssetId))
531-
529+
if 'entityType' in dct:
530+
entity_type = ENTITY_TYPES_INVERSE[_get_ts(dct, 'entityType', str)]
531+
else:
532+
entity_type = None
532533
ret = object_class(id_short=None,
533-
entity_type=ENTITY_TYPES_INVERSE[_get_ts(dct, "entityType", str)],
534+
entity_type=entity_type,
534535
global_asset_id=global_asset_id,
535536
specific_asset_id=specific_asset_id)
536537
cls._amend_abstract_attributes(ret, dct)

sdk/basyx/aas/adapter/json/json_serialization.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,8 @@ def _blob_to_json(cls, obj: model.Blob) -> Dict[str, object]:
476476
:return: dict with the serialized attributes of this object
477477
"""
478478
data = cls._abstract_classes_to_json(obj)
479-
data['contentType'] = obj.content_type
479+
if obj.content_type is not None:
480+
data['contentType'] = obj.content_type
480481
if obj.value is not None:
481482
data['value'] = base64.b64encode(obj.value).decode()
482483
return data
@@ -490,7 +491,8 @@ def _file_to_json(cls, obj: model.File) -> Dict[str, object]:
490491
:return: dict with the serialized attributes of this object
491492
"""
492493
data = cls._abstract_classes_to_json(obj)
493-
data['contentType'] = obj.content_type
494+
if obj.content_type is not None:
495+
data['contentType'] = obj.content_type
494496
if obj.value is not None:
495497
data['value'] = obj.value
496498
return data
@@ -576,8 +578,7 @@ def _annotated_relationship_element_to_json(cls, obj: model.AnnotatedRelationshi
576578
:param obj: object of class AnnotatedRelationshipElement
577579
:return: dict with the serialized attributes of this object
578580
"""
579-
data = cls._abstract_classes_to_json(obj)
580-
data.update({'first': obj.first, 'second': obj.second})
581+
data = cls._relationship_element_to_json(obj)
581582
if not cls.stripped and obj.annotation:
582583
data['annotations'] = list(obj.annotation)
583584
return data
@@ -635,10 +636,11 @@ def _entity_to_json(cls, obj: model.Entity) -> Dict[str, object]:
635636
data = cls._abstract_classes_to_json(obj)
636637
if not cls.stripped and obj.statement:
637638
data['statements'] = list(obj.statement)
638-
data['entityType'] = _generic.ENTITY_TYPES[obj.entity_type]
639-
if obj.global_asset_id:
639+
if obj.entity_type is not None:
640+
data['entityType'] = _generic.ENTITY_TYPES[obj.entity_type]
641+
if obj.global_asset_id is not None:
640642
data['globalAssetId'] = obj.global_asset_id
641-
if obj.specific_asset_id:
643+
if obj.specific_asset_id is not None:
642644
data['specificAssetIds'] = list(obj.specific_asset_id)
643645
return data
644646

sdk/basyx/aas/adapter/xml/xml_deserialization.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -827,10 +827,14 @@ def construct_entity(cls, element: etree._Element, object_class=model.Entity, **
827827
for id in _child_construct_multiple(specific_asset_ids, NS_AAS + "specificAssetId",
828828
cls.construct_specific_asset_id, cls.failsafe):
829829
specific_asset_id.add(id)
830-
830+
entity_type_text = _get_text_or_none(element.find(NS_AAS + "entityType"))
831+
if entity_type_text is not None:
832+
entity_type = ENTITY_TYPES_INVERSE[entity_type_text]
833+
else:
834+
entity_type = None
831835
entity = object_class(
832836
id_short=None,
833-
entity_type=_child_text_mandatory_mapped(element, NS_AAS + "entityType", ENTITY_TYPES_INVERSE),
837+
entity_type=entity_type,
834838
global_asset_id=_get_text_or_none(element.find(NS_AAS + "globalAssetId")),
835839
specific_asset_id=specific_asset_id)
836840

sdk/basyx/aas/adapter/xml/xml_serialization.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,8 @@ def blob_to_xml(obj: model.Blob,
628628
if obj.value is not None:
629629
et_value.text = base64.b64encode(obj.value).decode()
630630
et_blob.append(et_value)
631-
et_blob.append(_generate_element(NS_AAS + "contentType", text=obj.content_type))
631+
if obj.content_type is not None:
632+
et_blob.append(_generate_element(NS_AAS + "contentType", text=obj.content_type))
632633
return et_blob
633634

634635

@@ -644,7 +645,8 @@ def file_to_xml(obj: model.File,
644645
et_file = abstract_classes_to_xml(tag, obj)
645646
if obj.value:
646647
et_file.append(_generate_element(NS_AAS + "value", text=obj.value))
647-
et_file.append(_generate_element(NS_AAS + "contentType", text=obj.content_type))
648+
if obj.content_type is not None:
649+
et_file.append(_generate_element(NS_AAS + "contentType", text=obj.content_type))
648650
return et_file
649651

650652

@@ -734,8 +736,10 @@ def relationship_element_to_xml(obj: model.RelationshipElement,
734736
:return: Serialized :class:`~lxml.etree._Element` object
735737
"""
736738
et_relationship_element = abstract_classes_to_xml(tag, obj)
737-
et_relationship_element.append(reference_to_xml(obj.first, NS_AAS+"first"))
738-
et_relationship_element.append(reference_to_xml(obj.second, NS_AAS+"second"))
739+
if obj.first is not None:
740+
et_relationship_element.append(reference_to_xml(obj.first, NS_AAS+"first"))
741+
if obj.second is not None:
742+
et_relationship_element.append(reference_to_xml(obj.second, NS_AAS+"second"))
739743
return et_relationship_element
740744

741745

@@ -823,7 +827,8 @@ def entity_to_xml(obj: model.Entity,
823827
for statement in obj.statement:
824828
et_statements.append(submodel_element_to_xml(statement))
825829
et_entity.append(et_statements)
826-
et_entity.append(_generate_element(NS_AAS + "entityType", text=_generic.ENTITY_TYPES[obj.entity_type]))
830+
if obj.entity_type:
831+
et_entity.append(_generate_element(NS_AAS + "entityType", text=_generic.ENTITY_TYPES[obj.entity_type]))
827832
if obj.global_asset_id:
828833
et_entity.append(_generate_element(NS_AAS + "globalAssetId", text=obj.global_asset_id))
829834
if obj.specific_asset_id:

sdk/basyx/aas/examples/data/example_aas.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
_embedded_data_specification_iec61360 = model.EmbeddedDataSpecification(
2424
data_specification=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
2525
value='https://admin-shell.io/DataSpecificationTemplates/'
26-
'DataSpecificationIEC61360/3'),)),
26+
'DataSpecificationIEC61360/3/1'),)),
2727
data_specification_content=model.DataSpecificationIEC61360(preferred_name=model.PreferredNameTypeIEC61360({
2828
'de': 'Test Specification',
2929
'en-US': 'TestSpecification'

0 commit comments

Comments
 (0)