Skip to content

Commit 3b4f7de

Browse files
authored
Merge pull request #414 from jacebrowning/python-3.14
Add Python 3.14 to the test matrix
2 parents 2ca7945 + b284b90 commit 3b4f7de

9 files changed

Lines changed: 209 additions & 50 deletions

File tree

.github/workflows/main.yml

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,46 @@ on: [push, pull_request]
44

55
jobs:
66
build:
7-
87
runs-on: ubuntu-latest
98
strategy:
9+
fail-fast: false
1010
matrix:
11-
python-version: ['3.10', '3.11', '3.12', '3.13']
11+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
1212

1313
steps:
14-
- uses: actions/checkout@v2
14+
- uses: actions/checkout@v2
1515

16-
- name: Set up Python ${{ matrix.python-version }}
17-
uses: actions/setup-python@v2
18-
with:
19-
python-version: ${{ matrix.python-version }}
16+
- name: Set up Python ${{ matrix.python-version }}
17+
uses: actions/setup-python@v2
18+
with:
19+
python-version: ${{ matrix.python-version }}
2020

21-
- name: Install Poetry
22-
run: curl -sSL https://install.python-poetry.org | python -
21+
- name: Install Poetry
22+
run: curl -sSL https://install.python-poetry.org | python -
2323

24-
- name: Check dependencies
25-
run: make doctor
24+
- name: Check dependencies
25+
run: make doctor
2626

27-
- uses: actions/cache@v4
28-
with:
29-
path: .venv
30-
key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }}
27+
- uses: actions/cache@v4
28+
with:
29+
path: .venv
30+
key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }}
3131

32-
- name: Install dependencies
33-
run: make install
32+
- name: Install dependencies
33+
run: make install
3434

35-
- name: Test code
36-
run: make test-repeat
35+
- name: Test code
36+
run: make test-repeat
3737

38-
- name: Upload coverage
39-
uses: codecov/codecov-action@v4
40-
if: steps.fork-check.outputs.is-fork == 'false'
41-
with:
42-
fail_ci_if_error: true
43-
token: ${{ secrets.CODECOV_TOKEN }}
38+
- name: Upload coverage
39+
uses: codecov/codecov-action@v4
40+
if: steps.fork-check.outputs.is-fork == 'false'
41+
with:
42+
fail_ci_if_error: true
43+
token: ${{ secrets.CODECOV_TOKEN }}
4444

45-
- name: Check code
46-
run: make check
45+
- name: Check code
46+
run: make check
4747

48-
- name: Check documentation
49-
run: make mkdocs notebooks
48+
- name: Check documentation
49+
run: make mkdocs notebooks

.tool-versions

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
python 3.13.11
1+
python 3.14.2
22
poetry 2.3.0

.vscode/settings.json

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
{
2+
"files.exclude": {
3+
".cache/": true,
4+
".venv/": true,
5+
"*.egg-info": true,
6+
"pip-wheel-metadata/": true,
7+
"**/__pycache__": true,
8+
"**/*.pyc": true,
9+
"**/.ipynb_checkpoints": true,
10+
"**/tmp/": true,
11+
"dist/": true,
12+
"htmlcov/": true,
13+
"prof/": true,
14+
"site/": true,
15+
"geckodriver.log": true
16+
},
17+
"python.defaultInterpreterPath": ".venv/bin/python",
18+
"editor.formatOnSave": true,
19+
"pylint.path": [
20+
".venv/bin/pylint"
21+
],
22+
"pylint.args": [
23+
"--rcfile=.pylint.ini"
24+
],
25+
"cSpell.words": [
26+
"abatilo",
27+
"addopts",
28+
"appex",
29+
"autorefs",
30+
"brandonaut",
31+
"brosche",
32+
"builtins",
33+
"choco",
34+
"classproperties",
35+
"codehilite",
36+
"completly",
37+
"cookiecutter",
38+
"coveragerc",
39+
"coveragespace",
40+
"currentdir",
41+
"cygstart",
42+
"cygwin",
43+
"dataclass",
44+
"dataclasses",
45+
"datafile",
46+
"ensurepip",
47+
"Etrange",
48+
"exitfirst",
49+
"findstr",
50+
"fontawesome",
51+
"freezegun",
52+
"gethostname",
53+
"getpid",
54+
"getplugin",
55+
"gitman",
56+
"gitsvn",
57+
"gittoplevel",
58+
"Graphviz",
59+
"gunechristensen",
60+
"hdnivara",
61+
"htmlcov",
62+
"iglob",
63+
"imac",
64+
"importlib",
65+
"ioreg",
66+
"iphoto",
67+
"ipynb",
68+
"ipython",
69+
"isort",
70+
"Jace",
71+
"lastfailed",
72+
"levelname",
73+
"logbreak",
74+
"macbook",
75+
"macfsevents",
76+
"mastupristi",
77+
"maxfail",
78+
"maxsplit",
79+
"MDEF",
80+
"minilog",
81+
"mkdocs",
82+
"mkdocstrings",
83+
"mrpossoms",
84+
"mttjohnson",
85+
"mylink",
86+
"mypy",
87+
"nbstripout",
88+
"netrc",
89+
"nobeep",
90+
"noclasses",
91+
"nohup",
92+
"onpass",
93+
"papermill",
94+
"pdbcls",
95+
"pipx",
96+
"pluginmanager",
97+
"preserialization",
98+
"Preserialized",
99+
"Preserializing",
100+
"psutil",
101+
"pydocstyle",
102+
"pygments",
103+
"pyinstaller",
104+
"pylint",
105+
"pync",
106+
"pyproject",
107+
"pyreverse",
108+
"pytest",
109+
"rcfile",
110+
"relpath",
111+
"repr",
112+
"ruamel",
113+
"rustup",
114+
"scalarstring",
115+
"sergey",
116+
"setuptools",
117+
"showfspath",
118+
"shuyskiy",
119+
"spurnvoj",
120+
"startfile",
121+
"tomlkit",
122+
"tput",
123+
"tracebackhide",
124+
"Trilean",
125+
"trufflehog",
126+
"udevadm",
127+
"unparseable",
128+
"unstaged",
129+
"USERPROFILE",
130+
"venv",
131+
"verchew",
132+
"verchewrc",
133+
"virtualenvs",
134+
"webfonts",
135+
"xenji",
136+
"xfail",
137+
"YORM"
138+
],
139+
"makefile.configureOnOpen": false
140+
}
141+

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Release Notes
22

