Skip to content

Commit e7d1dea

Browse files
authored
Fix byte to binary type mapping (#3114)
* Fix byte to binary type mapping * Fix type mapping guard
1 parent 318aa46 commit e7d1dea

6 files changed

Lines changed: 54 additions & 1 deletion

File tree

src/datamodel_code_generator/model/pydantic_v2/imports.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
IMPORT_FUTURE_DATETIME = Import.from_full_path("pydantic.FutureDatetime")
1717
IMPORT_PAST_DATE = Import.from_full_path("pydantic.PastDate")
1818
IMPORT_FUTURE_DATE = Import.from_full_path("pydantic.FutureDate")
19+
IMPORT_BASE64BYTES = Import.from_full_path("pydantic.Base64Bytes")
1920
IMPORT_BASE64STR = Import.from_full_path("pydantic.Base64Str")
2021
IMPORT_SERIALIZE_AS_ANY = Import.from_full_path("pydantic.SerializeAsAny")
2122
IMPORT_PYDANTIC_DATACLASS = Import.from_full_path("pydantic.dataclasses.dataclass")

src/datamodel_code_generator/model/pydantic_v2/types.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from datamodel_code_generator.model.pydantic_v2.imports import (
2828
IMPORT_ANYURL,
2929
IMPORT_AWARE_DATETIME,
30+
IMPORT_BASE64BYTES,
3031
IMPORT_BASE64STR,
3132
IMPORT_CONBYTES,
3233
IMPORT_CONDECIMAL,
@@ -365,8 +366,11 @@ def get_data_str_type(self, types: Types, **kwargs: Any) -> DataType:
365366

366367
def get_data_bytes_type(self, types: Types, **kwargs: Any) -> DataType:
367368
"""Get bytes data type with constraints (conbytes)."""
369+
base64_encoded = kwargs.pop("base64_encoded", False)
368370
data_type_kwargs: dict[str, Any] = self.transform_kwargs(kwargs, bytes_kwargs)
369371
strict = StrictTypes.bytes in self.strict_types
372+
if base64_encoded and not data_type_kwargs and not strict:
373+
return self.data_type.from_import(IMPORT_BASE64BYTES)
370374
if data_type_kwargs and not strict:
371375
return self.data_type.from_import(IMPORT_CONBYTES, kwargs=data_type_kwargs)
372376
# conbytes doesn't accept strict argument

src/datamodel_code_generator/parser/jsonschema.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,11 @@ def _get_type_with_mappings(self, type_: str, format_: str | None = None) -> Typ
746746

747747
return _get_type(type_, format_, data_formats)
748748

749+
def _is_base64_encoded_binary_mapping(self, type_: str, format_: str) -> bool:
750+
if type_ != "string" or format_ != "byte" or not self.type_mappings:
751+
return False
752+
return self.type_mappings.get((type_, format_)) == "binary"
753+
749754
@cached_property
750755
def schema_paths(self) -> list[tuple[str, list[str]]]:
751756
"""Get schema paths for definitions and defs.
@@ -1292,8 +1297,12 @@ def _get_data_type(type_: str, format__: str) -> DataType:
12921297
else:
12931298
kwargs_to_pass = obj.model_dump()
12941299

1300+
types = self._get_type_with_mappings(type_, format__)
1301+
if types == Types.binary and self._is_base64_encoded_binary_mapping(type_, format__):
1302+
kwargs_to_pass["base64_encoded"] = True
1303+
12951304
return self.data_type_manager.get_data_type(
1296-
self._get_type_with_mappings(type_, format__),
1305+
types,
12971306
field_constraints=self.field_constraints,
12981307
**kwargs_to_pass,
12991308
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# generated by datamodel-codegen:
2+
# filename: type_mappings_byte_to_binary.yaml
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from pydantic import Base64Bytes, BaseModel
8+
9+
10+
class Foo(BaseModel):
11+
bar: Base64Bytes
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
openapi: 3.0.3
2+
components:
3+
schemas:
4+
Foo:
5+
type: object
6+
properties:
7+
bar:
8+
type: string
9+
format: byte
10+
required:
11+
- bar

tests/main/openapi/test_main_openapi.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3855,6 +3855,23 @@ def test_main_openapi_byte_format(output_file: Path) -> None:
38553855
)
38563856

38573857

3858+
def test_main_openapi_type_mappings_byte_to_binary(output_file: Path) -> None:
3859+
"""Test mapping OpenAPI byte format to binary preserves base64 encoding."""
3860+
run_main_and_assert(
3861+
input_path=OPEN_API_DATA_PATH / "type_mappings_byte_to_binary.yaml",
3862+
output_path=output_file,
3863+
input_file_type="openapi",
3864+
assert_func=assert_file_content,
3865+
expected_file="type_mappings_byte_to_binary.py",
3866+
extra_args=[
3867+
"--output-model-type",
3868+
"pydantic_v2.BaseModel",
3869+
"--type-mappings",
3870+
"string+byte=binary",
3871+
],
3872+
)
3873+
3874+
38583875
def test_main_openapi_unquoted_null(output_file: Path) -> None:
38593876
"""Test OpenAPI generation with unquoted null values."""
38603877
run_main_and_assert(

0 commit comments

Comments
 (0)