Skip to content

Release 2.1.0#597

Merged
s-heppner merged 71 commits into
mainfrom
develop
Jul 16, 2026
Merged

Release 2.1.0#597
s-heppner merged 71 commits into
mainfrom
develop

Conversation

@s-heppner

Copy link
Copy Markdown
Member

Release Notes

Version 2.1.0 of the BaSyx-Python SDK is a feature release that brings the SDK up to date with the v3.1 line of the AAS specifications and significantly expands the server.

The metamodel now implements Part 1 v3.1.2, with matching updates to the IEC 61360 Data Specification (Part 3a v3.1.1) and the AASX Package File Format (Part 5 v3.1). On the server side, the HTTP API has been updated to Part 2 API v3.1, and the server now ships Registry and Discovery interfaces in addition to the Repository, each available as its own standalone Docker service.

As part of expanding the server, the AbstractObjectProvider and its object stores were refactored to support the updated AAS metamodel through generic typing and naming aligned with Identifiable. This is a refactoring only: the usability of the SDK remains unchanged and you do not have to change any existing code. The previous classes and methods (DictObjectStore, SetObjectStore, LocalFileObjectStore, CouchDBObjectStore, and AbstractObjectProvider.get_identifiable()) are now deprecated and emit warnings, but continue to work as before. Going forward, please use the new DictIdentifiableStore, SetIdentifiableStore, LocalFileIdentifiableStore, CouchDBIdentifiableStore classes and the generic get_item() method instead.

This release implements the following versions of the AAS specification:

Specification Version
Part 1: Metamodel v3.1.2 (01001-3-1-2)
Schemata (JSONSchema, XSD) v3.1.2 (IDTA-01001-3-1-2)
Part 2: API v3.1.1 (01002)
Part 3a: Data Specification IEC 61360 v3.1.1 (01003-a)
Part 5: Package File Format (AASX) v3.1 (01005)

Changelog

Notable:

Improvements:

Bugfixes:

Testing & Internal:

For more details, check out https://projects.eclipse.org/projects/dt.basyx/releases/2.1.0-python

ZANDx1 and others added 30 commits December 8, 2025 11:06
Previously, we did not test that `AASXWrite.write_all_aas_objects` did
indeed that: it includes all `Submodel`s inside the given `ObjectStore`.

This adds a unittest to `test_aasx_utils` that checks for this
expected behavior.

Fixes #209
…ge (#436)

adapter.aasx: Add support for loading and saving thumbnails in AASX package

Previously, the `AASXReader` did not load the thumbnail in the `read_into` function, instead a separate function needed to be called. This behavior did also occur in `AASXWriter` with writing the thumbnail. This was unintuitive, as it required a separat call of the load/save function.

This PR implements the loading/storing of the thumbnail directly in the loading/storing of other supplementary files.

Fixes #435
…434)

`adapter.aasx`: Add `rename_file` to `DictSupplementaryFileContainer`

Before:
There was no supported way to rename supplementary files in
`DictSupplementaryFileContainer`. Workflow had to manipulate internals or create duplicates, making file handling error-prone.

Now:
A public rename operation preserves content de-duplication and resolves name conflicts, simplifying AASX read/write flows and reducing inconsistent mappings.
Previously, multilingual fields (`display_name`, `description`,
`MultiLanguageProperty.value`) could be assigned invalid types, leading to inconsistent
and sometimes invalid JSON/XML serialization.
As a result, this behavior could cause interoperability issues. 
 
The AAS specification requires these fields to be of type
`LangStringSet / MultiLanguageTextType`. This requirement is now
enforced to ensure spec-compliant serialization.

Fixes #416
It appears that we forgot to update some of the copyright notices in
the module docstrings, where actual changes were done in 2026.

This adapts these ocurrences.
Previously the script only output the first problem with the copyright
issue, which is inefficient.

This adapts the script to output all copyright issues.

Fixes #451
Previously, the serialization of `adapter.xml` uses the general
`object_to_xml_element()` method. In it, there was branch to a call of
`lang_string_set_to_xml()` without setting the tag parameter, which
would have caused an exception, if the code was ever run.

