diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml index eb3d623..ec55d1f 100644 --- a/.github/workflows/unit-tests.yaml +++ b/.github/workflows/unit-tests.yaml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14.0-beta.2"] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} diff --git a/concoursetools/version.py b/concoursetools/version.py index d79ab34..36b5607 100644 --- a/concoursetools/version.py +++ b/concoursetools/version.py @@ -13,6 +13,7 @@ from dataclasses import dataclass from datetime import datetime from enum import Enum +import inspect from typing import Any, ClassVar, TypeVar, cast, get_type_hints from concoursetools.typing import TypedVersionT, VersionConfig, VersionT @@ -262,9 +263,13 @@ class TypedVersion(Version): def __init_subclass__(cls) -> None: try: - annotations = vars(cls)["__annotations__"] # avoid MRO lookup - except KeyError: - annotations = {} + annotations = inspect.get_annotations(cls) + except AttributeError: + # Function isn't available in Python 3.9, so use the old code until EOL + try: + annotations = vars(cls)["__annotations__"] # avoid MRO lookup + except KeyError: + annotations = {} if len(annotations) == 0: raise TypeError("Can't instantiate dataclass TypedVersion without any fields") diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 90129b2..b315c0d 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -4,4 +4,5 @@ What's New .. toctree:: :glob: + changelog/development changelog/* diff --git a/docs/source/changelog/development.rst b/docs/source/changelog/development.rst new file mode 100644 index 0000000..55b23a0 --- /dev/null +++ b/docs/source/changelog/development.rst @@ -0,0 +1,5 @@ +Development +=========== + + +* Added Python 3.14 support. diff --git a/pyproject.toml b/pyproject.toml index f1f5d2c..d36397f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Software Development", "Typing :: Typed", ] diff --git a/sonar-project.properties b/sonar-project.properties index a9a93d2..df018d6 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -3,7 +3,7 @@ sonar.projectDescription="Static analysis for the Concourse Tools Python library sonar.sources=concoursetools sonar.tests=tests -sonar.python.version=3.9,3.10,3.11,3.12,3.13 +sonar.python.version=3.9,3.10,3.11,3.12,3.13,3.14 sonar.coverage.exclusions=docs/**/*,tests/**/*,coverage.xml,concoursetools/colour.py,concoursetools/typing.py,**/__init__.py,**/__main__.py sonar.python.coverage.reportPaths=coverage.xml diff --git a/tests/test_resource.py b/tests/test_resource.py index e0e04ec..1d8b2fc 100644 --- a/tests/test_resource.py +++ b/tests/test_resource.py @@ -3,6 +3,7 @@ import random import shutil import string +import sys from tempfile import TemporaryDirectory from unittest import SkipTest, TestCase @@ -728,7 +729,12 @@ def _build_test_resource_docker_image() -> str: except FileNotFoundError: pass - cli_commands.dockerfile(str(temp_dir), resource_file="concourse.py", class_name=TestResource.__name__, dev=True) + # This pins the tag to the exact version of Python we're using, including alpha/beta + # '3.14.0b1 (main, May 9 2025, 23:49:24) [GCC 12.2.0]' + # ^^^^^^^^ + tag, *_ = sys.version.split() + cli_commands.dockerfile(str(temp_dir), resource_file="concourse.py", class_name=TestResource.__name__, + tag=tag, dev=True) stdout, _ = run_command("docker", ["build", ".", "-q"], cwd=temp_dir) sha1_hash = stdout.strip()