diff --git a/src/datamodel_code_generator/util.py b/src/datamodel_code_generator/util.py index 9117a0ed1..210fe1520 100644 --- a/src/datamodel_code_generator/util.py +++ b/src/datamodel_code_generator/util.py @@ -59,6 +59,9 @@ def is_pydantic_v2() -> bool: _YAML_1_2_BOOL_PATTERN = re.compile(r"^(?:true|false|True|False|TRUE|FALSE)$") _YAML_DEPRECATED_BOOL_VALUES = {"True", "False", "TRUE", "FALSE"} +# Pattern for scientific notation without decimal point (e.g., 1e-5, 1E+10) +# Standard YAML only matches floats with decimal points, missing patterns like "1e-5" +_YAML_SCIENTIFIC_NOTATION_PATTERN = re.compile(r"^[-+]?[0-9][0-9_]*[eE][-+]?[0-9]+$") def _construct_yaml_bool_with_warning(loader: Any, node: Any) -> bool: @@ -104,6 +107,13 @@ class CustomSafeLoader(_SafeLoader): # type: ignore[valid-type,misc] )) CustomSafeLoader.yaml_constructors["tag:yaml.org,2002:bool"] = _construct_yaml_bool_with_warning + # Add scientific notation without decimal point (e.g., 1e-5) as float + for key in ["-", "+", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]: + CustomSafeLoader.yaml_implicit_resolvers.setdefault(key, []).append(( + "tag:yaml.org,2002:float", + _YAML_SCIENTIFIC_NOTATION_PATTERN, + )) + return CustomSafeLoader diff --git a/tests/data/expected/main/yaml/scientific_notation.py b/tests/data/expected/main/yaml/scientific_notation.py new file mode 100644 index 000000000..e222718be --- /dev/null +++ b/tests/data/expected/main/yaml/scientific_notation.py @@ -0,0 +1,14 @@ +# generated by datamodel-codegen: +# filename: scientific_notation.yaml +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from pydantic import BaseModel + + +class Model(BaseModel): + exponential_default: float | None = 1e-05 + positive_exp: float | None = 20000000000.0 + negative_prefix: float | None = -30000.0 + with_decimal: float | None = 1.5e-05 diff --git a/tests/data/yaml/scientific_notation.yaml b/tests/data/yaml/scientific_notation.yaml new file mode 100644 index 000000000..14cb8ecb4 --- /dev/null +++ b/tests/data/yaml/scientific_notation.yaml @@ -0,0 +1,15 @@ +$schema: http://json-schema.org/draft-07/schema# +type: object +properties: + exponential_default: + type: number + default: 1e-5 + positive_exp: + type: number + default: 2E+10 + negative_prefix: + type: number + default: -3e4 + with_decimal: + type: number + default: 1.5e-5 diff --git a/tests/main/test_main_yaml.py b/tests/main/test_main_yaml.py index eed2ab915..631a2d0f3 100644 --- a/tests/main/test_main_yaml.py +++ b/tests/main/test_main_yaml.py @@ -73,3 +73,19 @@ def test_main_yaml_deprecated_bool(output_file: Path) -> None: output_path=output_file, input_file_type="openapi", ) + + +def test_main_yaml_scientific_notation(output_file: Path) -> None: + """Test YAML file with scientific notation default values (issue #1955). + + Scientific notation without decimal point (e.g., 1e-5) should be parsed as float, + not as string. + """ + run_main_and_assert( + input_path=YAML_DATA_PATH / "scientific_notation.yaml", + output_path=output_file, + input_file_type="jsonschema", + extra_args=["--output-model-type", "pydantic_v2.BaseModel"], + assert_func=assert_file_content, + expected_file="yaml/scientific_notation.py", + )