Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5c936d0
Increase coverage threshold from 88% to 100%
koxudaxi Jan 2, 2026
0cb6f24
Move fail_under check to combined coverage environment
koxudaxi Jan 2, 2026
528403a
Merge branch 'main' into feature/increase-coverage-threshold
koxudaxi Jan 2, 2026
b90ac67
Disable covdefaults fail_under for individual tests
koxudaxi Jan 2, 2026
b7f31be
Add --cov-fail-under=0 to individual tests
koxudaxi Jan 2, 2026
e246714
Add pragma: no cover to unreachable code paths
koxudaxi Jan 3, 2026
d1f0535
Remove Python 3.9 dead code from msgspec and update action
koxudaxi Jan 3, 2026
ef628ac
Revert config-types.yaml and action.yml to use tox
koxudaxi Jan 3, 2026
b0ff0ec
Add test for --no-use-union-operator and fix coverage
koxudaxi Jan 3, 2026
af2e700
Add tests for --no-use-union-operator and add pragma: no cover to env…
koxudaxi Jan 3, 2026
ade6d85
docs: update CLI reference documentation and prompt data
github-actions[bot] Jan 3, 2026
e7b58d8
Add test for collision resolution success path
koxudaxi Jan 3, 2026
0447f7c
Add pragma: no cover to test helper code
koxudaxi Jan 3, 2026
244217b
Fix pragma: no cover placement in test helper
koxudaxi Jan 3, 2026
cd0c2d1
Add pragma: no branch to branch partials
koxudaxi Jan 3, 2026
be749b8
Add deprecation warning to has_type_alias property
koxudaxi Jan 3, 2026
8141f1d
Add msgspec e2e tests and optimize pragma comments
koxudaxi Jan 3, 2026
c055f58
Remove unnecessary pragma comments and defensive checks
koxudaxi Jan 3, 2026
897c75b
Remove more unnecessary pragma comments
koxudaxi Jan 3, 2026
1037770
Remove pragma from pydantic/base_model.py and types.py
koxudaxi Jan 3, 2026
ec06425
Optimize graphql.py pragma comments and fix coverage
koxudaxi Jan 3, 2026
1535ce8
Remove all pragma comments from test files
koxudaxi Jan 3, 2026
969bc66
Remove dead code from test files
koxudaxi Jan 3, 2026
c5a5301
Remove skipped tests that are dead code
koxudaxi Jan 3, 2026
ecb34b8
Add pragmas to test failure paths and exclude cli_doc from coverage
koxudaxi Jan 3, 2026
209911a
Merge branch 'main' into feature/increase-coverage-threshold
koxudaxi Jan 3, 2026
976b9d2
docs: update llms.txt files
github-actions[bot] Jan 3, 2026
5262f03
Add pragmas for remaining test file uncovered branches
koxudaxi Jan 3, 2026
9fec965
Remove dead code from test files
koxudaxi Jan 3, 2026
3f2b713
Remove more dead code from test baseline checks
koxudaxi Jan 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/cli-reference/model-customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -4945,6 +4945,10 @@ where optional fields have defaults but cannot accept `None` values.
- type: string
- type: number
nullable: true
simpleUnion:
oneOf:
- type: string
- type: number
required:
- comments
- oneOfComments
Expand Down Expand Up @@ -5034,6 +5038,7 @@ where optional fields have defaults but cannot accept `None` values.
class Options(BaseModel):
comments: list[str | None]
oneOfComments: list[str | float | None]
simpleUnion: str | float | None = None
```

---
Expand Down
5 changes: 5 additions & 0 deletions docs/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5808,6 +5808,10 @@ where optional fields have defaults but cannot accept `None` values.
- type: string
- type: number
nullable: true
simpleUnion:
oneOf:
- type: string
- type: number
required:
- comments
- oneOfComments
Expand Down Expand Up @@ -5897,6 +5901,7 @@ where optional fields have defaults but cannot accept `None` values.
class Options(BaseModel):
comments: list[str | None]
oneOfComments: list[str | float | None]
simpleUnion: str | float | None = None
```

