From aeeb59e8a21c675ebda52b012c944f191d60a05b Mon Sep 17 00:00:00 2001 From: Julian Vogel Date: Mon, 16 Sep 2024 14:37:16 +0200 Subject: [PATCH 1/6] aas.backend: Support urllib3 version 2 --- basyx/aas/backend/couchdb.py | 4 ++-- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/basyx/aas/backend/couchdb.py b/basyx/aas/backend/couchdb.py index cd24a5562..be3354b1f 100644 --- a/basyx/aas/backend/couchdb.py +++ b/basyx/aas/backend/couchdb.py @@ -13,7 +13,7 @@ """ import threading import weakref -from typing import List, Dict, Any, Optional, Iterator, Iterable, Union, Tuple +from typing import List, Dict, Any, Optional, Iterator, Iterable, Union, Tuple, MutableMapping import urllib.parse import urllib.request import urllib.error @@ -108,7 +108,7 @@ def _parse_source(cls, source: str) -> str: @classmethod def do_request(cls, url: str, method: str = "GET", additional_headers: Dict[str, str] = {}, - body: Optional[bytes] = None) -> Dict[str, Any]: + body: Optional[bytes] = None) -> MutableMapping[str, Any]: """ Perform an HTTP(S) request to the CouchDBServer, parse the result and handle errors diff --git a/pyproject.toml b/pyproject.toml index 75aeea661..b8e44a8f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ requires-python = ">=3.8" dependencies = [ "python-dateutil>=2.8,<3", "lxml>=4.2,<5", - "urllib3>=1.26,<2.0", + "urllib3>=1.26,<3", "pyecma376-2>=0.2.4" ] diff --git a/requirements.txt b/requirements.txt index e087cdd18..08aa2a071 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ lxml>=4.2,<5 python-dateutil>=2.8,<3.0 types-python-dateutil pyecma376-2>=0.2.4 -urllib3>=1.26,<2.0 +urllib3>=1.26,<3 Werkzeug>=3.0.3,<4 schemathesis~=3.7 hypothesis~=6.13 From ec5f6ac66bb2020a4d39832d471b2a1418220b74 Mon Sep 17 00:00:00 2001 From: Frosty2500 <125310380+Frosty2500@users.noreply.github.com> Date: Tue, 24 Sep 2024 08:17:25 +0200 Subject: [PATCH 2/6] README.md: Fix specification links (#299) The IDTA seems to have changed the way their links to the specifications work, which led to the links in our README.md to be broken. This adapts the links in the README.md to point to the new and correct locations. --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e9c981a39..ac30e4054 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,12 @@ The Eclipse BaSyx Python project focuses on providing a Python implementation of for Industry 4.0 Systems. These are the currently implemented specifications: -| Specification | Version | -|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------| -| Part 1: Metamodel | [v3.0 (01001-3-0)](https://industrialdigitaltwin.org/content-hub/aasspecifications/idta_01001-3-0_metamodel) | -| Part 2: API | not implemented yet | -| Part 3a: Data Specification IEC 61360 | [v3.0 (01003-a-3-0)](https://industrialdigitaltwin.org/content-hub/aasspecifications/idta_01003-a-3-0_data_specification) | -| Part 5: Package File Format (AASX) | [v3.0 (01005-3-0)](https://industrialdigitaltwin.org/content-hub/aasspecifications/idta-01005-3-0_package_file_format_aasx) | +| Specification | Version | +|---------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Part 1: Metamodel | [v3.0 (01001-3-0)](https://industrialdigitaltwin.org/wp-content/uploads/2023/06/IDTA-01001-3-0_SpecificationAssetAdministrationShell_Part1_Metamodel.pdf) | +| Part 2: API | not implemented yet | +| 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) | +| Part 5: Package File Format (AASX) | [v3.0 (01005-3-0)](https://industrialdigitaltwin.org/wp-content/uploads/2023/04/IDTA-01005-3-0_SpecificationAssetAdministrationShell_Part5_AASXPackageFileFormat.pdf) | ## Features From 3d2b492384f0445c5cb709c0876883ef47bd6600 Mon Sep 17 00:00:00 2001 From: Julian Vogel Date: Mon, 16 Sep 2024 15:16:06 +0200 Subject: [PATCH 3/6] model.datatypes: add test to copy and deepcopy a date --- test/model/test_datatypes.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/model/test_datatypes.py b/test/model/test_datatypes.py index be4666c28..8021414db 100644 --- a/test/model/test_datatypes.py +++ b/test/model/test_datatypes.py @@ -7,6 +7,7 @@ import datetime import math import unittest +import copy import dateutil @@ -177,6 +178,13 @@ def test_parse_partial_dates(self) -> None: model.datatypes.from_xsd("10-10", model.datatypes.GYearMonth) self.assertEqual("Value is not a valid XSD GYearMonth string", str(cm.exception)) + def test_copy_date(self) -> None: + date = model.datatypes.Date(2020, 1, 24) + date_copy_shallow = copy.copy(date) + self.assertEqual(date, date_copy_shallow) + date_copy_deep = copy.deepcopy(date) + self.assertEqual(date, date_copy_deep) + def test_serialize_partial_dates(self) -> None: self.assertEqual("2019", model.datatypes.xsd_repr(model.datatypes.GYear(2019))) self.assertEqual("2019Z", model.datatypes.xsd_repr(model.datatypes.GYear(2019, datetime.timezone.utc))) From 47f5e613fb725f7cf627de1ebb1f4d8f9cd5cda9 Mon Sep 17 00:00:00 2001 From: Julian Vogel Date: Mon, 16 Sep 2024 15:16:20 +0200 Subject: [PATCH 4/6] model.datatypes: fix bytes copy issue for Date class --- basyx/aas/model/datatypes.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/basyx/aas/model/datatypes.py b/basyx/aas/model/datatypes.py index f20c4d045..fb2a622d6 100644 --- a/basyx/aas/model/datatypes.py +++ b/basyx/aas/model/datatypes.py @@ -78,6 +78,17 @@ def __eq__(self, other: object) -> bool: other_tzinfo = other.tzinfo if hasattr(other, 'tzinfo') else None # type: ignore return datetime.date.__eq__(self, other) and self.tzinfo == other_tzinfo + def __copy__(self): + cls = self.__class__ + result = cls.__new__(cls, self.year, self.month, self.day, self.tzinfo) + return result + + def __deepcopy__(self, memo): + cls = self.__class__ + result = cls.__new__(cls, self.year, self.month, self.day, self.tzinfo) + memo[id(self)] = result + return result + # TODO override comparsion operators # TODO add into_datetime function # TODO add includes(:DateTime) -> bool function From 71a602e069e9c63a5eb2946395ba1b4d570b6e8b Mon Sep 17 00:00:00 2001 From: s-heppner Date: Fri, 11 Oct 2024 15:17:48 +0200 Subject: [PATCH 5/6] Refactor dependency management to pyproject.toml Previously, our dependency management was a bit of a mess. We had both a `requirements.txt` and a `pyproject.toml` and some dependencies were only defined in one but not the other. This aims to fix this mess and refactor towards only using `pyproject.toml` to define the dependencies. At the same time, we also clean up the install process, which now works by simply calling `pip install .` or `pip install -e .[dev]` for the developer envrionment. This change is reflected in the `.github/workflows`. Furthermore, we configure the already introduced `setuptools_scm` to automatically infer a version based on the most recent git tag. --- .github/workflows/ci.yml | 30 +++++++++++++++-------------- .github/workflows/release.yml | 6 +++--- .gitignore | 3 +++ CONTRIBUTING.md | 2 +- pyproject.toml | 36 ++++++++++++++++++++++++++--------- requirements.txt | 10 ---------- 6 files changed, 50 insertions(+), 37 deletions(-) delete mode 100644 requirements.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e65ca3b7..8ded4cb0e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,13 +7,19 @@ env: jobs: 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"] env: COUCHDB_ADMIN_PASSWORD: "yo0Quai3" - AAS_SPECS_RELEASE_TAG: "V3.0.7" + # (2024-10-11, s-heppner) + # Specify the tag of the released schema files from https://github.com/admin-shell-io/aas-specs/releases + # that you want to use to test the serialization adapter against. + # Currently, we need to update this manually, however I'm afraid this is not possible to automatically infer, + # since it's heavily dependant of the version of the AAS specification we support. + AAS_SPECS_RELEASE_TAG: "IDTA-01001-3-0-1_schemasV3.0.8" services: couchdb: image: couchdb:3 @@ -22,7 +28,6 @@ jobs: env: COUCHDB_USER: "admin" COUCHDB_PASSWORD: ${{ env.COUCHDB_ADMIN_PASSWORD }} - steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} @@ -37,8 +42,7 @@ jobs: - name: Install Python dependencies run: | python -m pip install --upgrade pip - pip install coverage - pip install -r requirements.txt + pip install .[dev] - name: Setup test config and CouchDB database server run: | python test/_helper/setup_testdb.py -u "admin" -p "$COUCHDB_ADMIN_PASSWORD" @@ -51,8 +55,8 @@ jobs: coverage report -m static-analysis: + # This job runs static code analysis, namely pycodestyle and mypy runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 - name: Set up Python ${{ env.X_PYTHON_VERSION }} @@ -62,8 +66,7 @@ jobs: - name: Install Python dependencies run: | python -m pip install --upgrade pip - pip install pycodestyle mypy - pip install -r requirements.txt + pip install .[dev] - name: Check typing with MyPy run: | mypy basyx test @@ -72,8 +75,8 @@ jobs: pycodestyle --count --max-line-length 120 basyx test readme-codeblocks: + # This job runs the same static code analysis (mypy and pycodestyle) on the codeblocks in our docstrings. runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 - name: Set up Python ${{ env.X_PYTHON_VERSION }} @@ -83,8 +86,7 @@ jobs: - name: Install Python dependencies run: | python -m pip install --upgrade pip - pip install pycodestyle mypy codeblocks - pip install -r requirements.txt + pip install .[dev] - name: Check typing with MyPy run: | mypy <(codeblocks python README.md) @@ -96,8 +98,8 @@ jobs: codeblocks python README.md | python docs: + # This job checks, if the automatically generated documentation using sphinx can be compiled runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 - name: Set up Python ${{ env.X_PYTHON_VERSION }} @@ -107,15 +109,15 @@ jobs: - name: Install Python dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt + pip install . pip install -r docs/add-requirements.txt - name: Check documentation for errors run: | SPHINXOPTS="-a -E -n -W --keep-going" make -C docs html package: + # This job checks if we can build our SDK package runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 - name: Set up Python ${{ env.X_PYTHON_VERSION }} @@ -125,7 +127,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools wheel build + pip install build - name: Create source and wheel dist run: | python -m build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0edc20093..912e7da42 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,8 +6,8 @@ on: jobs: publish: + # This job publishes the package to PyPI runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 - name: Set up Python 3.10 @@ -17,10 +17,10 @@ jobs: - 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 - name: Publish distribution to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: diff --git a/.gitignore b/.gitignore index 587fc5b12..eb5b0c468 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ /test/test_config.ini # Schema files needed for testing /test/adapter/schemas + +# Ignore dynamically generated version file +basyx/version.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 339e42442..90e2d8fdc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -127,7 +127,7 @@ Additionally, we use [PEP 484 -- Type Hints](https://www.python.org/dev/peps/pep 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: ```bash -pip install mypy pycodestyle +pip install .[dev] ``` Running all checks: diff --git a/pyproject.toml b/pyproject.toml index b8e44a8f9..c4d9aeaf8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,26 @@ [build-system] -requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"] +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 basyx.version import version +# print(f"Project version: {version}") +# ``` +write_to = "basyx/version.py" + [project] name = "basyx-python-sdk" -version = "1.0.0" +dynamic = ["version"] description = "The Eclipse BaSyx Python SDK, an implementation of the Asset Administration Shell for Industry 4.0 systems" authors = [ { name = "The Eclipse BaSyx Authors", email = "admins@iat.rwth-aachen.de" } @@ -19,10 +35,16 @@ classifiers = [ ] requires-python = ">=3.8" dependencies = [ - "python-dateutil>=2.8,<3", + "jsonschema~=4.7", "lxml>=4.2,<5", + "python-dateutil>=2.8,<3", + "types-python-dateutil", + "pyecma376-2>=0.2.4", "urllib3>=1.26,<3", - "pyecma376-2>=0.2.4" + "Werkzeug>=3.0.3,<4", + "schemathesis~=3.7", + "hypothesis~=6.13", + "lxml-stubs~=0.5.1", ] [project.optional-dependencies] @@ -37,12 +59,8 @@ dev = [ "Homepage" = "https://github.com/eclipse-basyx/basyx-python-sdk" [tool.setuptools] -packages = ["basyx"] +packages = { find = { include = ["basyx*"], exclude = ["test*"] } } [tool.setuptools.package-data] basyx = ["py.typed"] "basyx.aas.examples.data" = ["TestFile.pdf"] - -[tool.setuptools.exclude-package-data] -"*" = ["test", "test.*"] - diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 08aa2a071..000000000 --- a/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -jsonschema~=4.7 -lxml>=4.2,<5 -python-dateutil>=2.8,<3.0 -types-python-dateutil -pyecma376-2>=0.2.4 -urllib3>=1.26,<3 -Werkzeug>=3.0.3,<4 -schemathesis~=3.7 -hypothesis~=6.13 -lxml-stubs~=0.5.1 From dfca376daae6f74266c6e699fed3dc5147b56760 Mon Sep 17 00:00:00 2001 From: Igor Garmaev <56840636+zrgt@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:09:19 +0200 Subject: [PATCH 6/6] http.py: Fix cursor in response and set JSON Response as default (#311) Previously, our `cursor` in a http response was an integer (as it is a number). The specification asks however for this attribute ot be a string. Therefore, we adapt the type. Fixes #309 Previously, we returned XML by default, if the `accept_mimetypes` had `*/*` in it. This is non- compliant to the specification, which expects JSON to be returned. We fix the issue to return JSON by default now instead. Fixes #310 --- basyx/aas/adapter/http.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/basyx/aas/adapter/http.py b/basyx/aas/adapter/http.py index 4f07f43d1..df3a8e9c9 100644 --- a/basyx/aas/adapter/http.py +++ b/basyx/aas/adapter/http.py @@ -149,7 +149,7 @@ def serialize(self, obj: ResponseData, cursor: Optional[int], stripped: bool) -> data = obj else: data = { - "paging_metadata": {"cursor": cursor}, + "paging_metadata": {"cursor": str(cursor)}, "result": obj } return json.dumps( @@ -226,7 +226,7 @@ def get_response_type(request: Request) -> Type[APIResponse]: "application/xml": XmlResponse, "text/xml": XmlResponseAlt } - if len(request.accept_mimetypes) == 0: + if len(request.accept_mimetypes) == 0 or request.accept_mimetypes.best in (None, "*/*"): return JsonResponse mime_type = request.accept_mimetypes.best_match(response_types) if mime_type is None: