Skip to content

Commit 48339e3

Browse files
author
Doug Borg
committed
feat: add support for date-only format (datetime.date type)
Addresses Issue #89 When an OpenAPI schema defines a string field with format='date', the generator now produces a datetime.date type instead of falling back to str. Changes: - Extended _handle_format_conversions() to handle 'date' format - Added date type conversion with proper imports (from datetime import date) - Follows same pattern as existing date-time format handling - Requires orjson=True (like other format conversions) - Falls back to str when orjson is disabled Benefits: - Type-safe handling of date-only fields (birth_date, etc.) - Proper Pydantic validation for date fields - Consistent with OpenAPI 3.x date format specification - Clean implementation in single consolidated function (PR #122 refactoring) Tests: - test_date_format_generates_date_type: Verifies date type generation - test_date_format_without_orjson: Verifies str fallback behavior - All 252 existing tests pass (including 11 xfailed) - Coverage maintained at 95%+ Closes #89
1 parent 72f56a8 commit 48339e3

File tree

3 files changed

+127
-1
lines changed

3 files changed

+127
-1
lines changed

src/openapi_python_generator/language_converters/python/model_generator.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def _handle_format_conversions(
8787
schema: Schema, base_type: str, required: bool
8888
) -> Optional[TypeConversion]:
8989
"""
90-
Handle UUID and datetime format conversions based on orjson usage.
90+
Handle UUID, datetime, and date format conversions based on orjson usage.
9191
9292
Returns TypeConversion if special format handling is needed, None otherwise.
9393
@@ -126,6 +126,15 @@ def _handle_format_conversions(
126126
import_types=["from datetime import datetime"],
127127
)
128128

129+
# Handle date format
130+
if schema.schema_format == "date" and common.get_use_orjson():
131+
converted_type = "date" if required else "Optional[date]"
132+
return TypeConversion(
133+
original_type=base_type,
134+
converted_type=converted_type,
135+
import_types=["from datetime import date"],
136+
)
137+
129138
return None
130139

131140

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"openapi": "3.0.0",
3+
"info": {
4+
"title": "Date Format Test API",
5+
"version": "1.0.0"
6+
},
7+
"paths": {
8+
"/person": {
9+
"get": {
10+
"operationId": "get_person",
11+
"responses": {
12+
"200": {
13+
"description": "Person details",
14+
"content": {
15+
"application/json": {
16+
"schema": {
17+
"$ref": "#/components/schemas/Person"
18+
}
19+
}
20+
}
21+
}
22+
}
23+
}
24+
}
25+
},
26+
"components": {
27+
"schemas": {
28+
"Person": {
29+
"type": "object",
30+
"required": ["name", "birth_date", "created_at"],
31+
"properties": {
32+
"name": {
33+
"type": "string"
34+
},
35+
"birth_date": {
36+
"type": "string",
37+
"format": "date"
38+
},
39+
"created_at": {
40+
"type": "string",
41+
"format": "date-time"
42+
}
43+
}
44+
}
45+
}
46+
}
47+
}

tests/test_date_format.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""Test support for date-only format (datetime.date type)."""
2+
import tempfile
3+
from pathlib import Path
4+
5+
from openapi_python_generator.common import Formatter, HTTPLibrary
6+
from openapi_python_generator.generate_data import generate_data
7+
8+
9+
def test_date_format_generates_date_type():
10+
"""Test that string fields with format='date' generate datetime.date type."""
11+
# OpenAPI spec with date format
12+
spec_path = Path(__file__).parent / "test_data" / "test_date_format.json"
13+
14+
with tempfile.TemporaryDirectory() as tmpdir:
15+
output_path = Path(tmpdir) / "generated"
16+
17+
# Generate code
18+
generate_data(
19+
spec_path,
20+
output_path,
21+
HTTPLibrary.httpx,
22+
use_orjson=True, # date format handling requires orjson
23+
)
24+
25+
# Read generated Person model
26+
person_model_path = output_path / "models" / "person.py"
27+
assert person_model_path.exists(), "Person model should be generated"
28+
29+
person_model_content = person_model_path.read_text()
30+
31+
# Verify import from datetime
32+
assert "from datetime import date, datetime" in person_model_content or (
33+
"from datetime import date" in person_model_content
34+
and "from datetime import datetime" in person_model_content
35+
), "Should import date from datetime"
36+
37+
# Verify birth_date field uses date type
38+
assert "birth_date: date" in person_model_content, (
39+
"birth_date with format='date' should use date type"
40+
)
41+
42+
# Verify created_at field uses datetime type (to confirm date-time still works)
43+
assert "created_at: datetime" in person_model_content, (
44+
"created_at with format='date-time' should use datetime type"
45+
)
46+
47+
48+
def test_date_format_without_orjson():
49+
"""Test that date format falls back to str when orjson is not enabled."""
50+
spec_path = Path(__file__).parent / "test_data" / "test_date_format.json"
51+
52+
with tempfile.TemporaryDirectory() as tmpdir:
53+
output_path = Path(tmpdir) / "generated"
54+
55+
# Generate code WITHOUT orjson
56+
generate_data(
57+
spec_path,
58+
output_path,
59+
HTTPLibrary.httpx,
60+
use_orjson=False, # date format handling requires orjson
61+
)
62+
63+
# Read generated Person model
64+
person_model_path = output_path / "models" / "person.py"
65+
person_model_content = person_model_path.read_text()
66+
67+
# Without orjson, date should fall back to str
68+
assert "birth_date: str" in person_model_content, (
69+
"Without orjson, date format should fall back to str"
70+
)

0 commit comments

Comments
 (0)