---
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,8 @@ paths.other = [
"*\\datamodel-code-generator",
]
run.dynamic_context = "none"
run.omit = [ "tests/data/*", "tests/main/test_performance.py", "*/_types/*" ]
report.fail_under = 88
run.omit = [ "tests/data/*", "tests/main/test_performance.py", "*/_types/*", "tests/cli_doc/*" ]
report.fail_under = 0
run.parallel = true
run.plugins = [
"covdefaults",
Expand Down
10 changes: 5 additions & 5 deletions src/datamodel_code_generator/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ def validate_keyword_only(self: Self) -> Self: # pyright: ignore[reportRedeclar
and output_model_type == DataModelType.DataclassesDataclass
and not python_target.has_kw_only_dataclass
):
raise Error(self.__validate_keyword_only_err)
raise Error(self.__validate_keyword_only_err) # pragma: no cover
return self

@model_validator() # pyright: ignore[reportArgumentType]
Expand Down Expand Up @@ -449,7 +449,7 @@ def validate_keyword_only(cls, values: dict[str, Any]) -> dict[str, Any]: # noq
and output_model_type == DataModelType.DataclassesDataclass
and not python_target.has_kw_only_dataclass
):
raise Error(cls.__validate_keyword_only_err)
raise Error(cls.__validate_keyword_only_err) # pragma: no cover
return values