We decided to remove the call of `lang_string_set_to_xml` completely as 
this specific case in `object_to_xml()` is considered dead code, as the
XML tag for a `LangStringSet` varies based on its context 
(the tag is determined by the *parent's* attribute), it must always be 
called explicitly with a tag parameter by the *parent's serialization 
logic*. Since there is no scenario where a `LangStringSet` is 
serialized in isolation (It **cannot** be a standalone xml object), 
this branch is unreachable and should be removed to maintain code 
cleanliness. 

Fixes #397
This adds an additional unittest for the `adatper.aasx.AASXReader` 
class. While the reading of AASX was previously implicitly tested by 
first reading and then writing AASX when testing the `AASXWriter`, this
adds dedicated test cases for the reading. 

Furthermore, we fix some years in the copyright notices of some files.

Fixes #441
Just as described in #453, now that the hotfix fixed our CI pipeline on
push to `main`, we need to make sure that `develop` stays up to 
date with `main`. 

Improve CI.yml definition (#453)

Previously, we had some weird bugs with the CI pipeline sometimes
failing (#400), but not always reproducible. Namely, sometimes the CI
failed, due to `mypy` not finding the `sdk` types when running from the
`compliance_tool` environment. Since currently, we are at a point where
it is impossible to reproduce the failing CI (at least for me), I
decided to clean up the job definitions a little bit and make some
things more explicit.

Namely, instead of calling scripts like `pip` or `mypy` from their PATH,
we now explicitly call them via `python -m pip` and `python -m mypy`.
This theoretically ensures, that it always uses the script we just
installed with the dependencies and not something the VM already had
in its path via `actions/setup-python@v5`.
This should ensure that a script like `mypy` actually has all the
necessary dependencies installed.

Secondly, we had a `pip install -e ../sdk[dev]`, therefore installing
the development dependencies of the `sdk` in the `compliance_tool` CI
check. This is technically incorrect, since we use the `sdk` as external
dependency and therefore shouldn't depend on the development
dependencies. I therefore removed this.

Lastly, the `sdk-readme-codeblocks` check uses `bash` syntax. In theory,
the Ubuntu environment should use `bash` by default, but now it is made
explicit.

Fixes #400
Previously, the `AbstractObjectProvider` only worked with
`Identifiables`, which made it incompatible with the new version of the
AAS metamodel.

This renames and restructures both the `AbstractObjectProvider` and the
`AbstractObjectStore`. In all non-abstract subclasses where `Object`
appears in the class or method name, it has been replaced with
`Identifiable`. Old classes are still available with a deprecation
warning.

Moreover, `AbstractObjectProvider` and `AbstractObjectStore` are now
generic to be able to handle more classes than just `Identifiables`.

In order to handle the new `AASDescriptor`, a new class `HasIdentifier`
has been added. It is intended to be an abstract superclass for all
classes that have an identifier, but are not `Identifiables`.

As of now, this PR is intended to serve as a basis for discussion.
Therefore, the documentation has not yet been adapted.

Fixes #428
Previously, the `_object_cache` of the `LocalFileIdentifiableStore` was
initialised empty. Calling `discard()` immediately after store
initialisation raised a `KeyError` if the Identifiable to delete existed
on disk but had not yet been loaded into the in-memory cache.

This adds explicit handling of missing cache entries in `discard()`,
preventing errors when a key is not present in the in-memory cache.
Moreover, a corresponding regression test is added. Finally, the interim
empty-cache workaround in `sync()` is removed.

Fixes #438
Previously, we lacked a tutorial demonstrating how to navigate a
Submodel's hierarchy using IdShorts and IdShortPaths.

This adds a simple tutorial for Submodel navigation and updates the
documentation to reference it. The end user is shown how to navigate
SubmodelElements, such as simple Properties, Property Collections,
Property Lists and Collection Lists.

Fixes #351
Previously, the `EXIT_CODE` of the copyright year check was hardcoded to
the value 0, causing the CI to pass unconditionally.

This fixes the bug by coupling the `EXIT_CODE` to the result of the
copyright year check. Moreover, the check has been extended to
additionally verify the copyright year of the `LICENSE` file.

This also updates the relevant copyright years that were missed before.

Fixes #458
Previously, the `server` was built only with the `repository` API
endpoints in mind. Since we now plan to also support `registry` and
`discovery`, we need to adapt the `./server` project structure.

For this reason, we did the following refactoring:

- This moves the `pyproject.toml` from the `app` directory into the
  server project's top-level directory (`./server/pyproject.toml`).
  This aligns the `server` project directory with the `sdk` and the
  `compliance-tool` project directories. See #466
- This also moves the files for Docker image definition into the
  `./server/docker` subdirectories, where `./server/docker/common` are
  shared files across different Docker images, and
  `./server/docker/repository` and others contain the `Dockerfile`, as
  well as the image specific configuration files. See #468
- This moves the Docker `compose.yaml` into the
  `./server/example_configurations` and its subdirectories. Each example
  configuration should have its own `README.md` specifying what the
  example contains and how to run it. See #468
- Lastly, this moves the repository specific `main.py` to
  `.server/app/services/run_repository.py`

And this adapts (hopefully) all relevant paths in all the different
files accordingly.

Fixes #466
Fixes #468

* server: Fix review issues in server package restructure

- Rename CI job server-package -> server-repository-docker to reflect
  that it specifically builds the repository Docker image
- Narrow pycodestyle scope to 'app test' to match mypy, avoiding
  scanning docker/ and example_configurations/ directories
- Fix broken #running-examples anchor and stale Dockerfile path in
  example_configurations/repository_standalone/README.md

---------

Co-authored-by: zrgt <garmaev@gmx.de>
Previously we had OpenAPI files in the sdk tests, which were used
to check the first iteration of our server implementation.

Since these files are not used anymore, this removes them.

Fixes #476
Previously, the `sdk`'s `util.traversal.walk_submodel()` method skipped
`Entity` and `Operation` children, both `SubmodelElement`s that can
contain further children `SubmodelElement`s.

This fixes this bug and adds additional unittests to check for 
successful traversal of all kinds of `SubmodelElement`s. 

Fixes #423
Previously the LangStringSet checked language tags that were added
to match the format of xx or xx-XX. This did not follow the
documented behavior, which requires all IETF BCP 47 conform
language tags to be accepted.

These changes replaced the previous constraint check, with a
check by regular expression, following the IETF BCP 47 standard.

Fixes #157
Previously, there were lots of occurrences of the old `acplt.org` domain name 
used whenever we needed an URL name example. 

This replaces occurrences of this domain name with `example.org`, one of 
the official [IANA example domains].

Fixes #460

[IANA example domains]: https://www.iana.org/help/example-domains
Previously, the server only contained the AAS Repository API, more
precisely the AAS Repository, Submodel Repository and Concept Description
Repository APIs.

This also adds the Registry and Discovery APIs.
Because of this, we had to refactor the way we start the server, as now
effectively, we have multiple different servers that could be executed
in the `server` package.
Till now we had not implemented the additional primitive data types introduced in the [API specification]. 
Many of them are still not usable since we for example do not support asynchronous operations yet. 
But I still think it is better to have them implemented for later use.

[API specification]: https://industrialdigitaltwin.io/aas-specifications/IDTA-01002/v3.1/specification/interfaces-payload.html#_primitive_data_types

---------

Co-authored-by: s-heppner <iat@s-heppner.com>
* Update SDK to V3.0.1 of AAS Part 5 (AASX)

* Adapt link to specs and adapt link to schema files

---------

Co-authored-by: Moritz Sommer <m.sommer@iat.rwth-aachen.de>
…#421)

This PR updates the AAS- and SubmodelRepository servers from version 3.01 to version 3.1 and fixes some bugs.

Fixes #492
Fixes #443

---------

Co-authored-by: Moritz Sommer <m.sommer@iat.rwth-aachen.de>
Previously, the flag to surpress output in the compliance-tool `cli.py`
was erroneously: "`--quite`", when it should have been `--quiet`, of
course. 

This changes all occurences of this flag to `--quiet`.

# Fixes 505
…ion code (#509)

Previously, the `DataSpecificationIEC61360.value_id` attribute was 
removed from the metamodel of the AAS. However, we forgot to remove part
of the deserialization, so we parsed and immediately discarded this
attribute.

This removes the code artefact.

Fixes #504
This implements the changes of the metamodel for version 3.1.2 of the spec.

Fixes #437

---------

Co-authored-by: Moritz Sommer <m.sommer@iat.rwth-aachen.de>
Co-authored-by: Leon Huang <hleon04@icloud.com>
Co-authored-by: Sercan Sahin <s.sahin@iat.rwth-aachen.de>
Add deprecated discovery route

Previously, we decided not to implement any routes that are deprecated
in the specification. However, as it turns out, for compatibility with
the [basyx-aas-web-ui], the `/lookup/shells` route is required.

This implements the missing route.

Fixes #522

[basyx-aas-web-ui]: https://github.com/eclipse-basyx/basyx-aas-web-ui/

---------

Co-authored-by: s-heppner <iat@s-heppner.com>
Previously we had two implementations of the service specification:
one in interfaces.base.py and the other one in
model.service_specification.py.

This removes the implementation in interfaces.base.py and adapt the
correponding usages to the one in model.service_specification.py.

Fixes #523

---------

Co-authored-by: s-heppner <iat@s-heppner.com>
zrgt and others added 28 commits May 14, 2026 14:31
…que_name (#513)

`DictSupplementaryFileContainer_store_refcount` is designed to track how many `_name_map` entries reference the same content hash, so `delete_file()` can free the underlying bytes only when the last reference is removed. The refcount is never incremented, so files are never freed.

Previously, because `_assign_unique_name()` never increments `_store_refcount`, the count stays at 0 after any `add_file()`. Every `delete_file()` decrements to -1 and the equality check `== 0` is never true, so `_store[hash]` and `_store_refcount[hash]` are never cleaned up. Every file ever added leaks indefinitely.

This fixes this bug by incrementing `_store_refcount[sha] += 1` inside `_assign_unique_name()` when a new `_name_map` entry is created (the first branch of the `while True` loop). Also decrement it (and skip the increment) inside the second branch when a duplicate name already maps to the same hash.

Fixes #495
…rned empty results (#512)

When 2 or more `globalAssetId` query parameters were sent, a
`len(global_asset_ids) <= 1` guard in the filter lambda in
`repository.py` evaluated `False` for every shell, causing an
empty HTTP 200 response with no error.

The guard was likely intended to reject invalid input with a 400
error, not silently discard all results.

The guard is replaced with proper input validation that raises
`BadRequest` when multiple global asset IDs are provided.

Fixes #500
The compliance tool still had code to check a json or xml file
against the defined schema from admin-shell-io/aas-specs-metamodel.
The option to run this schema check was removed earlier from cli.py
with commit af73a4b.

As no other code uses the implemented schema check, this function
is now removed, including the schema files and the unittests.
Previously, `provider.py` imported `ServerAASFromJsonDecoder` directly
from `app.adapter`, which caused a circular import.

Reverted the import to use the `app.adapter` module reference instead,
accessing `adapter.ServerAASFromJsonDecoder` at the call site.

Co-authored-by: s-heppner <iat@s-heppner.com>
PR #370 removed `Referable.commit()` and all call sites in the `server`
handlers without replacing the write-back mechanism. Since then, any
mutation to an object retrieved from `LocalFileIdentifiableStore`,
`LocalFileDescriptorStore`, or `CouchDBIdentifiableStore` was silently
lost on cache eviction, visible only within the same in-process
`WeakValueDictionary` cache entry. A different uWSGI worker, or any
request after the cache entry expired, would re-read the stale on-disk
or on-database state.

There was also a compounding bug: `get_item()` / `get_identifiable_by_hash()`
always re-read from storage even on a cache hit, then called
`update_from()` on the cached object, discarding any in-memory
mutations even within the same request.

This change fixes both issues across all three backends:

- `get_identifiable_by_hash()` / `get_identifiable_by_couchdb_id()`: return the cached instance on a hit instead of overwriting it with a freshly-deserialized copy.
- `get_item()`: check the cache first and return immediately on a hit.
- Add `commit()` to `LocalFileIdentifiableStore` (re-serializes to .json), `LocalFileDescriptorStore` (same), and `CouchDBIdentifiableStore` (PUT with stored `_rev`, updates revision on success).
- `AbstractObjectStore.commit()` is added as a no-op default so in-memory stores (`DictIdentifiableStore`) require no changes.
All mutating handlers in `server/app/interfaces/repository.py` and `registry.py` now call `self.object_store.commit()` after each mutation.
- A regression test `test_mutation_persistence` is added to `sdk/test/backend/test_local_file.py`.

Fixes #552
After refactoring the server package, the CI for building and
releasing did not longer work. Additionally the registry and
discovery services are not published.

These changes fix the build in the release CI for the repository
service and add the missing ones. Builds are now executed using
QEMU for amd64, armv7 and arm64 to create a multi-platform image.

Fixes #555
Previously, the `sync()` method in `model.provider.AbstractObjectStore`
was not tested at all.
This adds a unittest with full line coverage of this method.
Previously, tutorial_navigate_aas.py was not covered by Unit Tests.

This adds the module to the unittests.
Previously `_parse_xsd_date()` and `_parse_xsd_datetime()` raised `ValueError("Negative Dates are not supported by Python")` when given an XSD value with a leading `-` (BCE year). This was technically false as input is not malformed, only not supported.

This change raises `NotImplementedError` instead, allowing callers to distinguish "invalid input" from "known SDK limitation.". Additionally pointing users to https://github.com/eclipse-basyx/basyx-python-sdk/issues to report the lack of implementation.

Fixes #565
The project root contained an empty `1.0.0` file since commit 4f15ce2 [1].
As no usage of this file is known or observable, nor did the commit
author document any reason for adding the file, we concluded that this must
be a mistake from refactoring a `pyproject.toml`.

Therefore the file is now removed.

[1]: 4f15ce2
Previously, JSON deserialization accepted both `ModelReference` and `ExternalReference` for the `dataSpecification` field of `EmbeddedDataSpecification`. As the spec (constraint AASc-3a-050) requires `EmbeddedDataSpecification.dataSpecification` to be an `ExternalReference`, JSON deserialization was tightened to only accept `ExternalReference`. It now matches the requirement and the current behavior of XML deserialization.

Fixes #567
Currently, the JSON and XMOL deserializiers raise an error when parsing Blob or File submodels that do not include a contentType

The contentType attribute has a cardinality of 0..1 for both classes and should therefore be optional.
The implementation now aligns with this specification and defaults to None if the field is absent in the data.

Fixes #561
…d XML (#579)

Previously, the JSON and XML deserializers raised a `KeyError` if a `ValueReferencePair` did not contain a `valueId`. 

However, according to the IDTA specification, `valueId` is explicitly optional (cardinality 0..1).

Fixes #562

---------

Co-authored-by: s-heppner <iat@s-heppner.com>
Previously, the check_directory method was not covered by unit tests.

Unit tests for all three directory state branches are added.
Previously compliance_tool tests relied on handcrafted examples.

Replaced examples by mocking the underlying sdk functionality to
just test the output parsing. Currently only for json and only for
'check_deserialization' and 'check_example'.

Applied the same refactoring to resulting equivalence check of json
implementation and the xml implementation.

Tests for the compliance check for aasx files were refactored so
they use mocking instead of actual files. Additional test cases
were added to ensure

 1. alignment of core properties is checked
 2. content of supplementary files is equal

Replace the old fixture-dependent subprocess tests with direct calls to
main() and parse_cli_arguments(), mocking compliance_check_* modules to
verify routing without touching the filesystem.

Previously the aasx file equivalence check would
 1. still execute subsequent steps if the loading of one file fails.
    This was caused by checking only if `state_manager.status is Status.FAILED`,
    missing that `Status.NOT_EXECUTED > Status.FAILED`.
 2. use blank assertions to ensure core_properties `created` attribute
    is of type `datetime.datetime`.
    This resulted in the compliance_tool failing with `AssertionError`
    in cases that were caused by (1).

Status guards for fast-failing are now corrected to take
`Status.NOT_EXECUTED` into account.
The assertions are removed and replaced with `DataChecker` checks
to inform the user gracefully on problems. `cast(...)` is used
to inform the type-checker about the `isinstance(...)` result.

Previously the supplementary files of the aasx container were not
tested for presence or equality although the check_aas_example did
it. Now a two-way check, coparing presence, content-type and sha256
is implemented. Tests were adapted to expect the additional step.

The previous check on the supplementary file was broken, as it
compared the sha256 value of the TestFile to itself.

Introduced a new step which checks for presence and equality of
supplementary file `/TestFile.pdf` in the AASX container.
Refactored the core property checks to no longer use `assert` in
combination with `try ... except AssertionError`.

The `failed_checks` is a property that returns a fresh iterator
at each call. With `mock_data_checker.return_value.failed_checks = iter([])`
only one iterator is created that may be exhausted at subsequent calls.

The introduced `PropertyMock` fixes this.

As the new test structure uses mocking extensively, the risk for
missing error cases in these tests increases. To overcome this
simple end-to-end integration tests are integrated now, which do
not use mocking but operate on real temporary files.

For all three adapters a full cycle is implemented:
 1.  create -> check_example (both success and fail)
 2.  create x2 -> check_file_equivalence (both sucess and fail)

The help output of the compliance tool is cleaned. Old fragments
of the long ago removed schema-checking functionality were still
in place.

Fixes #486
Previously the API base path (currently `api/v3.1`) was hardcoded across interface modules, dockerfiles and tests. Bumping the version risks drift across the occurences.

This change adds a unittest that extracts the version from each of the currently known files via regex and asserts the version matches.

The risk of newly added occurrences not being tracked remains.

Fixes #535
Previously, only the `DictIdentifiableStore` was tested in the 
unittests.

This extends the unittests by adding a `_STORE_CLASSES` class variable 
and consolidates duplicated test methods into `subTest` loops, covering 
both `Dict` and `Set` store implementations.
…exhausted iterator (#517)

* fix: OrderedNamespaceSet.__setitem__ slice uses exhausted iterator

slice branch stored exhausted islice iterator back into _order instead
of the materialized successful_new_items list, resulting in an empty
slice assignment that discarded all newly added items.

Fixes #494

* fix: OrderedNamespaceSet.__setitem__ int removes old item before adding new

add-before-remove caused false AASConstraintViolation when new item
shares an attribute (e.g. id_short) with the item being replaced.
Fix removes old item first, rolls back if the subsequent add fails.

Fixes #498
Previously, the compliance-tool package name still reflected the time
we kept it separate from the basyx-python-sdk repository and published
it ourselves.

As we now prepare to officially publish it via the Eclipse PyPI account,
we update the name to reflect its affiliation with Eclipse BaSyx via:
`basyx-compliance-tool` in the compliance-tool's `pyproject.toml`.
Previously, UnsignedInt was mapped to the XSD type name xs:unsignedByte in XSD_TYPE_NAMES, and UnsignedByte had no entry. As a result, serialized UnsignedInt values were tagged with the wrong XSD type and UnsignedByte values could not be serialized.

This change corrects the mapping of UnsignedInt to xs:unsignedInt and adds the missing UnsignedByte entry mapped to xs:unsignedByte.

Fixes #560
Until now, each value of a MultiLanguageNameType was constrained to a
maximum length of 64 via check_short_name_type. This never matched the
specification: LangStringNameType/text has had a maximum length of 128
characters since AAS Part 1 Metamodel V3.0, identical to NameType.

MultiLanguageNameType now uses check_name_type instead. The now-unused
check_short_name_type and constrain_short_name_type functions are
removed accordingly.

Fixes #576
Metamodel v3.1.2 changed `RelationshipElement.first` and `second` to be optional. This was missed earlier and required by JSON and XML adapters.

- `AnnotatedRelationshipElement.__init__` now accepts `first` and `second` as `Optional[Reference]`, matching the parent class.
- The JSON and XML deserializers treat both fields as optional default to `None` when absent.
- The JSON serializer omits the fields when `None` (XML serializer already handled this).
- Added tests for this behaviour.

Fixes #525
Previously, `_parse_xsd_datetime` and `_parse_xsd_time` rejected the
XSD end-of-day midnight notation (`24:00:00`), even though the XSD
spec allows it as an alternative representation of midnight. Error
messages on invalid input were also generic ("Value is not a valid
XSD datetime string"), giving no indication of which value failed.

This change accepts `hour=24` when minutes, seconds, and
microseconds are all zero, normalizing it to `00:00:00` of the
following day for `DateTime` (and to `00:00:00` for `Time`, which has
no notion of a following day). Any other non-zero component combined
with `hour=24` now raises a clear, value-specific error. Error
messages throughout both parsers now include the offending value.

Additionally, the `Date`, `DateTime`, and `Time` constructor calls in
these parsers were switched from positional to keyword arguments, so
each parsed field is clearly labeled instead of being a same-typed
`int(...)` in a long positional list.

Fixes #564
* compliance_tool: Fix packaging and CI SDK install

Previously, `compliance_tool/pyproject.toml` was missing the
`[project.scripts]` console entry point, so the `aas-compliance-check`
command documented in the README did not exist and only
`python -m aas_compliance_tool.cli` worked. It also declared its SDK
dependency as a direct reference (`basyx-python-sdk @ file:../sdk`),
which bakes a local path into the built wheel and is rejected by PyPI
on upload, so the package could not be published.

This restores the `aas-compliance-check = aas_compliance_tool.cli:main`
console script and replaces the direct reference with a normal
`basyx-python-sdk>=1.0.0` version constraint, matching the original
`setup.py`.

Switching to the loose `>=1.0.0` constraint broke the compliance-tool
CI jobs, however: they checked out shallow (no tags), so
`setuptools_scm` built the local sdk as `0.1.dev1`. That does not
satisfy `>=1.0.0`, so `pip install .[dev]` uninstalled the local sdk
and pulled `basyx-python-sdk` from PyPI instead, and the tests failed
against a release that predates the local refactors. Both compliance
tool jobs now check out with `fetch-depth: 0` so the local sdk builds
as a `>=1.0.0` dev version and pip keeps it (see #592).

* compliance_tool: Document SDK version caveat in README

Previously, the README described what the compliance tool checks but not
how the installed `basyx-python-sdk` determines which metamodel version
is actually verified. Because the SDK dependency is declared loosely
(`basyx-python-sdk>=1.0.0`), `pip` keeps an already-installed, older SDK
instead of upgrading it, so the tool can silently check against an older
metamodel than the README advertises, with no error to signal the
mismatch.

This adds an `Installation` section that splits the default PyPI install
(`pip install basyx-compliance-tool`) from the developer install against
the sibling `./sdk` source tree, and documents — in the developer flow,
where it is most relevant — that the SDK version must be matched
explicitly until the pinning is tightened (see #592).
Previously, `_construct_blob` in `json_deserialization.py` was
decorated with `@classmethod` twice, producing a
`classmethod(classmethod(func))` wrapper.

On Python 3.10 and 3.12 (used in CI) this went unnoticed, because
`classmethod` chained through the inner descriptor and still resolved
to a callable bound method. Python 3.13 removed that descriptor
chaining, so accessing `cls._construct_blob` now yields a raw,
non-callable `classmethod` object. As a result, deserializing any
`Blob` raised `TypeError: 'classmethod' object is not callable`,
breaking JSON deserialization and the AASX round-trip tests on 3.13.

This change removes the redundant decorator so `_construct_blob`
behaves like all other constructor methods.

This finding motivated #594.
Previously, `GYEAR_RE` and `GYEARMONTH_RE` only matched exactly four
digits and did not allow a leading minus sign. This rejected valid
XSD values such as years with more than four digits (e.g. "20000")
and negative years (e.g. "-2001"), even though the XSD spec permits
both.

Additionally, `GMonthDay.from_date(...)` passed `date.year` instead
of `date.day` to the constructor, and `into_date(...)` on `GYear`/
`GYearMonth` raised Python's cryptic "year -2001 is out of range"
error for negative years, since `datetime.date` does not support
them.

This change widens the regular expressions to accept any number of
digits and an optional leading minus sign, fixes the `GMonthDay.
from_date(...)` argument order bug, and makes `into_date(...)` raise
a clear `ValueError` when called on a G-Class with a negative year.

Fixes #566
Did a general pass over the root, `sdk`, and `server` READMEs and
brought them back in line with the current state of the code.

Previously these files still described the pre-2.1.0 state in a few
places and carried some long-standing typos and stale references, for
example missing server interfaces, an outdated `API_BASE_PATH` default,
a wrong package name and submodule count, and dead links.

The changes correct these outdated sections so the documentation
matches the actual packages, interfaces, and configuration again.
@s-heppner

Copy link
Copy Markdown
Member Author

The eclipsefdn/eca check is a false positive.

@s-heppner s-heppner merged commit 5754d62 into main Jul 16, 2026
28 of 29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.