diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ecdceb9..cf0a25fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,15 +3,47 @@ name: ci on: [push, pull_request] env: - X_PYTHON_VERSION: "3.12" + X_PYTHON_MIN_VERSION: "3.9" + X_PYTHON_MAX_VERSION: "3.12" jobs: + check-python-versions: + # This job checks that the Python Versions we support match and are not End of Life + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./etc/scripts + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }} + uses: actions/setup-python@v2 + with: + python-version: ${{ env.X_PYTHON_MIN_VERSION }} + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install -r ./check_python_versions_requirements.txt + - name: Check Supported Python Versions + run: | + python check_python_versions_supported.py \ + ${{ env.X_PYTHON_MIN_VERSION }} \ + ${{ env.X_PYTHON_MAX_VERSION }} + + - name: Check Python Versions coincide with the SDKs pyproject.toml + run: | + python check_python_versions_coincide.py \ + ../../sdk/pyproject.toml \ + ${{ env.X_PYTHON_MIN_VERSION }} \ + ${{ env.X_PYTHON_MAX_VERSION }} + + # Todo: Check other pyproject.toml here as well, as we add them + sdk-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"] env: COUCHDB_ADMIN_PASSWORD: "yo0Quai3" # (2024-10-11, s-heppner) @@ -33,6 +65,12 @@ jobs: working-directory: ./sdk steps: - uses: actions/checkout@v4 + - name: Verify Matrix Version matches Global Version + run: | + if [ "${{ matrix.python-version }}" != "${{ env.X_PYTHON_MIN_VERSION }}" ] && [ "${{ matrix.python-version }}" != "${{ env.X_PYTHON_MAX_VERSION }}" ]; then + echo "Error: Matrix version ${{ matrix.python-version }} does not match global version." + exit 1 + fi - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: @@ -65,10 +103,10 @@ jobs: working-directory: ./sdk steps: - uses: actions/checkout@v4 - - name: Set up Python ${{ env.X_PYTHON_VERSION }} - uses: actions/setup-python@v4 + - name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }} + uses: actions/setup-python@v2 with: - python-version: ${{ env.X_PYTHON_VERSION }} + python-version: ${{ env.X_PYTHON_MIN_VERSION }} - name: Install Python dependencies run: | python -m pip install --upgrade pip @@ -88,10 +126,10 @@ jobs: working-directory: ./sdk steps: - uses: actions/checkout@v4 - - name: Set up Python ${{ env.X_PYTHON_VERSION }} - uses: actions/setup-python@v4 + - name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }} + uses: actions/setup-python@v2 with: - python-version: ${{ env.X_PYTHON_VERSION }} + python-version: ${{ env.X_PYTHON_MIN_VERSION }} - name: Install Python dependencies run: | python -m pip install --upgrade pip @@ -114,10 +152,10 @@ jobs: working-directory: ./sdk steps: - uses: actions/checkout@v4 - - name: Set up Python ${{ env.X_PYTHON_VERSION }} - uses: actions/setup-python@v4 + - name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }} + uses: actions/setup-python@v2 with: - python-version: ${{ env.X_PYTHON_VERSION }} + python-version: ${{ env.X_PYTHON_MIN_VERSION }} - name: Install Python dependencies run: | python -m pip install --upgrade pip @@ -135,10 +173,10 @@ jobs: working-directory: ./sdk steps: - uses: actions/checkout@v4 - - name: Set up Python ${{ env.X_PYTHON_VERSION }} - uses: actions/setup-python@v4 + - name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }} + uses: actions/setup-python@v2 with: - python-version: ${{ env.X_PYTHON_VERSION }} + python-version: ${{ env.X_PYTHON_MIN_VERSION }} - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/etc/scripts/check_python_versions_coincide.py b/etc/scripts/check_python_versions_coincide.py new file mode 100644 index 00000000..774b67db --- /dev/null +++ b/etc/scripts/check_python_versions_coincide.py @@ -0,0 +1,46 @@ +""" +This helper script checks if the Python versions defined in a `pyproject.toml` coincide with the given `min_version` +and `max_version` and returns an error if they don't. +""" +import re +import argparse +import sys +from packaging.version import Version, InvalidVersion + +def main(pyproject_toml_path: str, min_version: str, max_version: str) -> None: + # Load and check `requires-python` version from `pyproject.toml` + try: + with open(pyproject_toml_path, "r") as f: + pyproject_content = f.read() + + match = re.search(r'requires-python\s*=\s*">=([\d.]+)"', pyproject_content) + if not match: + print(f"Error: `requires-python` field not found or invalid format in `{pyproject_toml_path}`") + sys.exit(1) + + pyproject_version = match.group(1) + if Version(pyproject_version) < Version(min_version): + print(f"Error: Python version in `{pyproject_toml_path}` `requires-python` ({pyproject_version}) " + f"is smaller than `min_version` ({min_version}).") + sys.exit(1) + + except FileNotFoundError: + print(f"Error: File not found: `{pyproject_toml_path}`.") + sys.exit(1) + + print(f"Success: Version in pyproject.toml `requires-python` (>={pyproject_version}) " + f"matches expected versions ([{min_version} to {max_version}]).") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Check Python version support and alignment with pyproject.toml.") + parser.add_argument("pyproject_toml_path", help="Path to the `pyproject.toml` file to check.") + parser.add_argument("min_version", help="The minimum Python version.") + parser.add_argument("max_version", help="The maximum Python version.") + args = parser.parse_args() + + try: + main(args.pyproject_toml_path, args.min_version, args.max_version) + except InvalidVersion: + print("Error: Invalid version format provided.") + sys.exit(1) diff --git a/etc/scripts/check_python_versions_requirements.txt b/etc/scripts/check_python_versions_requirements.txt new file mode 100644 index 00000000..a8597d49 --- /dev/null +++ b/etc/scripts/check_python_versions_requirements.txt @@ -0,0 +1,2 @@ +requests>=2.23 +packaging>=24.2 diff --git a/etc/scripts/check_python_versions_supported.py b/etc/scripts/check_python_versions_supported.py new file mode 100644 index 00000000..5aad31cc --- /dev/null +++ b/etc/scripts/check_python_versions_supported.py @@ -0,0 +1,61 @@ +""" +This helper script checks that the provided `min_version` and `max_version` are supported and released, respectively, +using the API from the great https://github.com/endoflife-date/endoflife.date project. +""" +import argparse +import sys +import requests +from packaging.version import InvalidVersion +from datetime import datetime + +def main(min_version: str, max_version: str) -> None: + # Fetch supported Python versions and check min/max versions + try: + response = requests.get("https://endoflife.date/api/python.json") + response.raise_for_status() + eol_data = response.json() + eol_versions = {entry["cycle"]: {"eol": entry["eol"], "releaseDate": entry["releaseDate"]} for entry in eol_data} + + # Get current date to compare with EoL and release dates + current_date = datetime.now().date() + + # Check min_version EoL status + min_eol_date = eol_versions.get(min_version, {}).get("eol") + if min_eol_date and datetime.strptime(min_eol_date, "%Y-%m-%d").date() <= current_date: + print(f"Error: min_version {min_version} has reached End-of-Life.") + sys.exit(1) + + # Check max_version EoL and release status + max_info = eol_versions.get(max_version) + if max_info: + max_eol_date = max_info["eol"] + max_release_date = max_info["releaseDate"] + + # Check if max_version has a release date in the future + if max_release_date and datetime.strptime(max_release_date, "%Y-%m-%d").date() > current_date: + print(f"Error: max_version {max_version} has not been officially released yet.") + sys.exit(1) + + # Check if max_version has reached EoL + if max_eol_date and datetime.strptime(max_eol_date, "%Y-%m-%d").date() <= current_date: + print(f"Error: max_version {max_version} has reached End-of-Life.") + sys.exit(1) + + except requests.RequestException: + print("Error: Failed to fetch Python version support data.") + sys.exit(1) + + print(f"Version check passed: min_version [{min_version}] is supported " + f"and max_version [{max_version}] is released.") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Check Python version support and alignment with pyproject.toml.") + parser.add_argument("min_version", help="The minimum Python version.") + parser.add_argument("max_version", help="The maximum Python version.") + args = parser.parse_args() + + try: + main(args.min_version, args.max_version) + except InvalidVersion: + print("Error: Invalid version format provided.") + sys.exit(1) diff --git a/sdk/basyx/aas/adapter/http.py b/sdk/basyx/aas/adapter/http.py index 80c248ee..a4d7ab28 100644 --- a/sdk/basyx/aas/adapter/http.py +++ b/sdk/basyx/aas/adapter/http.py @@ -80,7 +80,8 @@ def __init__(self, code: str, text: str, message_type: MessageType = MessageType self.code: str = code self.text: str = text self.message_type: MessageType = message_type - self.timestamp: datetime.datetime = timestamp if timestamp is not None else datetime.datetime.now(datetime.UTC) + self.timestamp: datetime.datetime = timestamp if timestamp is not None \ + else datetime.datetime.now(datetime.timezone.utc) class Result: diff --git a/sdk/pyproject.toml b/sdk/pyproject.toml index 80ed2ca3..7722b16e 100644 --- a/sdk/pyproject.toml +++ b/sdk/pyproject.toml @@ -34,7 +34,7 @@ classifiers = [ "Operating System :: OS Independent", "Development Status :: 5 - Production/Stable" ] -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ "jsonschema~=4.7", "lxml>=4.2,<5",