3+
## 2.5 (2026-01-29)
4+
5+
- Added support for Python 3.14.
6+
37
## 2.4 (2026-01-19)
48

59
- Dropped support for Python 3.9.

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ Datafiles is a bidirectional serialization library for Python [dataclasses](http
99
[![PyPI License](https://img.shields.io/pypi/l/datafiles.svg)](https://pypi.org/project/datafiles)
1010
[![PyPI Version](https://img.shields.io/pypi/v/datafiles.svg?label=version)](https://pypi.org/project/datafiles)
1111
[![PyPI Downloads](https://img.shields.io/pypi/dm/datafiles.svg?color=orange)](https://pypistats.org/packages/datafiles)
12-
[![Gitter](https://img.shields.io/gitter/room/jacebrowning/datafiles?color=D0164E)](https://gitter.im/jacebrowning/datafiles)
1312

1413
Some common use cases include:
1514

datafiles/converters/__init__.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,21 @@ def register(cls: Union[type, str], converter: type):
3737
register(dict, Dictionary)
3838

3939

40+
def _convert_union(args: tuple[type, ...]) -> type | None:
41+
"""Return converter for currently supported unions."""
42+
if len(args) != 2:
43+
return None
44+
none_type = type(None)
45+
if args[0] is none_type or args[1] is none_type:
46+
value_type = args[1] if args[0] is none_type else args[0]
47+
return map_type(value_type).as_optional()
48+
if str in args:
49+
return map_type(str)
50+
if args in {(int, float), (float, int)}:
51+
return Number
52+
return None
53+
54+
4055
@cached
4156
def map_type(cls, *, name: str = "", item_cls: Optional[type] = None):
4257
"""Infer the converter type from a dataclass, type, or annotation."""
@@ -59,11 +74,10 @@ def map_type(cls, *, name: str = "", item_cls: Optional[type] = None):
5974
return converter
6075

6176
if hasattr(types, "UnionType") and isinstance(cls, types.UnionType): # type: ignore
62-
# Python 3.10 behavior
63-
converter = map_type(cls.__args__[0])
64-
assert len(cls.__args__) == 2
65-
assert cls.__args__[1] == type(None)
66-
converter = converter.as_optional()
77+
args = tuple(getattr(cls, "__args__", ()))
78+
converter = _convert_union(args)
79+
if converter is None:
80+
raise TypeError(f"Unsupported union type: {cls}")
6781
return converter
6882

6983
if hasattr(cls, "__origin__"):
@@ -114,16 +128,16 @@ def map_type(cls, *, name: str = "", item_cls: Optional[type] = None):
114128
converter = Dictionary.of_mapping(key, value)
115129

116130
elif cls.__origin__ == Union:
117-
if str in cls.__args__:
118-
converter = map_type(str)
119-
if type(None) in cls.__args__:
120-
converter = converter.as_optional()
121-
elif cls.__args__ in {(int, float), (float, int)}:
122-
converter = Number
131+
args = tuple(cls.__args__)
132+
if len(args) == 2:
133+
converter = _convert_union(args)
123134
else:
124-
assert len(cls.__args__) == 2
125-
assert cls.__args__[1] == type(None)
126-
converter = map_type(cls.__args__[0]).as_optional()
135+
if str in cls.__args__:
136+
converter = map_type(str)
137+
if type(None) in cls.__args__:
138+
converter = converter.as_optional()
139+
if converter is None:
140+
raise TypeError(f"Unsupported union type: {cls}")
127141

128142
elif issubclass(cls.__origin__, Converter):
129143
subtypes = [map_type(t) for t in cls.__args__]

docs/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
jinja2==3.1.4 ; python_version >= "3.10" and python_version < "4.0"
1+
jinja2==3.1.6 ; python_version >= "3.10" and python_version < "4.0"
22
markdown==3.9 ; python_version >= "3.10" and python_version < "4.0"
33
mkdocs-get-deps==0.2.0 ; python_version >= "3.10" and python_version < "4.0"
44
mkdocs==1.6.1 ; python_version >= "3.10" and python_version < "4.0"

poetry.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[tool.poetry]
22

33
name = "datafiles"
4-
version = "2.4"
4+
version = "2.5"
55
description = "File-based ORM for dataclasses."
66

77
license = "MIT"
@@ -36,6 +36,7 @@ classifiers = [
3636
"Programming Language :: Python :: 3.11",
3737
"Programming Language :: Python :: 3.12",
3838
"Programming Language :: Python :: 3.13",
39+
"Programming Language :: Python :: 3.14",
3940
"Programming Language :: Python",
4041
"Topic :: Software Development",
4142
"Topic :: Utilities",

0 commit comments

Comments
 (0)