Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ jobs:
strategy:
fail-fast: true
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
experimental: [false]
include:
- python-version: "3.14-dev"
- python-version: "3.15-dev"
experimental: true

steps:
Expand Down
9 changes: 9 additions & 0 deletions HISTORY
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
2.11
====

Changes
-------

- Handle Python 3.14+ delayed annotations.


2.11rc3
=======

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Odin provides a declarative framework for defining resources (classes) and their relationships, validation of the fields
that make up the resources and mapping between objects (either a resource, or other python structures).

Odin also comes with built in serialisation tools for importing and exporting data from resources.
Odin also comes with built-in serialisation tools for importing and exporting data from resources.

<table>
<tr>
Expand Down Expand Up @@ -77,7 +77,7 @@ Odin also comes with built in serialisation tools for importing and exporting da
* Fields for building composite resources
* Field and Resource level validation
* Easy extension to support custom fields
* Python 3.8+ and PyPy <sup>1</sup> supported
* Python 3.10+ and PyPy <sup>1</sup> supported
* Support for documenting resources with [Sphinx](http://sphinx-doc.org/)
* Minimal dependencies

Expand Down
1,141 changes: 669 additions & 472 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "odin"
version = "2.11rc3"
version = "2.11"
description = "Data-structure definition/validation/traversal, mapping and serialisation toolkit for Python"
authors = ["Tim Savage <[email protected]>"]
license = "BSD-3-Clause"
Expand Down
5 changes: 5 additions & 0 deletions sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ sonar.python.coverage.reportPaths=reports/coverage-*.xml

# Encoding of the source code. Default is default system encoding
sonar.sourceEncoding=UTF-8
sonar.sources=src
sonar.tests=tests

# Exclude everything EXCEPT the src folder from coverage calculations
sonar.coverage.exclusions=**/*, !src/**/*
19 changes: 18 additions & 1 deletion src/odin/annotated_resource/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

from collections.abc import Iterable

try:
import annotationlib
except ImportError:
annotationlib = None

from odin import registration
from odin.fields import BaseField
from odin.resources import (
Expand All @@ -34,11 +39,23 @@
"AResource",
)

def _resolve_annotations(attrs: dict[str, ...]) -> dict[str, type]:
"""Resolve annotations.

Support delayed annotations in Python 3.14 by using annotationlib
"""
if annotationlib is None:
return attrs.pop("__annotations__", None) or {}

af = annotationlib.get_annotate_from_class_namespace(attrs)
if af is None:
return {}
return annotationlib.call_annotate_function(af, annotationlib.Format.VALUE)

def _iterate_attrs(attrs: dict[str, ...]) -> Iterable[tuple[str, BaseField]]:
"""Iterate through attributes and combine with annotations."""

annotations = attrs.pop("__annotations__", None) or {}
annotations = _resolve_annotations(attrs)

# Yield any annotations processed into field instances
for name, type_ in annotations.items():
Expand Down
Loading