From 5362a35cf859ee5e0b9798b603543a9e032c91ff Mon Sep 17 00:00:00 2001 From: Robert Abram Date: Wed, 24 Dec 2025 13:41:52 -0600 Subject: [PATCH 1/4] Update supported Python versions --- .github/workflows/coverage.yaml | 4 ++-- .github/workflows/tests.yaml | 2 +- pyproject.toml | 2 +- setup.py | 7 ++++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index f95b58b..7cf1e87 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -8,10 +8,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up Python 3.12 + - name: Set up Python 3.13 uses: actions/setup-python@v4 with: - python-version: 3.12 + python-version: 3.13 - name: Install Dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 61305f8..2fa549a 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v3 diff --git a/pyproject.toml b/pyproject.toml index 0a7cefd..90f7b74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,2 @@ line-length = 120 -target-version = ['py37', 'py38', 'py39', 'py310'] \ No newline at end of file +target-version = ['py37', 'py38', 'py39', 'py310', 'py311', 'py312', 'py313', 'py314'] \ No newline at end of file diff --git a/setup.py b/setup.py index b701756..18c1f0d 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ from setuptools import setup, find_packages -__VERSION__ = "1.2.0" +__VERSION__ = "1.2.2" base_dir = os.path.abspath(os.path.dirname(__file__)) @@ -49,19 +49,20 @@ "models", "data" ], - python_requires=">=3.6", + python_requires=">=3.7", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ], test_suite="tests", project_urls={ From 3bae9ba4498a510e23d0e0f03a5532148c568cde Mon Sep 17 00:00:00 2001 From: Robert Abram Date: Wed, 24 Dec 2025 13:49:13 -0600 Subject: [PATCH 2/4] Be specific about Github Actions python versions --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 2fa549a..8e331e2 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.7.17", "3.8.18", "3.9.25", "3.10.18", "3.11.14", "3.12.12", "3.13.11", "3.14.2"] steps: - uses: actions/checkout@v3 From 1df9bacb2be69084bfaa82b557b877d4ce605fec Mon Sep 17 00:00:00 2001 From: Robert Abram Date: Wed, 24 Dec 2025 13:50:34 -0600 Subject: [PATCH 3/4] Remove v3.7 from Github Actions test --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 8e331e2..1b34506 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.7.17", "3.8.18", "3.9.25", "3.10.18", "3.11.14", "3.12.12", "3.13.11", "3.14.2"] + python-version: ["3.8.18", "3.9.25", "3.10.18", "3.11.14", "3.12.12", "3.13.11", "3.14.2"] steps: - uses: actions/checkout@v3 From 71baaaed4158841aad3194882deaae316801dfe4 Mon Sep 17 00:00:00 2001 From: Robert Abram Date: Wed, 24 Dec 2025 15:29:07 -0600 Subject: [PATCH 4/4] Support 3.14 annotation changes, fix utcnow() deprecation --- src/python_easy_json/json_object.py | 18 ++++++++++++------ tests/test_json_with_enum.py | 6 +++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/python_easy_json/json_object.py b/src/python_easy_json/json_object.py index f520ff4..63e83a1 100644 --- a/src/python_easy_json/json_object.py +++ b/src/python_easy_json/json_object.py @@ -13,6 +13,10 @@ from dateutil import parser as dt_parser from json import JSONDecodeError +# 3.14 introduced lazy annotation loading, we must use the 'annotationlib' to inspect annotations. +if not (sys.version_info.major == 3 and sys.version_info.minor < 14): + from annotationlib import get_annotations, Format as annot_format + _enum_t = type(enum.Enum) # Support OrderedDict for Python versions 3.6 or below. @@ -45,8 +49,8 @@ def _get_annot_cls(annots: dict, key: str, ignore_builtins = False) -> typing.Li cls_ = cls_.__args__[0] # Check if typing annotation class is a Union type. # Try to find the right object class in the Union types list, ignore 'builtin' types. - if '__args__' in cls_.__dict__ and isinstance(cls_.__dict__['__args__'], (list, tuple)): - for cls_item in cls_.__dict__['__args__']: + if hasattr(cls_, '__args__') and isinstance(cls_.__args__, (list, tuple)): + for cls_item in cls_.__args__: # Try to find the right object class in the Union types list, ignore 'builtin' types. if issubclass(type(cls_item), object) and not isinstance(cls_item, typing.TypeVar): if ignore_builtins and cls_item.__module__ == 'builtins': @@ -75,10 +79,12 @@ def _collect_annotations(self, cls_: object): continue result = self._collect_annotations(base) annots.update(result) - - if hasattr(cls_, '__annotations__'): - annots.update(cls_.__annotations__) - + # 3.14 introduced breaking changes to annotation inspection due to lazy annotation loading. + if sys.version_info.major == 3 and sys.version_info.minor < 14: + if hasattr(cls_, '__annotations__'): + annots.update(cls_.__annotations__) + else: + annots.update(get_annotations(cls_, format=annot_format.VALUE)) return annots @staticmethod diff --git a/tests/test_json_with_enum.py b/tests/test_json_with_enum.py index 4b30943..22f2fb4 100644 --- a/tests/test_json_with_enum.py +++ b/tests/test_json_with_enum.py @@ -2,7 +2,7 @@ # This file is subject to the terms and conditions defined in the # file 'LICENSE', which is part of this source code package. # -from datetime import datetime +from datetime import datetime, timezone from enum import Enum, IntEnum from tests.base_test import BaseTestCase @@ -45,7 +45,7 @@ class TestJSONWithEnum(BaseTestCase): def test_object_with_enum_values(self): """ Test JSONObject class with IntEnum property. """ - ts = datetime.utcnow() + ts = datetime.now(timezone.utc) data = { 'timestamp': ts.isoformat(), @@ -69,7 +69,7 @@ def test_object_with_enum_values(self): def test_str_enum(self): """ Test an enum with string values """ - ts = datetime.utcnow() + ts = datetime.now(timezone.utc) data = { 'timestamp': ts.isoformat(),