feat: replace python-dateutil with stdlib datetime.fromisoformat#1429
Open
splch wants to merge 4 commits into
Open
feat: replace python-dateutil with stdlib datetime.fromisoformat#1429splch wants to merge 4 commits into
splch wants to merge 4 commits into
Conversation
Drop the python-dateutil dependency from both the generator and all
generated client code. Date/datetime parsing now uses the stdlib:
- datetime fields: datetime.datetime.fromisoformat(v.replace("Z", "+00:00"))
- date fields: datetime.date.fromisoformat(v)
The .replace("Z", "+00:00") call is needed because Python 3.10's
fromisoformat() does not accept the Z timezone suffix (added in 3.11).
It is a no-op on strings that do not contain Z.
Default values in OpenAPI specs are normalized at generation time
(Z replaced with +00:00), so the generated default expressions are
clean datetime.datetime.fromisoformat("...") calls without the
replace.
This removes one runtime dependency from every generated client
package, reducing install size and eliminating a dependency that is
in maintenance-only mode upstream.
dbanty
reviewed
May 25, 2026
| if value is None or isinstance(value, Value): | ||
| return value | ||
| if isinstance(value, str): | ||
| normalized = value.replace("Z", "+00:00") |
Collaborator
There was a problem hiding this comment.
We should either put this behind a version check or leave a comment to remove it when we drop 3.10 support (so when I search for 3.10-specific things in a few months I'll find it)
Author
There was a problem hiding this comment.
sounds good :) i added a comment with a TODO to make it easy to find
| @@ -1,5 +1,5 @@ | |||
| {% macro construct_function(property, source) %} | |||
| isoparse({{ source }}) | |||
| datetime.datetime.fromisoformat({{ source }}.replace("Z", "+00:00")) | |||
Collaborator
There was a problem hiding this comment.
Leave a comment on this one too.
| [[package]] | ||
| name = "coverage" | ||
| version = "7.11.0" | ||
| version = "7.13.5" |
Collaborator
There was a problem hiding this comment.
Looks like this file was not updated with -S direct_minimal_versions. The goal of it is to make sure everything works with lower bounds.
Same with other minimal lock file
Make the .replace("Z", "+00:00") workaround discoverable by both
`rg TODO` and `rg "3.10|py3.10"` so it can be cleanly removed
when the project drops Python 3.10 support.
Brings in 8 commits since the PR opened, notably:
- typer constraint bump to <0.27
- uv_build 0.11 template update
- mypy v2 cast cleanup in generated output
Conflict resolution:
- pdm.lock, integration-tests/pdm.lock: regenerated via 'pdm lock'
against the merged pyproject.toml.
- Golden records: regenerated via 'pdm run regen' to absorb the
mypy-cast cleanup; the dateutil -> fromisoformat change from this
branch is preserved.
Verified locally with ruff check, ruff format --check, mypy, and the
unit-test suite (283 passed, 4 skipped).
The minimal lockfiles were previously regenerated without
'-S direct_minimal_versions', so direct dependencies were locked
to highest-compatible versions instead of lowest. That defeats the
point of the test_min_deps CI job, which exists to verify the
declared lower bounds in pyproject.toml still work.
Re-locked both pdm.minimal.lock files using:
pdm lock -S direct_minimal_versions -L pdm.minimal.lock
Strategy marker confirmed as
["direct_minimal_versions", "inherit_metadata"] in the metadata
of both files. python-dateutil is absent (this branch's main change).
This was referenced May 27, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Drop the
python-dateutildependency from both the generator and all generated client packages. Date/datetime parsing now uses Python's built-indatetime.fromisoformat()instead ofdateutil.parser.isoparse().This removes one runtime dependency from every generated client, reducing install size and eliminating a dependency that is unmaintained upstream and being deprecated by Fedora 45 (no releases since March 2024).
Changes
Generator source (
openapi_python_client/parser/properties/):datetime.py: Validate defaults withdatetime.datetime.fromisoformat()instead ofisoparse(). NormalizeZto+00:00at generation time so the emitted Python code is clean.date.py: Validate defaults withdatetime.date.fromisoformat()instead ofisoparse().date()."from dateutil.parser import isoparse"from the import set returned byget_imports().Jinja templates (
openapi_python_client/templates/property_templates/):datetime_property.py.jinja:isoparse(x)->datetime.datetime.fromisoformat(x.replace("Z", "+00:00"))date_property.py.jinja:isoparse(x).date()->datetime.date.fromisoformat(x)Dependency removal (all 4 metadata formats):
pyproject_uv.toml.jinja,pyproject_poetry.toml.jinja,pyproject_pdm.toml.jinja,setup.py.jinja: Removepython-dateutilfrom generated dependencies.pyproject.toml(generator): Removepython-dateutilfrom runtime deps andtypes-python-dateutilfrom dev deps.integration-tests/pyproject.toml: Remove both as well.Tests & golden records: All regenerated. Unit tests pass (283 passed, 4 skipped).
Python 3.10 compatibility
datetime.fromisoformat()gained full ISO 8601 support (including theZsuffix) in Python 3.11. On Python 3.10, theZsuffix raises aValueError:The generated datetime parsing code uses
.replace("Z", "+00:00")to normalizeZto an explicit UTC offset before callingfromisoformat(), which works on both 3.10 and 3.11+. This is a no-op on strings withoutZ. Date parsing does not need this since date strings have no timezone component.Default values in OpenAPI specs are normalized at generation time (Z -> +00:00), so the emitted default expressions are clean
datetime.datetime.fromisoformat("...")calls.