@model_validator() # pyright: ignore[reportArgumentType]
Expand Down Expand Up @@ -643,9 +643,9 @@ def _extract_additional_imports(extra_template_data: defaultdict[str, dict[str,
if "additional_imports" in type_data:
imports = type_data.pop("additional_imports")
if isinstance(imports, str):
if imports.strip():
if imports.strip(): # pragma: no branch
additional_imports.append(imports.strip())
elif isinstance(imports, list):
elif isinstance(imports, list): # pragma: no branch
additional_imports.extend(item.strip() for item in imports if isinstance(item, str) and item.strip())
return additional_imports

Expand Down Expand Up @@ -717,7 +717,7 @@ def _get_pyproject_toml_config(source: Path, profile: str | None = None) -> dict
pyproject_config["capitalise_enum_members"] = pyproject_config.pop("capitalize_enum_members")
return pyproject_config

if (current_path / ".git").exists():
if (current_path / ".git").exists(): # pragma: no cover
break

current_path = current_path.parent
Expand Down
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
ExtraTemplateDataType = defaultdict[str, dict[str, Any]]
elif is_pydantic_v2():
ExtraTemplateDataType = defaultdict[str, Annotated[dict[str, Any], Field(default_factory=dict)]]
else:
else: # pragma: no cover
ExtraTemplateDataType = defaultdict[str, dict[str, Any]]


Expand Down
21 changes: 15 additions & 6 deletions src/datamodel_code_generator/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,20 @@ def has_union_operator(self) -> bool: # pragma: no cover
"""Check if Python version supports the union operator (|)."""
return self._is_py_310_or_later

@property
def has_type_alias(self) -> bool: # pragma: no cover
"""Check if Python version supports TypeAlias.

.. deprecated::
This property is unused and will be removed in a future version.
"""
warn(
"has_type_alias is deprecated and will be removed in a future version.",
DeprecationWarning,
stacklevel=2,
)
return self._is_py_310_or_later

@property
def has_typed_dict_non_required(self) -> bool:
"""Check if Python version supports TypedDict NotRequired."""
Expand All @@ -119,11 +133,6 @@ def has_kw_only_dataclass(self) -> bool:
"""Check if Python version supports kw_only in dataclasses."""
return self._is_py_310_or_later

@property
def has_type_alias(self) -> bool:
"""Check if Python version supports TypeAlias."""
return self._is_py_310_or_later

@property
def has_type_statement(self) -> bool:
"""Check if Python version supports type statements."""
Expand Down Expand Up @@ -243,7 +252,7 @@ def __init__( # noqa: PLR0912, PLR0913, PLR0915, PLR0917
black_kwargs: dict[str, Any] = {}
if wrap_string_literal is not None:
experimental_string_processing = wrap_string_literal
elif black.__version__ < "24.1.0":
elif black.__version__ < "24.1.0": # pragma: no cover
experimental_string_processing = config.get("experimental-string-processing")
else:
experimental_string_processing = config.get("preview", False) and ( # pragma: no cover
Expand Down
15 changes: 7 additions & 8 deletions src/datamodel_code_generator/imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,15 @@ def append(self, imports: Import | Iterable[Import] | None) -> None:

def remove(self, imports: Import | Iterable[Import]) -> None: # noqa: PLR0912
"""Remove one or more imports from the collection."""
if isinstance(imports, Import): # pragma: no cover
if isinstance(imports, Import):
imports = [imports]
for import_ in imports:
if "." in import_.import_: # pragma: no cover
key = (None, import_.import_)
if self.counter.get(key, 0) <= 0:
continue
self.counter[key] -= 1
if self.counter[key] == 0: # pragma: no cover
if self.counter[key] == 0:
del self.counter[key]
if None in self and import_.import_ in self[None]:
self[None].remove(import_.import_)
Expand All @@ -108,13 +108,12 @@ def remove(self, imports: Import | Iterable[Import]) -> None: # noqa: PLR0912
key = (import_.from_, import_.import_)
if self.counter.get(key, 0) <= 0:
continue
self.counter[key] -= 1 # pragma: no cover
if self.counter[key] == 0: # pragma: no cover
self.counter[key] -= 1
if self.counter[key] == 0:
del self.counter[key]
if import_.from_ in self and import_.import_ in self[import_.from_]:
self[import_.from_].remove(import_.import_)
if not self[import_.from_]:
del self[import_.from_]
self[import_.from_].remove(import_.import_)
if not self[import_.from_]:
del self[import_.from_]
if import_.alias and import_.from_ in self.alias and import_.import_ in self.alias[import_.from_]:
del self.alias[import_.from_][import_.import_]
if not self.alias[import_.from_]:
Expand Down
5 changes: 2 additions & 3 deletions src/datamodel_code_generator/input_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ def _add_python_type_info(schema: dict[str, Any], model: type) -> dict[str, Any]
continue
nested_model = nested_models[def_name]
nested_fields = getattr(nested_model, "model_fields", None)
if nested_fields: # pragma: no branch
if nested_fields:
_add_python_type_to_properties(def_schema["properties"], nested_fields)

return schema
Expand Down Expand Up @@ -906,8 +906,7 @@ def _load_single_model_schema( # noqa: PLR0912, PLR0914, PLR0915
if not hasattr(obj, "model_json_schema"):
msg = "--input-model with Pydantic model requires Pydantic v2 runtime. Please upgrade Pydantic to v2."
raise Error(msg)
if hasattr(obj, "model_rebuild"): # pragma: no branch
_try_rebuild_model(obj)
_try_rebuild_model(obj)
schema_generator = _get_input_model_json_schema_class()
schema = obj.model_json_schema(schema_generator=schema_generator)
schema = _add_python_type_for_unserializable(schema, obj)
Expand Down
4 changes: 2 additions & 2 deletions src/datamodel_code_generator/model/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ def docstring(self) -> str | None:
parts.append(f"Examples:\n{examples_str}")
elif example is not None:
parts.append(f"Example: {example!r}")
elif examples and isinstance(examples, list) and len(examples) == 1:
elif examples and isinstance(examples, list) and len(examples) == 1: # pragma: no branch
parts.append(f"Example: {examples[0]!r}")

if parts:
Expand Down Expand Up @@ -913,7 +913,7 @@ def path(self) -> str:
def set_reference_path(self, new_path: str) -> None:
"""Set reference path and clear cached path property."""
self.reference.path = new_path
if "path" in self.__dict__:
if "path" in self.__dict__: # pragma: no branch
del self.__dict__["path"]

def render(self, *, class_name: str | None = None) -> str:
Expand Down
16 changes: 8 additions & 8 deletions src/datamodel_code_generator/model/msgspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,12 @@ def _add_unset_type(type_: str, use_union_operator: bool) -> str: # noqa: FBT00
"""Add UnsetType to a type hint without removing None."""
if use_union_operator:
return f"{type_}{UNION_OPERATOR_DELIMITER}{UNSET_TYPE}"
if type_.startswith(UNION_PREFIX):
if type_.startswith(UNION_PREFIX): # pragma: no cover
return f"{type_[:-1]}{UNION_DELIMITER}{UNSET_TYPE}]"
if type_.startswith(OPTIONAL_PREFIX): # pragma: no cover
inner_type = type_[len(OPTIONAL_PREFIX) : -1]
return f"{UNION_PREFIX}{inner_type}{UNION_DELIMITER}{NONE}{UNION_DELIMITER}{UNSET_TYPE}]"
return f"{UNION_PREFIX}{type_}{UNION_DELIMITER}{UNSET_TYPE}]"
return f"{UNION_PREFIX}{type_}{UNION_DELIMITER}{UNSET_TYPE}]" # pragma: no cover


@import_extender
Expand Down Expand Up @@ -281,7 +281,7 @@ def process_const(self) -> None:
self.const = True
self.nullable = False
const = self.extras["const"]
if self.data_type.type == "str" and isinstance(const, str): # pragma: no cover # Literal supports only str
if self.data_type.type == "str" and isinstance(const, str): # pragma: no cover
self.replace_data_type(self.data_type.__class__(literals=[const]), clear_old_parent=False)

def _get_strict_field_constraint_value(self, constraint: str, value: Any) -> Any:
Expand Down Expand Up @@ -405,13 +405,13 @@ def annotated(self) -> str | None: # noqa: PLR0911
For ClassVar fields (discriminator tag_field), ClassVar is required
regardless of use_annotated setting.
"""
if self.extras.get("is_classvar"):
if self.extras.get("is_classvar"): # pragma: no cover
meta = self._get_meta_string()
if self.use_annotated and meta:
return f"ClassVar[Annotated[{self.type_hint}, {meta}]]"
return f"ClassVar[{self.type_hint}]"

if not self.use_annotated: # pragma: no cover
if not self.use_annotated:
return None

meta = self._get_meta_string()
Expand All @@ -438,7 +438,7 @@ def needs_annotated_import(self) -> bool:
"""
if not self.annotated:
return False
if self.extras.get("is_classvar"):
if self.extras.get("is_classvar"): # pragma: no cover
return self.use_annotated and self._get_meta_string() is not None
return True

Expand All @@ -453,10 +453,10 @@ def _get_default_as_struct_model(self) -> str | None:
# TODO: Check nested data_types
if data_type.is_dict:
# TODO: Parse dict model for default
continue # pragma: no cover
continue
if data_type.is_list and len(data_type.data_types) == 1:
data_type_child = data_type.data_types[0]
if ( # pragma: no cover
if (
data_type_child.reference
and (isinstance(data_type_child.reference.source, (Struct, TypeAliasBase)))
and isinstance(self.default, list)
Expand Down
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/model/pydantic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def dict( # type: ignore[override]

if is_pydantic_v2():
return self.model_dump(**kwargs)
return super().dict(**kwargs)
return super().dict(**kwargs) # pragma: no cover


__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/model/pydantic/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def _get_strict_field_constraint_value(self, constraint: str, value: Any) -> Any
if "e" in str_value.lower(): # pragma: no cover
# Scientific notation like 1e-08 - keep as float
return float(value)
if isinstance(value, int) and not isinstance(value, bool): # pragma: no branch
if isinstance(value, int) and not isinstance(value, bool):
return value
return int(value)

Expand Down
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/model/pydantic_v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def dict(self, **kwargs: Any) -> dict[str, Any]: # type: ignore[override]

if is_pydantic_v2():
return self.model_dump(**kwargs)
return super().dict(**kwargs)
return super().dict(**kwargs) # pragma: no cover


__all__ = [
Expand Down
2 changes: 1 addition & 1 deletion src/datamodel_code_generator/parser/_scc.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def extract_scc(self, root: ModulePath) -> None:
w: ModulePath = self.stack.pop()
self.on_stack.remove(w)
scc.add(w)
if w == root: # pragma: no branch
if w == root:
break
self.result.append(scc)

Expand Down
16 changes: 8 additions & 8 deletions src/datamodel_code_generator/parser/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ def to_hashable(item: Any) -> HashableComparable: # noqa: PLR0911
)
if isinstance(item, set): # pragma: no cover
return frozenset(to_hashable(i) for i in item) # type: ignore[return-value]
if isinstance(item, BaseModel):
if isinstance(item, BaseModel): # pragma: no cover
return to_hashable(model_dump(item))
if item is None:
return ""
Expand Down Expand Up @@ -1274,7 +1274,7 @@ def __change_from_import( # noqa: PLR0913, PLR0914
),
)
after_import = model.imports
if before_import != after_import:
if before_import != after_import: # pragma: no cover
imports.append(after_import)

@classmethod
Expand Down Expand Up @@ -1364,7 +1364,7 @@ def check_paths(
t_path = path[str(path).find("/") + 1 :]
t_disc = model.path[: str(model.path).find("#")].lstrip("../") # noqa: B005
t_disc_2 = "/".join(t_disc.split("/")[1:])
if t_path not in {t_disc, t_disc_2}:
if t_path not in {t_disc, t_disc_2}: # pragma: no branch
continue
type_names.append(name)

Expand Down Expand Up @@ -1664,7 +1664,7 @@ def __create_shared_module_from_duplicates( # noqa: PLR0912
msg = f"Duplicate model {duplicate_model.name} not found in module {duplicate_module}"
raise RuntimeError(msg)

for module, models in module_models:
for module, models in module_models: # pragma: no branch
if module != duplicate_module:
continue
if isinstance(duplicate_model, Enum) or not supports_inheritance or self.collapse_reuse_models:
Expand Down Expand Up @@ -1802,7 +1802,7 @@ def __collapse_root_models( # noqa: PLR0912, PLR0914, PLR0915
inner_reference.children.append(data_type)

imports.remove_referenced_imports(root_type_model.path)
if not root_type_model.reference.children:
if not root_type_model.reference.children: # pragma: no branch
unused_models.append(root_type_model)

continue
Expand Down Expand Up @@ -2065,7 +2065,7 @@ def __fix_dataclass_field_ordering(self, models: list[DataModel]) -> None:
for field in model.fields:
if self.__is_new_required_field(field, inherited_names):
field.extras["kw_only"] = True
else:
else: # pragma: no cover
warn(
f"Dataclass '{model.class_name}' has a field ordering conflict due to inheritance. "
f"An inherited field has a default value, but new required fields are added. "
Expand Down Expand Up @@ -2333,7 +2333,7 @@ def __apply_generic_base_class( # noqa: PLR0912, PLR0914, PLR0915
current_module_name = ".".join(module[:-1]) if module else ""
is_first_root = module == first_root_module
for model in target_models:
if original_import:
if original_import: # pragma: no branch
additional_imports = model._additional_imports # noqa: SLF001
model._additional_imports = [i for i in additional_imports if i != original_import] # noqa: SLF001
parent_refs = [bc.reference for bc in model.base_classes if bc.reference]
Expand Down Expand Up @@ -2420,7 +2420,7 @@ def _resolve_export_collisions(
return result

@classmethod
def _raise_collision_error(
def _raise_collision_error( # pragma: no cover
cls,
by_name: dict[str, list[tuple[str, tuple[str, ...], str]]],
colliding: set[str],
Expand Down
Loading
Loading