diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d8774ebfd..8901688fd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,9 +14,9 @@ jobs:
run:
working-directory: ./etc/scripts
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ env.X_PYTHON_MIN_VERSION }}
- name: Install Python dependencies
@@ -29,12 +29,14 @@ jobs:
${{ env.X_PYTHON_MIN_VERSION }} \
${{ env.X_PYTHON_MAX_VERSION }}
- - name: Check Python Versions coincide with the SDKs pyproject.toml
+ - name: Check Python Versions coincide with all pyproject.toml Files
run: |
- python check_python_versions_coincide.py \
- ../../sdk/pyproject.toml \
- ${{ env.X_PYTHON_MIN_VERSION }} \
- ${{ env.X_PYTHON_MAX_VERSION }}
+ for file in ../../sdk/pyproject.toml ../../compliance_tool/pyproject.toml; do
+ python check_python_versions_coincide.py \
+ $file \
+ ${{ env.X_PYTHON_MIN_VERSION }} \
+ ${{ env.X_PYTHON_MAX_VERSION }}
+ done
# Todo: Check other pyproject.toml here as well, as we add them
@@ -72,7 +74,7 @@ jobs:
exit 1
fi
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Collect schema files from aas-specs
@@ -104,7 +106,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ env.X_PYTHON_MIN_VERSION }}
- name: Install Python dependencies
@@ -127,7 +129,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ env.X_PYTHON_MIN_VERSION }}
- name: Install Python dependencies
@@ -153,7 +155,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ env.X_PYTHON_MIN_VERSION }}
- name: Install Python dependencies
@@ -174,7 +176,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ env.X_PYTHON_MIN_VERSION }}
- name: Install dependencies
@@ -185,28 +187,45 @@ jobs:
run: |
python -m build
+ repository-check-copyright:
+ # This job checks that the copyright year in the header of all files is up to date
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # Ensure full history is available
+ - name: Install required dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y bash git
+ - name: Run copyright check
+ run: |
+ chmod +x ./etc/scripts/set_copyright_year.sh
+ ./etc/scripts/set_copyright_year.sh --check
+
compliance-tool-test:
# This job runs the unittests on the python versions specified down at the matrix
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: ["3.8", "3.10", "3.12"]
+ python-version: ["3.9", "3.12"]
defaults:
run:
working-directory: ./compliance_tool
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install Python dependencies
+ # install the local sdk in editable mode so it does not get overwritten
run: |
python -m pip install --upgrade pip
- pip install coverage
- pip install -r requirements.txt
+ pip install -e ../sdk[dev]
+ pip install .[dev]
- name: Test with coverage + unittest
run: |
coverage run --source=aas_compliance_tool -m unittest
@@ -223,16 +242,17 @@ jobs:
run:
working-directory: ./compliance_tool
steps:
- - uses: actions/checkout@v2
- - name: Set up Python ${{ env.X_PYTHON_VERSION }}
- uses: actions/setup-python@v2
+ - uses: actions/checkout@v4
+ - name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }}
+ uses: actions/setup-python@v5
with:
- python-version: ${{ env.X_PYTHON_VERSION }}
+ python-version: ${{ env.X_PYTHON_MIN_VERSION }}
- name: Install Python dependencies
+ # install the local sdk in editable mode so it does not get overwritten
run: |
python -m pip install --upgrade pip
- pip install pycodestyle mypy
- pip install -r requirements.txt
+ pip install -e ../sdk[dev]
+ pip install .[dev]
- name: Check typing with MyPy
run: |
mypy ./aas_compliance_tool test
@@ -248,16 +268,17 @@ jobs:
run:
working-directory: ./compliance_tool
steps:
- - uses: actions/checkout@v2
- - name: Set up Python ${{ env.X_PYTHON_VERSION }}
- uses: actions/setup-python@v2
+ - uses: actions/checkout@v4
+ - name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }}
+ uses: actions/setup-python@v5
with:
- python-version: ${{ env.X_PYTHON_VERSION }}
+ python-version: ${{ env.X_PYTHON_MIN_VERSION }}
- name: Install Python dependencies
+ # install the local sdk in editable mode so it does not get overwritten
run: |
python -m pip install --upgrade pip
- pip install pycodestyle mypy codeblocks
- pip install -r requirements.txt
+ pip install -e ../sdk[dev]
+ pip install .[dev]
- name: Check typing with MyPy
run: |
mypy <(codeblocks python README.md)
@@ -276,18 +297,18 @@ jobs:
run:
working-directory: ./compliance_tool
steps:
- - uses: actions/checkout@v2
- - name: Set up Python ${{ env.X_PYTHON_VERSION }}
- uses: actions/setup-python@v2
+ - uses: actions/checkout@v4
+ - name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }}
+ uses: actions/setup-python@v5
with:
- python-version: ${{ env.X_PYTHON_VERSION }}
+ python-version: ${{ env.X_PYTHON_MIN_VERSION }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install setuptools wheel
+ pip install build
- name: Create source and wheel dist
run: |
- python setup.py sdist bdist_wheel
+ python -m build
server-package:
# This job checks if we can build our server package
@@ -299,7 +320,7 @@ jobs:
- uses: actions/checkout@v4
- name: Build the Docker image
run: |
- docker build -t basyx-python-server .
+ docker build -t basyx-python-server -f Dockerfile ..
- name: Run container
run: |
docker run -d --name basyx-python-server basyx-python-server
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 15b22ea25..06491c0cb 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -5,16 +5,16 @@ on:
types: [published]
jobs:
- publish:
- # This job publishes the package to PyPI
+ sdk-publish:
+ # This job publishes the SDK package to PyPI
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./sdk
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Set up Python 3.10
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install dependencies
@@ -31,3 +31,30 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_ORG_TOKEN }}
+
+ compliance-tool-publish:
+ # This job publishes the compliance_tool package to PyPI
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: ./compliance_tool
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up Python 3.10
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install build
+ - name: Create source and wheel dist
+ # (2024-12-11, s-heppner)
+ # The PyPI Action expects the dist files in a toplevel `/dist` directory,
+ # so we have to specify this as output directory here.
+ run: |
+ python -m build --outdir ../dist
+ - name: Publish distribution to PyPI
+ uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ password: ${{ secrets.PYPI_ORG_TOKEN }}
diff --git a/.gitignore b/.gitignore
index dc7eddbb6..18b522c3a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,6 +28,7 @@ sdk/test/adapter/schemas
# Ignore dynamically generated version file
sdk/basyx/version.py
+compliance_tool/aas_compliance_tool/version.py
# ignore the content of the server storage
server/storage/
diff --git a/compliance_tool/__init__.py b/1.0.0
similarity index 100%
rename from compliance_tool/__init__.py
rename to 1.0.0
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ce3a4fd6b..a15c9c20c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -94,7 +94,7 @@ the further steps via the comments.
In order to effectively communicate, there are some conventions to respect when writing
commit messages and pull requests.
-Similarily to when creating an issue, the commit title, as well as the PR title should
+Similarly to when creating an issue, the commit title, as well as the PR title should
be as short as possible, ideally 72 characters or fewer using imperative language.
If a specific module is affected, please mention it at the beginning of the title.
@@ -119,16 +119,41 @@ The following guidelines are for the commit or PR message text:
via `https://link/to.pdf#Page=123`
- Optionally, where applicable reference respective issues: `Fixes #123`
-## Codestyle and Testing
+## Code Quality
+The Eclipse BaSyx Python project emphasizes high code quality.
+To achieve this, we apply best practices where possible and have developed an extensive suite of tests that are
+expected to pass for each Pull Request to the project.
+
+### Codestyle
Our code follows the [PEP 8 -- Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/)
with the following exceptions:
- Line length is allowed to be up to 120 characters, though lines up to 100 characters are preferred.
Additionally, we use [PEP 484 -- Type Hints](https://www.python.org/dev/peps/pep-0484/) throughout the code to enable type checking the code.
-Before submitting any changes, make sure to let `mypy` and `pycodestyle` check your code and run the unit tests with
-Python's builtin `unittest`. To install the required tools, use:
+Before submitting any changes to the SDK, make sure to let `mypy` and `pycodestyle` check your code and run the unit
+tests with Python's builtin `unittest`.
+
+### Testing
+There are many automated checks implemented in the CI pipelines of this project, all of which are expected to pass
+before new code can be added:
+
+- We check that the Python packages can be built.
+- We run the developed unittests and aim for a code coverage of at least 80%.
+- We perform static code analysis for type-checking and codestyle, not just in the code itself, but also in codeblocks
+ that are inside docstrings and the `README.md`.
+- We check that the automatically generated developer documentation compiles.
+- We check that the Python Versions we support match between the different subprojects in the monorepository and are
+ not End of Life.
+- We check that the year in the copyright headers in each file (stemming from the license) is correct.
+
+> [!note]
+> We strongly suggest to run the tests locally, before submitting a Pull Request, in order to accelerate the review
+> process.
+
+### Testing the SDK
+For testing the SDK locally on your machine, you can install the required tools like so:
```bash
pip install .[dev]
```
@@ -142,12 +167,51 @@ Running all checks:
mypy basyx test
pycodestyle --max-line-length 120 basyx test
python -m unittest
+coverage run --source basyx --branch -m unittest
+coverage report -m
```
-We aim to cover our code with test by at least 80%. To check test coverage, you can use `coverage`:
+We aim to cover our code with tests by at least 80%.
+
+This should help you sort out the most important bugs in your code.
+Note that there are more checks that run in the CI once you open a Pull Request.
+If you want to run the additional checks, please refer to the [CI definition](./.github/workflows/ci.yml).
+### Testing the Server
+Currently, the automated server tests are still under development.
+To test that the server is working, we expect to at least be able to build the docker images and run a container
+of it without error.
+
+For that, you need to have Docker installed on your system.
+In the directory with the `Dockerfile`:
```bash
-pip install coverage
+docker build -t basyx-python-server .
+docker run --name basyx-python-server basyx-python-server
+```
+Wait until you see the line:
+```
+INFO success: quit_on_failure entered RUNNING state
+```
+
+### Testing the Compliance Tool
+For the Compliance Tool, you can install the required tools like this (from the `./compliance_tool` directory):
+```bash
+pip install -e ../sdk[dev]
+pip install .[dev]
+```
+The first line installs the SDK and its dependencies, the second the developer dependencies for the compliance tool
+itself.
+
+Then you can run the checks via:
+```bash
+mypy basyx test
+pycodestyle --max-line-length 120 basyx test
+python -m unittest
coverage run --source basyx --branch -m unittest
coverage report -m
```
+
+We aim to cover our code with tests by at least 80%.
+This should help you sort out the most important bugs in your code.
+Note that there are more checks that run in the CI once you open a Pull Request.
+If you want to run the additional checks, please refer to the [CI definition](./.github/workflows/ci.yml).
diff --git a/README.md b/README.md
index f271c4d5b..82e56055f 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ These are the currently implemented specifications:
| Specification | Version |
|---------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Part 1: Metamodel | [v3.0.1 (01001-3-1)](https://industrialdigitaltwin.org/wp-content/uploads/2024/06/IDTA-01001-3-0-1_SpecificationAssetAdministrationShell_Part1_Metamodel.pdf) |
+| Part 1: Metamodel | [v3.0.1 (01001-3-0-1)](https://industrialdigitaltwin.org/wp-content/uploads/2024/06/IDTA-01001-3-0-1_SpecificationAssetAdministrationShell_Part1_Metamodel.pdf) |
| 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) |
| Part 2: API | [v3.0 (01002-3-0)](https://industrialdigitaltwin.org/en/wp-content/uploads/sites/2/2023/06/IDTA-01002-3-0_SpecificationAssetAdministrationShell_Part2_API_.pdf) |
| Part 3a: Data Specification IEC 61360 | [v3.0 (01003-a-3-0)](https://industrialdigitaltwin.org/wp-content/uploads/2023/04/IDTA-01003-a-3-0_SpecificationAssetAdministrationShell_Part3a_DataSpecification_IEC61360.pdf) |
diff --git a/compliance_tool/README.md b/compliance_tool/README.md
index 40face78e..185e157a0 100644
--- a/compliance_tool/README.md
+++ b/compliance_tool/README.md
@@ -6,7 +6,7 @@ Following functionalities are supported:
* create an aasx file with xml or json files compliant to the official schema containing example Asset Administration
Shell elements
* check if a given xml or json file is compliant to the official schema
-* check if a given xml, json or aasx file is readable even if it is not compliant to the offical schema
+* check if a given xml, json or aasx file is readable even if it is not compliant to the official schema
* check if the data in a given xml, json or aasx file is the same as the example data
* check if two given xml, json or aasx files contain the same Asset Administration Shell elements in any order
diff --git a/compliance_tool/aas_compliance_tool/cli.py b/compliance_tool/aas_compliance_tool/cli.py
index cfd008140..3ffd7d219 100644
--- a/compliance_tool/aas_compliance_tool/cli.py
+++ b/compliance_tool/aas_compliance_tool/cli.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 the Eclipse BaSyx Authors
+# 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.
@@ -19,11 +19,12 @@
from basyx.aas.adapter import aasx
from basyx.aas.adapter.xml import write_aas_xml_file
-from basyx.aas.compliance_tool import compliance_check_xml as compliance_tool_xml, \
- compliance_check_json as compliance_tool_json, compliance_check_aasx as compliance_tool_aasx
+from aas_compliance_tool import compliance_check_xml as compliance_tool_xml, \
+ compliance_check_json as compliance_tool_json, \
+ compliance_check_aasx as compliance_tool_aasx
from basyx.aas.adapter.json import write_aas_json_file
from basyx.aas.examples.data import create_example, create_example_aas_binding, TEST_PDF_FILE
-from basyx.aas.compliance_tool.state_manager import ComplianceToolStateManager, Status
+from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
def parse_cli_arguments() -> argparse.ArgumentParser:
@@ -71,7 +72,7 @@ def parse_cli_arguments() -> argparse.ArgumentParser:
'f or file_compare: checks if two given files contain the same aas elements in any order')
parser.add_argument('file_1', help="path to file 1")
parser.add_argument('file_2', nargs='?', default=None, help="path to file 2: is required if action f or files is "
- "choosen")
+ "chosen")
parser.add_argument('-v', '--verbose', help="Print detailed information for each check. Multiple -v options "
"increase the verbosity. 1: Detailed error information, 2: Additional "
"detailed success information", action='count', default=0)
diff --git a/compliance_tool/aas_compliance_tool/compliance_check_aasx.py b/compliance_tool/aas_compliance_tool/compliance_check_aasx.py
index 74c0a3356..9dfbb982c 100644
--- a/compliance_tool/aas_compliance_tool/compliance_check_aasx.py
+++ b/compliance_tool/aas_compliance_tool/compliance_check_aasx.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 the Eclipse BaSyx Authors
+# 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.
@@ -19,14 +19,14 @@
import pyecma376_2
-from . import compliance_check_json, compliance_check_xml
+from aas_compliance_tool import compliance_check_json, compliance_check_xml
from basyx.aas import model
from basyx.aas.adapter import aasx
from basyx.aas.adapter.xml import xml_deserialization
from basyx.aas.adapter.json import json_deserialization
from basyx.aas.examples.data import example_aas, create_example_aas_binding
from basyx.aas.examples.data._helper import AASDataChecker, DataChecker
-from .state_manager import ComplianceToolStateManager, Status
+from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
def check_deserialization(file_path: str, state_manager: ComplianceToolStateManager,
diff --git a/compliance_tool/aas_compliance_tool/compliance_check_json.py b/compliance_tool/aas_compliance_tool/compliance_check_json.py
index 1469e8355..2050f570c 100644
--- a/compliance_tool/aas_compliance_tool/compliance_check_json.py
+++ b/compliance_tool/aas_compliance_tool/compliance_check_json.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 the Eclipse BaSyx Authors
+# 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.
@@ -20,7 +20,7 @@
from basyx.aas.adapter.json import json_deserialization
from basyx.aas.examples.data import example_aas, create_example
from basyx.aas.examples.data._helper import AASDataChecker
-from .state_manager import ComplianceToolStateManager, Status
+from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
JSON_SCHEMA_FILE = os.path.join(os.path.dirname(__file__), 'schemas/aasJSONSchema.json')
diff --git a/compliance_tool/aas_compliance_tool/compliance_check_xml.py b/compliance_tool/aas_compliance_tool/compliance_check_xml.py
index 51fb06068..6d4e10c55 100644
--- a/compliance_tool/aas_compliance_tool/compliance_check_xml.py
+++ b/compliance_tool/aas_compliance_tool/compliance_check_xml.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 the Eclipse BaSyx Authors
+# 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.
@@ -20,7 +20,7 @@
from basyx.aas.adapter.xml import xml_deserialization
from basyx.aas.examples.data import example_aas, create_example
from basyx.aas.examples.data._helper import AASDataChecker
-from .state_manager import ComplianceToolStateManager, Status
+from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
XML_SCHEMA_FILE = os.path.join(os.path.dirname(__file__), 'schemas/aasXMLSchema.xsd')
diff --git a/compliance_tool/aas_compliance_tool/state_manager.py b/compliance_tool/aas_compliance_tool/state_manager.py
index 3116fe150..33153038f 100644
--- a/compliance_tool/aas_compliance_tool/state_manager.py
+++ b/compliance_tool/aas_compliance_tool/state_manager.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 the Eclipse BaSyx Authors
+# 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.
diff --git a/compliance_tool/pyproject.toml b/compliance_tool/pyproject.toml
new file mode 100644
index 000000000..c907f90df
--- /dev/null
+++ b/compliance_tool/pyproject.toml
@@ -0,0 +1,60 @@
+[build-system]
+requires = [
+ "setuptools>=45",
+ "wheel",
+ "setuptools_scm[toml]>=6.2"
+]
+build-backend = "setuptools.build_meta"
+
+[tool.setuptools_scm]
+# Configure setuptools_scm for version management:
+# - Automatically infers the version number from the most recent git tag
+# - Generates a version.py file in the package directory
+# - Allows for automatic versioning between releases (e.g., 1.0.1.dev4+g12345)
+# If you want to use the version anywhere in the code, use
+# ```
+# from aas_compliance_tool.version import version
+# print(f"Project version: {version}")
+# ```
+root = ".." # Defines the path to the root of the repository
+version_file = "aas_compliance_tool/version.py"
+
+[project]
+name = "aas_compliance_tool"
+dynamic = ["version"]
+description = "AAS compliance checker based on the Eclipse BaSyx Python SDK"
+authors = [
+ { name = "The AAS Compliance Tool authors", email = "admins@iat.rwth-aachen.de" }
+]
+readme = "README.md"
+license = { file = "LICENSE" }
+classifiers = [
+ "Programming Language :: Python :: 3",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ "Development Status :: 5 - Production/Stable"
+]
+requires-python = ">=3.9"
+dependencies = [
+ "pyecma376-2>=0.2.4",
+ "jsonschema>=4.21.1",
+ "basyx-python-sdk>=1.0.0",
+]
+
+[project.optional-dependencies]
+dev = [
+ "mypy",
+ "pycodestyle",
+ "codeblocks",
+ "coverage",
+]
+
+[project.urls]
+"Homepage" = "https://github.com/eclipse-basyx/basyx-python-sdk"
+
+[tool.setuptools]
+packages = { find = { include = ["aas_compliance_tool*"], exclude = ["test*"] } }
+
+[tool.setuptools.package-data]
+aas_compliance_tool = ["py.typed"]
+
diff --git a/compliance_tool/requirements.txt b/compliance_tool/requirements.txt
deleted file mode 100644
index 89a1d39df..000000000
--- a/compliance_tool/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-pyecma376-2>=0.2.4
-jsonschema>=4.21.1
-basyx-python-sdk>=1.0.0
diff --git a/compliance_tool/setup.py b/compliance_tool/setup.py
deleted file mode 100644
index c214d4bcd..000000000
--- a/compliance_tool/setup.py
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2019-2021 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.
-#
-# SPDX-License-Identifier: MIT
-
-import setuptools
-
-with open("README.md", "r", encoding='utf-8') as fh:
- long_description = fh.read()
-
-setuptools.setup(
- name="aas_compliance_tool",
- version="1.0.0",
- author="The AAS Compliance Tool authors",
- description="AAS compliance checker based on the Eclipse BaSyx Python SDK",
- long_description=long_description,
- long_description_content_type="text/markdown",
- url="https://github.com/rwth-iat/aas-compliance-tool",
- packages=setuptools.find_packages(exclude=["test", "test.*"]),
- zip_safe=False,
- package_data={
- "aas_compliance_tool": ["py.typed", "schemas/aasJSONSchema.json", "schemas/aasXMLSchema.xsd"],
- },
- classifiers=[
- "Programming Language :: Python :: 3",
- "License :: OSI Approved :: MIT License",
- "Operating System :: OS Independent",
- "Development Status :: 5 - Production/Stable",
- ],
- entry_points={
- 'console_scripts': [
- "aas-compliance-check = aas_compliance_tool:main"
- ]
- },
- python_requires='>=3.8',
- install_requires=[
- 'pyecma376-2>=0.2.4',
- 'basyx-python-sdk>=1.0.0',
- ]
-)
diff --git a/compliance_tool/test/files/test_demo_full_example.json b/compliance_tool/test/files/test_demo_full_example.json
index 31fde424d..68e154f73 100644
--- a/compliance_tool/test/files/test_demo_full_example.json
+++ b/compliance_tool/test/files/test_demo_full_example.json
@@ -499,7 +499,7 @@
},
{
"language": "de",
- "text": "Ein Beispiel-BillofMaterial-Submodel f\u00fcr eine Test-Anwendung"
+ "text": "Ein Beispiel-BillOfMaterial-Submodel f\u00fcr eine Test-Anwendung"
}
],
"modelType": "Submodel",
@@ -1309,7 +1309,7 @@
},
{
"language": "de",
- "text": "Beispielswert f\u00fcr ein MulitLanguageProperty-Element"
+ "text": "Beispielwert f\u00fcr ein MultiLanguageProperty-Element"
}
],
"valueId": {
@@ -2281,7 +2281,7 @@
},
{
"language": "de",
- "text": "Beispiel MulitLanguageProperty Element"
+ "text": "Beispiel MultiLanguageProperty Element"
}
],
"modelType": "MultiLanguageProperty",
@@ -2301,7 +2301,7 @@
},
{
"language": "de",
- "text": "Beispielswert f\u00fcr ein MulitLanguageProperty-Element"
+ "text": "Beispielwert f\u00fcr ein MultiLanguageProperty-Element"
}
]
},
@@ -2825,7 +2825,7 @@
},
{
"language": "de",
- "text": "Beispiel MulitLanguageProperty Element"
+ "text": "Beispiel MultiLanguageProperty Element"
}
],
"modelType": "MultiLanguageProperty",
diff --git a/compliance_tool/test/files/test_demo_full_example.xml b/compliance_tool/test/files/test_demo_full_example.xml
index 39fae5599..759e3c403 100644
--- a/compliance_tool/test/files/test_demo_full_example.xml
+++ b/compliance_tool/test/files/test_demo_full_example.xml
@@ -486,7 +486,7 @@
de
- Ein Beispiel-BillofMaterial-Submodel für eine Test-Anwendung
+ Ein Beispiel-BillOfMaterial-Submodel für eine Test-Anwendung
@@ -1424,7 +1424,7 @@
de
- Beispielswert für ein MulitLanguageProperty-Element
+ Beispielwert für ein MultiLanguageProperty-Element
@@ -2109,7 +2109,7 @@
de
- Beispiel MulitLanguageProperty Element
+ Beispiel MultiLanguageProperty Element
@@ -2128,7 +2128,7 @@
de
- Beispielswert für ein MulitLanguageProperty-Element
+ Beispielwert für ein MultiLanguageProperty-Element
@@ -2632,7 +2632,7 @@
de
- Beispiel MulitLanguageProperty Element
+ Beispiel MultiLanguageProperty Element
diff --git a/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/data.json b/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/data.json
index 7172735e6..7ddb5f17c 100644
--- a/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/data.json
+++ b/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/data.json
@@ -507,7 +507,7 @@
},
{
"language": "de",
- "text": "Ein Beispiel-BillofMaterial-Submodel f\u00fcr eine Test-Anwendung"
+ "text": "Ein Beispiel-BillOfMaterial-Submodel f\u00fcr eine Test-Anwendung"
}
],
"modelType": "Submodel",
@@ -1317,7 +1317,7 @@
},
{
"language": "de",
- "text": "Beispielswert f\u00fcr ein MulitLanguageProperty-Element"
+ "text": "Beispielwert f\u00fcr ein MultiLanguageProperty-Element"
}
],
"valueId": {
@@ -2289,7 +2289,7 @@
},
{
"language": "de",
- "text": "Beispiel MulitLanguageProperty Element"
+ "text": "Beispiel MultiLanguageProperty Element"
}
],
"modelType": "MultiLanguageProperty",
@@ -2309,7 +2309,7 @@
},
{
"language": "de",
- "text": "Beispielswert f\u00fcr ein MulitLanguageProperty-Element"
+ "text": "Beispielwert f\u00fcr ein MultiLanguageProperty-Element"
}
]
},
@@ -2833,7 +2833,7 @@
},
{
"language": "de",
- "text": "Beispiel MulitLanguageProperty Element"
+ "text": "Beispiel MultiLanguageProperty Element"
}
],
"modelType": "MultiLanguageProperty",
diff --git a/compliance_tool/test/files/test_demo_full_example_wrong_attribute.json b/compliance_tool/test/files/test_demo_full_example_wrong_attribute.json
index 8f816697d..d748e7908 100644
--- a/compliance_tool/test/files/test_demo_full_example_wrong_attribute.json
+++ b/compliance_tool/test/files/test_demo_full_example_wrong_attribute.json
@@ -499,7 +499,7 @@
},
{
"language": "de",
- "text": "Ein Beispiel-BillofMaterial-Submodel f\u00fcr eine Test-Anwendung"
+ "text": "Ein Beispiel-BillOfMaterial-Submodel f\u00fcr eine Test-Anwendung"
}
],
"modelType": "Submodel",
@@ -1309,7 +1309,7 @@
},
{
"language": "de",
- "text": "Beispielswert f\u00fcr ein MulitLanguageProperty-Element"
+ "text": "Beispielwert f\u00fcr ein MultiLanguageProperty-Element"
}
],
"valueId": {
@@ -2281,7 +2281,7 @@
},
{
"language": "de",
- "text": "Beispiel MulitLanguageProperty Element"
+ "text": "Beispiel MultiLanguageProperty Element"
}
],
"modelType": "MultiLanguageProperty",
@@ -2301,7 +2301,7 @@
},
{
"language": "de",
- "text": "Beispielswert f\u00fcr ein MulitLanguageProperty-Element"
+ "text": "Beispielwert f\u00fcr ein MultiLanguageProperty-Element"
}
]
},
@@ -2825,7 +2825,7 @@
},
{
"language": "de",
- "text": "Beispiel MulitLanguageProperty Element"
+ "text": "Beispiel MultiLanguageProperty Element"
}
],
"modelType": "MultiLanguageProperty",
diff --git a/compliance_tool/test/files/test_demo_full_example_wrong_attribute.xml b/compliance_tool/test/files/test_demo_full_example_wrong_attribute.xml
index 061ee58b6..94c47d36b 100644
--- a/compliance_tool/test/files/test_demo_full_example_wrong_attribute.xml
+++ b/compliance_tool/test/files/test_demo_full_example_wrong_attribute.xml
@@ -486,7 +486,7 @@
de
- Ein Beispiel-BillofMaterial-Submodel für eine Test-Anwendung
+ Ein Beispiel-BillOfMaterial-Submodel für eine Test-Anwendung
@@ -1424,7 +1424,7 @@
de
- Beispielswert für ein MulitLanguageProperty-Element
+ Beispielwert für ein MultiLanguageProperty-Element
@@ -2109,7 +2109,7 @@
de
- Beispiel MulitLanguageProperty Element
+ Beispiel MultiLanguageProperty Element
@@ -2128,7 +2128,7 @@
de
- Beispielswert für ein MulitLanguageProperty-Element
+ Beispielwert für ein MultiLanguageProperty-Element
@@ -2632,7 +2632,7 @@
de
- Beispiel MulitLanguageProperty Element
+ Beispiel MultiLanguageProperty Element
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/data.xml b/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/data.xml
index c0eb40769..cb203c9d8 100644
--- a/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/data.xml
+++ b/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/data.xml
@@ -494,7 +494,7 @@
de
- Ein Beispiel-BillofMaterial-Submodel für eine Test-Anwendung
+ Ein Beispiel-BillOfMaterial-Submodel für eine Test-Anwendung
@@ -1432,7 +1432,7 @@
de
- Beispielswert für ein MulitLanguageProperty-Element
+ Beispielwert für ein MultiLanguageProperty-Element
@@ -2117,7 +2117,7 @@
de
- Beispiel MulitLanguageProperty Element
+ Beispiel MultiLanguageProperty Element
@@ -2136,7 +2136,7 @@
de
- Beispielswert für ein MulitLanguageProperty-Element
+ Beispielwert für ein MultiLanguageProperty-Element
@@ -2640,7 +2640,7 @@
de
- Beispiel MulitLanguageProperty Element
+ Beispiel MultiLanguageProperty Element
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/data.xml b/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/data.xml
index 5e952db2f..7f2531f6c 100644
--- a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/data.xml
+++ b/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/data.xml
@@ -494,7 +494,7 @@
de
- Ein Beispiel-BillofMaterial-Submodel für eine Test-Anwendung
+ Ein Beispiel-BillOfMaterial-Submodel für eine Test-Anwendung
@@ -1432,7 +1432,7 @@
de
- Beispielswert für ein MulitLanguageProperty-Element
+ Beispielwert für ein MultiLanguageProperty-Element
@@ -2117,7 +2117,7 @@
de
- Beispiel MulitLanguageProperty Element
+ Beispiel MultiLanguageProperty Element
@@ -2136,7 +2136,7 @@
de
- Beispielswert für ein MulitLanguageProperty-Element
+ Beispielwert für ein MultiLanguageProperty-Element
@@ -2640,7 +2640,7 @@
de
- Beispiel MulitLanguageProperty Element
+ Beispiel MultiLanguageProperty Element
diff --git a/compliance_tool/test/test_aas_compliance_tool.py b/compliance_tool/test/test_aas_compliance_tool.py
index 8cd3004db..cb631cf9c 100644
--- a/compliance_tool/test/test_aas_compliance_tool.py
+++ b/compliance_tool/test/test_aas_compliance_tool.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 the Eclipse BaSyx Authors
+# 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.
@@ -13,8 +13,6 @@
import io
import tempfile
-
-import basyx.aas.compliance_tool
from basyx.aas import model
from basyx.aas.adapter import aasx
from basyx.aas.adapter.json import read_aas_json_file
@@ -25,14 +23,21 @@
def _run_compliance_tool(*compliance_tool_args, **kwargs) -> subprocess.CompletedProcess:
"""
- This function runs the compliance tool using subprocess.run() while adjusting the PYTHONPATH environment variable
- and setting the stdout and stderr parameters of subprocess.run() to PIPE.
+ This function runs the compliance tool using subprocess.run()
+ and sets the stdout and stderr parameters of subprocess.run() to PIPE.
Positional arguments are passed to the compliance tool, while keyword arguments are passed to subprocess.run().
"""
env = os.environ.copy()
- env['PYTHONPATH'] = "{}:{}".format(os.environ.get('PYTHONPATH', ''),
- os.path.join(os.path.dirname(basyx.__file__), os.pardir))
- compliance_tool_path = os.path.join(os.path.dirname(basyx.aas.compliance_tool.__file__), 'cli.py')
+ parent_dir = os.path.join(
+ os.path.dirname(os.path.dirname(__file__)),
+ 'aas_compliance_tool'
+ )
+ env["PYTHONPATH"] = parent_dir + os.pathsep + env.get("PYTHONPATH", "")
+ compliance_tool_path = os.path.join(
+ os.path.dirname(os.path.dirname(__file__)),
+ 'aas_compliance_tool',
+ 'cli.py'
+ )
return subprocess.run([sys.executable, compliance_tool_path] + list(compliance_tool_args), stdout=subprocess.PIPE,
stderr=subprocess.PIPE, env=env, **kwargs)
diff --git a/compliance_tool/test/test_compliance_check_aasx.py b/compliance_tool/test/test_compliance_check_aasx.py
index 78d9e04b7..953d4e35e 100644
--- a/compliance_tool/test/test_compliance_check_aasx.py
+++ b/compliance_tool/test/test_compliance_check_aasx.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 the Eclipse BaSyx Authors
+# 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.
@@ -7,8 +7,8 @@
import os
import unittest
-from basyx.aas.compliance_tool import compliance_check_aasx as compliance_tool
-from basyx.aas.compliance_tool.state_manager import ComplianceToolStateManager, Status
+from aas_compliance_tool import compliance_check_aasx as compliance_tool
+from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
class ComplianceToolAASXTest(unittest.TestCase):
@@ -20,7 +20,9 @@ def test_check_deserialization(self) -> None:
compliance_tool.check_deserialization(file_path_1, manager)
self.assertEqual(2, len(manager.steps))
self.assertEqual(Status.FAILED, manager.steps[0].status)
- self.assertIn("is not a valid ECMA376-2 (OPC) file", manager.format_step(0, verbose_level=1))
+ # we should expect a FileNotFound error here since the file does not exist and that is the first error
+ # aasx.py will throw if you try to open a file that does not exist.
+ self.assertIn("No such file or directory:", manager.format_step(0, verbose_level=1))
self.assertEqual(Status.NOT_EXECUTED, manager.steps[1].status)
# Todo add more tests for checking wrong aasx files
diff --git a/compliance_tool/test/test_compliance_check_json.py b/compliance_tool/test/test_compliance_check_json.py
index b6201d108..c738c0f13 100644
--- a/compliance_tool/test/test_compliance_check_json.py
+++ b/compliance_tool/test/test_compliance_check_json.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 the Eclipse BaSyx Authors
+# 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.
@@ -7,8 +7,8 @@
import os
import unittest
-import basyx.aas.compliance_tool.compliance_check_json as compliance_tool
-from basyx.aas.compliance_tool.state_manager import ComplianceToolStateManager, Status
+from aas_compliance_tool import compliance_check_json as compliance_tool
+from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
class ComplianceToolJsonTest(unittest.TestCase):
diff --git a/compliance_tool/test/test_compliance_check_xml.py b/compliance_tool/test/test_compliance_check_xml.py
index a1658e508..63089e186 100644
--- a/compliance_tool/test/test_compliance_check_xml.py
+++ b/compliance_tool/test/test_compliance_check_xml.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 the Eclipse BaSyx Authors
+# 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.
@@ -7,8 +7,8 @@
import os
import unittest
-import basyx.aas.compliance_tool.compliance_check_xml as compliance_tool
-from basyx.aas.compliance_tool.state_manager import ComplianceToolStateManager, Status
+from aas_compliance_tool import compliance_check_xml as compliance_tool
+from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
class ComplianceToolXmlTest(unittest.TestCase):
diff --git a/compliance_tool/test/test_state_manager.py b/compliance_tool/test/test_state_manager.py
index fc1c8260e..7654203d1 100644
--- a/compliance_tool/test/test_state_manager.py
+++ b/compliance_tool/test/test_state_manager.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 the Eclipse BaSyx Authors
+# 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.
@@ -7,7 +7,7 @@
import logging
import unittest
-from basyx.aas.compliance_tool.state_manager import ComplianceToolStateManager, Status
+from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
from basyx.aas.examples.data._helper import DataChecker
diff --git a/etc/scripts/set_copyright_year.sh b/etc/scripts/set_copyright_year.sh
index 5ff1fd5f9..de6c97eea 100755
--- a/etc/scripts/set_copyright_year.sh
+++ b/etc/scripts/set_copyright_year.sh
@@ -9,7 +9,36 @@
#
# The script will check the first two lines for a copyright
# notice (in case the first line is a shebang).
+#
+# Run this script with --check to have it raise an error if it
+# would change anything.
+
+
+# Set CHECK_MODE based on whether --check is passed
+ CHECK_MODE=false
+ if [[ "$1" == "--check" ]]; then
+ CHECK_MODE=true
+ shift # Remove --check from the arguments
+ fi
while read -rd $'\0' year file; do
- sed -i "1,2s/^\(# Copyright (c) \)[[:digit:]]\{4,\}/\1$year/" "$file"
-done < <(git ls-files -z "$@" | xargs -0I{} git log -1 -z --format="%ad {}" --date="format:%Y" "{}")
+
+ # Extract the first year from the copyright notice
+ current_year=$(sed -n '1,2s/^\(# Copyright (c) \)\([[:digit:]]\{4,\}\).*/\2/p' "$file")
+
+ # Skip the file if no year is found
+ if [[ -z "$current_year" ]]; then
+ continue
+ fi
+
+ if $CHECK_MODE && [[ "$current_year" != "$year" ]]; then
+ echo "Error: Copyright year mismatch in file $file. Expected $year, found $current_year."
+ exit 1
+ fi
+
+ if ! $CHECK_MODE && [[ "$current_year" != "$year" ]]; then
+ sed -i "1,2s/^\(# Copyright (c) \)[[:digit:]]\{4,\}/\1$year/" "$file"
+ echo "Updated copyright year in $file"
+ fi
+done < <(git ls-files -z "$@" | xargs -0I{} git log -1 -z --format="%cd {}" --date="format:%Y" -- "{}")
+
diff --git a/sdk/.readthedocs.yaml b/sdk/.readthedocs.yaml
index 92880ab17..e64e5daaf 100644
--- a/sdk/.readthedocs.yaml
+++ b/sdk/.readthedocs.yaml
@@ -12,6 +12,7 @@ sphinx:
configuration: docs/source/conf.py
python:
- install:
- - requirements: requirements.txt
- - requirements: docs/add-requirements.txt
+ install:
+ - method: pip
+ path: .
+ - requirements: docs/add-requirements.txt
diff --git a/sdk/README.md b/sdk/README.md
index fde12b465..f63f7afcb 100644
--- a/sdk/README.md
+++ b/sdk/README.md
@@ -38,19 +38,23 @@ file.
## Dependencies
The BaSyx Python SDK requires the following Python packages to be installed for production usage. These dependencies are listed in
-`setup.py` to be fetched automatically when installing with `pip`:
+`pyproject.toml` to be fetched automatically when installing with `pip`:
* `lxml` (BSD 3-clause License, using `libxml2` under MIT License)
* `python-dateutil` (BSD 3-clause License)
* `pyecma376-2` (Apache License v2.0)
* `urllib3` (MIT License)
* `Werkzeug` (BSD 3-clause License)
-Optional production usage dependencies:
-* For using the Compliance Tool to validate JSON files against the JSON Schema: `jsonschema` and its
-dependencies (MIT License, Apache License, PSF License)
-
-Development/testing/documentation/example dependencies (see `requirements.txt`):
-* `jsonschema` and its dependencies (MIT License, Apache License, PSF License)
+Development/testing/documentation/example dependencies:
+* `mypy` (MIT License)
+* `pycodestyle` (MIT License)
+* `codeblocks` (Apache License v2.0)
+* `coverage` (Apache License v2.0)
+* `jsonschema` (MIT License, Apache License, PSF License)
+* `schemathesis` (MIT License)
+* `hypothesis` (MPL v2.0)
+* `lxml-stubs` (Apache License)
+* `types-python-dateutil` (Apache License v2.0)
Dependencies for building the documentation (see `docs/add-requirements.txt`):
* `Sphinx` and its dependencies (BSD 2-clause License, MIT License, Apache License)
@@ -130,7 +134,3 @@ For further examples and tutorials, check out the `basyx.aas.examples`-package.
### Documentation
A detailed, complete API documentation is available on Read the Docs: https://basyx-python-sdk.readthedocs.io
-
-### Compliance Tool
-
-The compliance tool functionality moved to [github.com/rwth-iat/aas-compliance-tool](https://github.com/rwth-iat/aas-compliance-tool).
diff --git a/sdk/basyx/aas/adapter/_generic.py b/sdk/basyx/aas/adapter/_generic.py
index 6a37c7412..79c98fc8c 100644
--- a/sdk/basyx/aas/adapter/_generic.py
+++ b/sdk/basyx/aas/adapter/_generic.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/adapter/aasx.py b/sdk/basyx/aas/adapter/aasx.py
index 1c27640fa..0b66e533e 100644
--- a/sdk/basyx/aas/adapter/aasx.py
+++ b/sdk/basyx/aas/adapter/aasx.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2024 the Eclipse BaSyx Authors
+# 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.
@@ -402,17 +402,19 @@ def write_aas(self,
concept_descriptions: List[model.ConceptDescription] = []
for identifiable in objects_to_be_written:
for semantic_id in traversal.walk_semantic_ids_recursive(identifiable):
+ if isinstance(semantic_id, model.ExternalReference):
+ continue
if not isinstance(semantic_id, model.ModelReference) \
or semantic_id.type is not model.ConceptDescription:
- logger.info("semanticId %s does not reference a ConceptDescription.", str(semantic_id))
continue
try:
cd = semantic_id.resolve(object_store)
except KeyError:
- logger.info("ConceptDescription for semanticId %s not found in object store.", str(semantic_id))
+ logger.warning("ConceptDescription for semanticId %s not found in object store. Skipping it.",
+ str(semantic_id))
continue
except model.UnexpectedTypeError as e:
- logger.error("semanticId %s resolves to %s, which is not a ConceptDescription",
+ logger.error("semanticId %s resolves to %s, which is not a ConceptDescription. Skipping it.",
str(semantic_id), e.value)
continue
concept_descriptions.append(cd)
diff --git a/sdk/basyx/aas/adapter/http.py b/sdk/basyx/aas/adapter/http.py
index a4d7ab289..12bd533f3 100644
--- a/sdk/basyx/aas/adapter/http.py
+++ b/sdk/basyx/aas/adapter/http.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2024 the Eclipse BaSyx Authors
+# 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.
@@ -42,13 +42,14 @@
import io
import json
import itertools
+import urllib
from lxml import etree
import werkzeug.exceptions
import werkzeug.routing
import werkzeug.urls
import werkzeug.utils
-from werkzeug.exceptions import BadRequest, Conflict, NotFound, UnprocessableEntity
+from werkzeug.exceptions import BadRequest, Conflict, NotFound
from werkzeug.routing import MapAdapter, Rule, Submount
from werkzeug.wrappers import Request, Response
from werkzeug.datastructures import FileStorage
@@ -252,7 +253,13 @@ def http_exception_to_response(exception: werkzeug.exceptions.HTTPException, res
def is_stripped_request(request: Request) -> bool:
- return request.args.get("level") == "core"
+ level = request.args.get("level")
+ if level not in {"deep", "core", None}:
+ raise BadRequest(f"Level {level} is not a valid level!")
+ extent = request.args.get("extent")
+ if extent is not None:
+ raise werkzeug.exceptions.NotImplemented(f"The parameter extent is not yet implemented for this server!")
+ return level == "core"
T = TypeVar("T")
@@ -300,7 +307,7 @@ def check_type_supportance(cls, type_: type):
@classmethod
def assert_type(cls, obj: object, type_: Type[T]) -> T:
if not isinstance(obj, type_):
- raise UnprocessableEntity(f"Object {obj!r} is not of type {type_.__name__}!")
+ raise BadRequest(f"Object {obj!r} is not of type {type_.__name__}!")
return obj
@classmethod
@@ -312,10 +319,10 @@ def json_list(cls, data: Union[str, bytes], expect_type: Type[T], stripped: bool
parsed = json.loads(data, cls=decoder)
if not isinstance(parsed, list):
if not expect_single:
- raise UnprocessableEntity(f"Expected List[{expect_type.__name__}], got {parsed!r}!")
+ raise BadRequest(f"Expected List[{expect_type.__name__}], got {parsed!r}!")
parsed = [parsed]
elif expect_single:
- raise UnprocessableEntity(f"Expected a single object of type {expect_type.__name__}, got {parsed!r}!")
+ raise BadRequest(f"Expected a single object of type {expect_type.__name__}, got {parsed!r}!")
# TODO: the following is ugly, but necessary because references aren't self-identified objects
# in the json schema
# TODO: json deserialization will always create an ModelReference[Submodel], xml deserialization determines
@@ -339,7 +346,7 @@ def json_list(cls, data: Union[str, bytes], expect_type: Type[T], stripped: bool
return [constructor(obj, *args) for obj in parsed]
except (KeyError, ValueError, TypeError, json.JSONDecodeError, model.AASConstraintViolation) as e:
- raise UnprocessableEntity(str(e)) from e
+ raise BadRequest(str(e)) from e
return [cls.assert_type(obj, expect_type) for obj in parsed]
@@ -369,9 +376,9 @@ def xml(cls, data: bytes, expect_type: Type[T], stripped: bool) -> T:
f: BaseException = e
while f.__cause__ is not None:
f = f.__cause__
- raise UnprocessableEntity(str(f)) from e
+ raise BadRequest(str(f)) from e
except (etree.XMLSyntaxError, model.AASConstraintViolation) as e:
- raise UnprocessableEntity(str(e)) from e
+ raise BadRequest(str(e)) from e
return cls.assert_type(rv, expect_type)
@classmethod
@@ -647,13 +654,13 @@ def _get_submodel_reference(cls, aas: model.AssetAdministrationShell, submodel_i
@classmethod
def _get_slice(cls, request: Request, iterator: Iterable[T]) -> Tuple[Iterator[T], int]:
limit_str = request.args.get('limit', default="10")
- cursor_str = request.args.get('cursor', default="0")
+ cursor_str = request.args.get('cursor', default="1")
try:
- limit, cursor = int(limit_str), int(cursor_str)
+ limit, cursor = int(limit_str), int(cursor_str) - 1 # cursor is 1-indexed
if limit < 0 or cursor < 0:
raise ValueError
except ValueError:
- raise BadRequest("Cursor and limit must be positive integers!")
+ raise BadRequest("Limit can not be negative, cursor must be positive!")
start_index = cursor
end_index = cursor + limit
paginated_slice = itertools.islice(iterator, start_index, end_index)
@@ -667,14 +674,31 @@ def _get_shells(self, request: Request) -> Tuple[Iterator[model.AssetAdministrat
aas = filter(lambda shell: shell.id_short == id_short, aas)
asset_ids = request.args.getlist("assetIds")
- if asset_ids is not None:
- # Decode and instantiate SpecificAssetIds
- # This needs to be a list, otherwise we can only iterate it once.
- specific_asset_ids: List[model.SpecificAssetId] = list(
- map(lambda asset_id: HTTPApiDecoder.base64urljson(asset_id, model.SpecificAssetId, False), asset_ids))
- # Filter AAS based on these SpecificAssetIds
- aas = filter(lambda shell: all(specific_asset_id in shell.asset_information.specific_asset_id
- for specific_asset_id in specific_asset_ids), aas)
+
+ if asset_ids:
+ specific_asset_ids = []
+ global_asset_ids = []
+
+ for asset_id in asset_ids:
+ asset_id_json = base64url_decode(asset_id)
+ asset_dict = json.loads(asset_id_json)
+ name = asset_dict["name"]
+ value = asset_dict["value"]
+
+ if name == "specificAssetId":
+ decoded_specific_id = HTTPApiDecoder.json_list(value, model.SpecificAssetId,
+ False, True)[0]
+ specific_asset_ids.append(decoded_specific_id)
+ elif name == "globalAssetId":
+ global_asset_ids.append(value)
+
+ # Filter AAS based on both SpecificAssetIds and globalAssetIds
+ aas = filter(lambda shell: (
+ (not specific_asset_ids or all(specific_asset_id in shell.asset_information.specific_asset_id
+ for specific_asset_id in specific_asset_ids)) and
+ (len(global_asset_ids) <= 1 and
+ (not global_asset_ids or shell.asset_information.global_asset_id in global_asset_ids))
+ ), aas)
paginated_aas, end_index = self._get_slice(request, aas)
return paginated_aas, end_index
@@ -843,7 +867,7 @@ def delete_aas_submodel_refs_submodel(self, request: Request, url_args: Dict, re
aas.commit()
return response_t()
- def aas_submodel_refs_redirect(self, request: Request, url_args: Dict, map_adapter: MapAdapter,
+ def aas_submodel_refs_redirect(self, request: Request, url_args: Dict, map_adapter: MapAdapter, response_t=None,
**_kwargs) -> Response:
aas = self._get_shell(url_args)
# the following makes sure the reference exists
@@ -852,7 +876,7 @@ def aas_submodel_refs_redirect(self, request: Request, url_args: Dict, map_adapt
"submodel_id": url_args["submodel_id"]
}, force_external=True)
if "path" in url_args:
- redirect_url += url_args["path"] + "/"
+ redirect_url += "/" + url_args["path"]
if request.query_string:
redirect_url += "?" + request.query_string.decode("ascii")
return werkzeug.utils.redirect(redirect_url, 307)
@@ -940,6 +964,8 @@ def get_submodel_submodel_elements_id_short_path(self, request: Request, url_arg
def get_submodel_submodel_elements_id_short_path_metadata(self, request: Request, url_args: Dict,
response_t: Type[APIResponse], **_kwargs) -> Response:
submodel_element = self._get_submodel_submodel_elements_id_short_path(url_args)
+ if isinstance(submodel_element, model.Capability) or isinstance(submodel_element, model.Operation):
+ raise BadRequest(f"{submodel_element.id_short} does not allow the content modifier metadata!")
return response_t(submodel_element, stripped=True)
def get_submodel_submodel_elements_id_short_path_reference(self, request: Request, url_args: Dict,
diff --git a/sdk/basyx/aas/adapter/json/json_deserialization.py b/sdk/basyx/aas/adapter/json/json_deserialization.py
index 455a309e6..78e3713f5 100644
--- a/sdk/basyx/aas/adapter/json/json_deserialization.py
+++ b/sdk/basyx/aas/adapter/json/json_deserialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
@@ -340,10 +340,16 @@ def _construct_model_reference(cls, dct: Dict[str, object], type_: Type[T], obje
if reference_type is not model.ModelReference:
raise ValueError(f"Expected a reference of type {model.ModelReference}, got {reference_type}!")
keys = [cls._construct_key(key_data) for key_data in _get_ts(dct, "keys", list)]
- if keys and not issubclass(KEY_TYPES_CLASSES_INVERSE.get(keys[-1].type, type(None)), type_):
+ last_key_type = KEY_TYPES_CLASSES_INVERSE.get(keys[-1].type, type(None))
+ if keys and not issubclass(last_key_type, type_):
logger.warning("type %s of last key of reference to %s does not match reference type %s",
keys[-1].type.name, " / ".join(str(k) for k in keys), type_.__name__)
- return object_class(tuple(keys), type_, cls._construct_reference(_get_ts(dct, 'referredSemanticId', dict))
+ # Infer type the model refence points to using `last_key_type` instead of `type_`.
+ # `type_` is often a `model.Referable`, which is more abstract than e.g. a `model.ConceptDescription`,
+ # leading to information loss while deserializing.
+ # TODO Remove this fix, when this function is called with correct `type_`
+ return object_class(tuple(keys), last_key_type,
+ cls._construct_reference(_get_ts(dct, 'referredSemanticId', dict))
if 'referredSemanticId' in dct else None)
@classmethod
diff --git a/sdk/basyx/aas/adapter/json/json_serialization.py b/sdk/basyx/aas/adapter/json/json_serialization.py
index 8c6a671f1..f7d6626eb 100644
--- a/sdk/basyx/aas/adapter/json/json_serialization.py
+++ b/sdk/basyx/aas/adapter/json/json_serialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/adapter/xml/xml_deserialization.py b/sdk/basyx/aas/adapter/xml/xml_deserialization.py
index ab78d3c2e..4063b8df7 100644
--- a/sdk/basyx/aas/adapter/xml/xml_deserialization.py
+++ b/sdk/basyx/aas/adapter/xml/xml_deserialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/adapter/xml/xml_serialization.py b/sdk/basyx/aas/adapter/xml/xml_serialization.py
index 2620dad84..7e6585cea 100644
--- a/sdk/basyx/aas/adapter/xml/xml_serialization.py
+++ b/sdk/basyx/aas/adapter/xml/xml_serialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/backend/backends.py b/sdk/basyx/aas/backend/backends.py
index d921a52d5..31be12628 100644
--- a/sdk/basyx/aas/backend/backends.py
+++ b/sdk/basyx/aas/backend/backends.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/backend/couchdb.py b/sdk/basyx/aas/backend/couchdb.py
index 43428ae9d..4b6f43611 100644
--- a/sdk/basyx/aas/backend/couchdb.py
+++ b/sdk/basyx/aas/backend/couchdb.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/backend/local_file.py b/sdk/basyx/aas/backend/local_file.py
index 3f6bcb8dc..ec0757375 100644
--- a/sdk/basyx/aas/backend/local_file.py
+++ b/sdk/basyx/aas/backend/local_file.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/examples/data/_helper.py b/sdk/basyx/aas/examples/data/_helper.py
index 231ba2ade..a4c3edfce 100644
--- a/sdk/basyx/aas/examples/data/_helper.py
+++ b/sdk/basyx/aas/examples/data/_helper.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
@@ -213,6 +213,18 @@ def _check_has_semantics_equal(self, object_: model.HasSemantics, expected_objec
:return: The value of expression to be used in control statements
"""
self.check_attribute_equal(object_, "semantic_id", expected_object.semantic_id)
+ if isinstance(expected_object.semantic_id, model.ModelReference) and self.check(
+ isinstance(object_.semantic_id, model.ModelReference),
+ "{} must be a ModelReference".format(repr(object_)),
+ ): # type: ignore
+ self.check(
+ object_.semantic_id.type == expected_object.semantic_id.type, # type: ignore
+ "ModelReference type {} of {} must be equal to {}".format(
+ object_.semantic_id.type, # type: ignore
+ repr(object_),
+ expected_object.semantic_id.type,
+ ),
+ )
for suppl_semantic_id in expected_object.supplemental_semantic_id:
given_semantic_id = self._find_reference(suppl_semantic_id, object_.supplemental_semantic_id)
self.check(given_semantic_id is not None, f"{object_!r} must have supplementalSemanticId",
diff --git a/sdk/basyx/aas/examples/data/example_aas.py b/sdk/basyx/aas/examples/data/example_aas.py
index c1c0db426..e093c603a 100644
--- a/sdk/basyx/aas/examples/data/example_aas.py
+++ b/sdk/basyx/aas/examples/data/example_aas.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/examples/data/example_aas_mandatory_attributes.py b/sdk/basyx/aas/examples/data/example_aas_mandatory_attributes.py
index d86f6f5fb..a18f3cbc6 100644
--- a/sdk/basyx/aas/examples/data/example_aas_mandatory_attributes.py
+++ b/sdk/basyx/aas/examples/data/example_aas_mandatory_attributes.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/examples/data/example_aas_missing_attributes.py b/sdk/basyx/aas/examples/data/example_aas_missing_attributes.py
index 45f3df3c2..83232914a 100644
--- a/sdk/basyx/aas/examples/data/example_aas_missing_attributes.py
+++ b/sdk/basyx/aas/examples/data/example_aas_missing_attributes.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/examples/data/example_submodel_template.py b/sdk/basyx/aas/examples/data/example_submodel_template.py
index 6f5a7789b..358e06b05 100644
--- a/sdk/basyx/aas/examples/data/example_submodel_template.py
+++ b/sdk/basyx/aas/examples/data/example_submodel_template.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/examples/tutorial_serialization_deserialization.py b/sdk/basyx/aas/examples/tutorial_serialization_deserialization.py
index 6e1f6a889..6c99409a7 100755
--- a/sdk/basyx/aas/examples/tutorial_serialization_deserialization.py
+++ b/sdk/basyx/aas/examples/tutorial_serialization_deserialization.py
@@ -10,6 +10,7 @@
from basyx.aas import model
import basyx.aas.adapter.xml
+import basyx.aas.adapter.json
# 'Details of the Asset Administration Shell' specifies multiple official serialization formats for AAS data. In this
# tutorial, we show how the Eclipse BaSyx Python library can be used to serialize AAS objects into JSON or XML and to
diff --git a/sdk/basyx/aas/model/_string_constraints.py b/sdk/basyx/aas/model/_string_constraints.py
index aa9833cf2..376f76b0e 100644
--- a/sdk/basyx/aas/model/_string_constraints.py
+++ b/sdk/basyx/aas/model/_string_constraints.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/model/aas.py b/sdk/basyx/aas/model/aas.py
index 684a1ff06..4a9fb4640 100644
--- a/sdk/basyx/aas/model/aas.py
+++ b/sdk/basyx/aas/model/aas.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py
index a1dc9f1a9..a93e3cb59 100644
--- a/sdk/basyx/aas/model/base.py
+++ b/sdk/basyx/aas/model/base.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/model/concept.py b/sdk/basyx/aas/model/concept.py
index 6ee0155b9..a9cbd1a33 100644
--- a/sdk/basyx/aas/model/concept.py
+++ b/sdk/basyx/aas/model/concept.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/model/datatypes.py b/sdk/basyx/aas/model/datatypes.py
index 24a7fb261..d5acc6d45 100644
--- a/sdk/basyx/aas/model/datatypes.py
+++ b/sdk/basyx/aas/model/datatypes.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/model/provider.py b/sdk/basyx/aas/model/provider.py
index 0d4430de5..ac50d33da 100644
--- a/sdk/basyx/aas/model/provider.py
+++ b/sdk/basyx/aas/model/provider.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
@@ -11,7 +11,7 @@
"""
import abc
-from typing import MutableSet, Iterator, Generic, TypeVar, Dict, List, Optional, Iterable
+from typing import MutableSet, Iterator, Generic, TypeVar, Dict, List, Optional, Iterable, Set
from .base import Identifier, Identifiable
@@ -84,6 +84,15 @@ class DictObjectStore(AbstractObjectStore[_IT], Generic[_IT]):
"""
A local in-memory object store for :class:`~basyx.aas.model.base.Identifiable` objects, backed by a dict, mapping
:class:`~basyx.aas.model.base.Identifier` → :class:`~basyx.aas.model.base.Identifiable`
+
+ .. note::
+ The `DictObjectStore` provides efficient retrieval of objects by their :class:`~basyx.aas.model.base.Identifier`
+ However, since object stores are not referenced via the parent attribute, the mapping is not updated
+ if the :class:`~basyx.aas.model.base.Identifier` of an :class:`~basyx.aas.model.base.Identifiable` changes.
+ For more details, see [issue #216](https://github.com/eclipse-basyx/basyx-python-sdk/issues/216).
+ As a result, the `DictObjectStore` is unsuitable for storing objects whose
+ :class:`~basyx.aas.model.base.Identifier` may change.
+ In such cases, consider using a :class:`~.SetObjectStore` instead.
"""
def __init__(self, objects: Iterable[_IT] = ()) -> None:
self._backend: Dict[Identifier, _IT] = {}
@@ -117,6 +126,64 @@ def __iter__(self) -> Iterator[_IT]:
return iter(self._backend.values())
+class SetObjectStore(AbstractObjectStore[_IT], Generic[_IT]):
+ """
+ A local in-memory object store for :class:`~basyx.aas.model.base.Identifiable` objects, backed by a set
+
+ .. note::
+ The `SetObjectStore` is slower than the `DictObjectStore` for retrieval of objects, because it has to iterate
+ over all objects to find the one with the correct :class:`~basyx.aas.model.base.Identifier`.
+ On the other hand, the `SetObjectStore` is more secure, because it is less affected by changes in the
+ :class:`~basyx.aas.model.base.Identifier` of an :class:`~basyx.aas.model.base.Identifiable` object.
+ Therefore, the `SetObjectStore` is suitable for storing objects whose :class:`~basyx.aas.model.base.Identifier`
+ may change.
+ """
+ def __init__(self, objects: Iterable[_IT] = ()) -> None:
+ self._backend: Set[_IT] = set()
+ for x in objects:
+ self.add(x)
+
+ def get_identifiable(self, identifier: Identifier) -> _IT:
+ for x in self._backend:
+ if x.id == identifier:
+ return x
+ raise KeyError(identifier)
+
+ def add(self, x: _IT) -> None:
+ if x in self:
+ # Object is already in store
+ return
+ try:
+ self.get_identifiable(x.id)
+ except KeyError:
+ self._backend.add(x)
+ else:
+ raise KeyError(f"Identifiable object with same id {x.id} is already stored in this store")
+
+ def discard(self, x: _IT) -> None:
+ self._backend.discard(x)
+
+ def remove(self, x: _IT) -> None:
+ self._backend.remove(x)
+
+ def __contains__(self, x: object) -> bool:
+ if isinstance(x, Identifier):
+ try:
+ self.get_identifiable(x)
+ return True
+ except KeyError:
+ return False
+ if not isinstance(x, Identifiable):
+ return False
+ return x in self._backend
+
+ def __len__(self) -> int:
+ return len(self._backend)
+
+ def __iter__(self) -> Iterator[_IT]:
+ return iter(self._backend)
+
+
class ObjectProviderMultiplexer(AbstractObjectProvider):
"""
A multiplexer for Providers of :class:`~basyx.aas.model.base.Identifiable` objects.
diff --git a/sdk/basyx/aas/util/identification.py b/sdk/basyx/aas/util/identification.py
index c3647ef90..74cccd99a 100644
--- a/sdk/basyx/aas/util/identification.py
+++ b/sdk/basyx/aas/util/identification.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/basyx/aas/util/traversal.py b/sdk/basyx/aas/util/traversal.py
index 43a55af44..0b288ddf7 100644
--- a/sdk/basyx/aas/util/traversal.py
+++ b/sdk/basyx/aas/util/traversal.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/pyproject.toml b/sdk/pyproject.toml
index 7722b16ef..0fe88d1bd 100644
--- a/sdk/pyproject.toml
+++ b/sdk/pyproject.toml
@@ -36,24 +36,24 @@ classifiers = [
]
requires-python = ">=3.9"
dependencies = [
- "jsonschema~=4.7",
- "lxml>=4.2,<5",
+ "lxml>=5.3",
"python-dateutil>=2.8,<3",
- "types-python-dateutil",
- "pyecma376-2>=0.2.4",
+ "pyecma376-2>=1.0.1",
"urllib3>=1.26,<3",
"Werkzeug>=3.0.3,<4",
- "schemathesis~=3.7",
- "hypothesis~=6.13",
- "lxml-stubs~=0.5.1",
]
[project.optional-dependencies]
dev = [
- "mypy",
+ "mypy==1.15.0",
"pycodestyle",
"codeblocks",
"coverage",
+ "schemathesis~=3.7",
+ "jsonschema~=4.7",
+ "hypothesis~=6.13",
+ "types-python-dateutil",
+ "lxml-stubs~=0.5.1",
]
[project.urls]
diff --git a/sdk/test/_helper/setup_testdb.py b/sdk/test/_helper/setup_testdb.py
index d668ac865..20f56c4d9 100755
--- a/sdk/test/_helper/setup_testdb.py
+++ b/sdk/test/_helper/setup_testdb.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/adapter/aasx/test_aasx.py b/sdk/test/adapter/aasx/test_aasx.py
index 9f1bb5ebb..a83c60186 100644
--- a/sdk/test/adapter/aasx/test_aasx.py
+++ b/sdk/test/adapter/aasx/test_aasx.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2022 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/adapter/json/test_json_deserialization.py b/sdk/test/adapter/json/test_json_deserialization.py
index 28da288cd..9272bdf98 100644
--- a/sdk/test/adapter/json/test_json_deserialization.py
+++ b/sdk/test/adapter/json/test_json_deserialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/adapter/json/test_json_serialization.py b/sdk/test/adapter/json/test_json_serialization.py
index 01bc65c95..8e9bc8d01 100644
--- a/sdk/test/adapter/json/test_json_serialization.py
+++ b/sdk/test/adapter/json/test_json_serialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/adapter/json/test_json_serialization_deserialization.py b/sdk/test/adapter/json/test_json_serialization_deserialization.py
index 9b016e17a..e33921a21 100644
--- a/sdk/test/adapter/json/test_json_serialization_deserialization.py
+++ b/sdk/test/adapter/json/test_json_serialization_deserialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/adapter/xml/test_xml_deserialization.py b/sdk/test/adapter/xml/test_xml_deserialization.py
index e62ebaa08..331ad98c5 100644
--- a/sdk/test/adapter/xml/test_xml_deserialization.py
+++ b/sdk/test/adapter/xml/test_xml_deserialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/adapter/xml/test_xml_serialization.py b/sdk/test/adapter/xml/test_xml_serialization.py
index 11328f62f..e07e10255 100644
--- a/sdk/test/adapter/xml/test_xml_serialization.py
+++ b/sdk/test/adapter/xml/test_xml_serialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/adapter/xml/test_xml_serialization_deserialization.py b/sdk/test/adapter/xml/test_xml_serialization_deserialization.py
index 2e06c44d8..7a4f5c12d 100644
--- a/sdk/test/adapter/xml/test_xml_serialization_deserialization.py
+++ b/sdk/test/adapter/xml/test_xml_serialization_deserialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/backend/test_backends.py b/sdk/test/backend/test_backends.py
index a7f025558..e0beee8f8 100644
--- a/sdk/test/backend/test_backends.py
+++ b/sdk/test/backend/test_backends.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2022 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/backend/test_couchdb.py b/sdk/test/backend/test_couchdb.py
index 5e6c2fbe4..89fe992de 100644
--- a/sdk/test/backend/test_couchdb.py
+++ b/sdk/test/backend/test_couchdb.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/backend/test_local_file.py b/sdk/test/backend/test_local_file.py
index 4a8186ac3..22aaa3155 100644
--- a/sdk/test/backend/test_local_file.py
+++ b/sdk/test/backend/test_local_file.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2022 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/examples/test_examples.py b/sdk/test/examples/test_examples.py
index cacf9395c..5695e3b94 100644
--- a/sdk/test/examples/test_examples.py
+++ b/sdk/test/examples/test_examples.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/examples/test_helpers.py b/sdk/test/examples/test_helpers.py
index 754d5cdfb..faca8602b 100644
--- a/sdk/test/examples/test_helpers.py
+++ b/sdk/test/examples/test_helpers.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/examples/test_tutorials.py b/sdk/test/examples/test_tutorials.py
index b82c3bf7b..2b5dbe54e 100644
--- a/sdk/test/examples/test_tutorials.py
+++ b/sdk/test/examples/test_tutorials.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2022 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/model/test_aas.py b/sdk/test/model/test_aas.py
index 27ce13b4d..3b974c04f 100644
--- a/sdk/test/model/test_aas.py
+++ b/sdk/test/model/test_aas.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/model/test_base.py b/sdk/test/model/test_base.py
index 3cf7ea2d0..1e0432a58 100644
--- a/sdk/test/model/test_base.py
+++ b/sdk/test/model/test_base.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/model/test_concept.py b/sdk/test/model/test_concept.py
index c8bfefce9..94fc497b8 100644
--- a/sdk/test/model/test_concept.py
+++ b/sdk/test/model/test_concept.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/model/test_datatypes.py b/sdk/test/model/test_datatypes.py
index 8021414db..b83c5e5fb 100644
--- a/sdk/test/model/test_datatypes.py
+++ b/sdk/test/model/test_datatypes.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/model/test_provider.py b/sdk/test/model/test_provider.py
index 549232cc3..68fb01cff 100644
--- a/sdk/test/model/test_provider.py
+++ b/sdk/test/model/test_provider.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/model/test_string_constraints.py b/sdk/test/model/test_string_constraints.py
index 5adb15523..55d5789f5 100644
--- a/sdk/test/model/test_string_constraints.py
+++ b/sdk/test/model/test_string_constraints.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/model/test_submodel.py b/sdk/test/model/test_submodel.py
index 15746bd5d..37a5792df 100644
--- a/sdk/test/model/test_submodel.py
+++ b/sdk/test/model/test_submodel.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2023 the Eclipse BaSyx Authors
+# 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.
diff --git a/sdk/test/util/test_identification.py b/sdk/test/util/test_identification.py
index 9a43ea16b..ebfed8606 100644
--- a/sdk/test/util/test_identification.py
+++ b/sdk/test/util/test_identification.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2022 the Eclipse BaSyx Authors
+# 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.
diff --git a/server/Dockerfile b/server/Dockerfile
index 6dc3c4cac..4df672c41 100644
--- a/server/Dockerfile
+++ b/server/Dockerfile
@@ -13,13 +13,12 @@ ENV PYTHONUNBUFFERED=1
RUN apk update && \
apk add --no-cache nginx supervisor gcc musl-dev linux-headers python3-dev git bash && \
pip install uwsgi && \
- pip install --no-cache-dir git+https://github.com/eclipse-basyx/basyx-python-sdk@main#subdirectory=sdk && \
apk del git bash
-COPY uwsgi.ini /etc/uwsgi/
-COPY supervisord.ini /etc/supervisor/conf.d/supervisord.ini
-COPY stop-supervisor.sh /etc/supervisor/stop-supervisor.sh
+COPY server/uwsgi.ini /etc/uwsgi/
+COPY server/supervisord.ini /etc/supervisor/conf.d/supervisord.ini
+COPY server/stop-supervisor.sh /etc/supervisor/stop-supervisor.sh
RUN chmod +x /etc/supervisor/stop-supervisor.sh
# Makes it possible to use a different configuration
@@ -34,12 +33,16 @@ ENV LISTEN_PORT=80
ENV CLIENT_BODY_BUFFER_SIZE=1M
# Copy the entrypoint that will generate Nginx additional configs
-COPY entrypoint.sh /entrypoint.sh
+COPY server/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
-COPY ./app /app
+ENV SETUPTOOLS_SCM_PRETEND_VERSION=1.0.0
+
+COPY ./sdk /sdk
+COPY ./server/app /app
WORKDIR /app
+RUN pip install ../sdk
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.ini"]
diff --git a/server/README.md b/server/README.md
index b0e6b80db..37b649d79 100644
--- a/server/README.md
+++ b/server/README.md
@@ -7,7 +7,7 @@ The server currently implements the following interfaces:
- [Submodel Repository Service][5]
It uses the [HTTP API][1] and the [AASX][7], [JSON][8], and [XML][9] Adapters of the [BaSyx Python SDK][3], to serve regarding files from a given directory.
-The files are only read, chages won't persist.
+The files are only read, changes won't persist.
Alternatively, the container can also be told to use the [Local-File Backend][2] instead, which stores AAS and Submodels as individual JSON files and allows for persistent changes (except supplementary files, i.e. files referenced by `File` submodel elements).
See [below](#options) on how to configure this.
@@ -15,7 +15,7 @@ See [below](#options) on how to configure this.
## Building
The container image can be built via:
```
-$ docker buildx build -t basyx-python-sdk-http-server .
+$ docker build -t basyx-python-server -f Dockerfile ..
```
## Running
@@ -46,17 +46,17 @@ The container can be configured via environment variables:
Putting it all together, the container can be started via the following command:
```
-$ docker run -p 8080:80 -v ./storage:/storage basyx-python-sdk-http-server
+$ docker run -p 8080:80 -v ./storage:/storage basyx-python-server
```
Since Windows uses backslashes instead of forward slashes in paths, you'll have to adjust the path to the storage directory there:
```
-> docker run -p 8080:80 -v .\storage:/storage basyx-python-sdk-http-server
+> docker run -p 8080:80 -v .\storage:/storage basyx-python-server
```
Per default, the server will use the `LOCAL_FILE_READ_ONLY` storage type and serve the API under `/api/v3.0` and read files from `/storage`. If you want to change this, you can do so like this:
```
-$ docker run -p 8080:80 -v ./storage2:/storage2 -e API_BASE_PATH=/api/v3.1 -e STORAGE_TYPE=LOCAL_FILE_BACKEND -e STORAGE_PATH=/storage2 basyx-python-sdk-http-server
+$ docker run -p 8080:80 -v ./storage2:/storage2 -e API_BASE_PATH=/api/v3.1 -e STORAGE_TYPE=LOCAL_FILE_BACKEND -e STORAGE_PATH=/storage2 basyx-python-server
```
## Building and running the image with docker-compose
@@ -70,7 +70,9 @@ This is the exemplary `docker-compose` file for the server:
````yaml
services:
app:
- build: .
+ build:
+ context: ..
+ dockerfile: server/Dockerfile
ports:
- "8080:80"
volumes:
diff --git a/server/compose.yml b/server/compose.yml
index 5465e04ce..666484a5d 100644
--- a/server/compose.yml
+++ b/server/compose.yml
@@ -1,6 +1,8 @@
services:
app:
- build: .
+ build:
+ context: ..
+ dockerfile: server/Dockerfile
ports:
- "8080:80"
volumes: