Skip to content

Migrate from pyright to ty type checker#2928

Merged
koxudaxi merged 2 commits intomainfrom
migrate-pyright-to-ty
Jan 5, 2026
Merged

Migrate from pyright to ty type checker#2928
koxudaxi merged 2 commits intomainfrom
migrate-pyright-to-ty

Conversation

@koxudaxi
Copy link
Copy Markdown
Owner

@koxudaxi koxudaxi commented Jan 5, 2026

Summary by CodeRabbit

  • Chores
    • Updated development tooling and internal configuration for project maintenance.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 5, 2026

Warning

Rate limit exceeded

@koxudaxi has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 52 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 0d090d5 and 9cd72a3.

📒 Files selected for processing (4)
  • src/datamodel_code_generator/format.py
  • src/datamodel_code_generator/input_model.py
  • src/datamodel_code_generator/parser/graphql.py
  • src/datamodel_code_generator/parser/jsonschema.py
📝 Walkthrough

Walkthrough

This pull request migrates the project's type checking tool from Pyright to Ty. The changes include updating the dependency declaration in pyproject.toml, replacing Pyright-specific type-ignore directives with generic ty: ignore comments throughout the codebase, and updating the type-checking command in tox.ini.

Changes

Cohort / File(s) Summary
Configuration & Build
pyproject.toml, tox.ini
Replaced Pyright dependency with ty>=0.0.8; updated [tool.pyright] configuration block with new [tool.ty.*] configuration sections for src, environment, and rules; changed type-checking command from pyright src to ty check src
Parser Modules
src/datamodel_code_generator/__init__.py, src/datamodel_code_generator/parser/base.py, src/datamodel_code_generator/parser/graphql.py, src/datamodel_code_generator/parser/jsonschema.py, src/datamodel_code_generator/parser/openapi.py
Updated type-ignore comments from Pyright-specific directives to generic ty: ignore annotations on parser instantiations, config creation, and resolver/field handling
Pydantic Model Modules
src/datamodel_code_generator/model/base.py, src/datamodel_code_generator/model/pydantic/__init__.py, src/datamodel_code_generator/model/pydantic/base_model.py, src/datamodel_code_generator/model/pydantic/types.py, src/datamodel_code_generator/model/pydantic_v2/base_model.py, src/datamodel_code_generator/model/pydantic_v2/dataclass.py, src/datamodel_code_generator/model/msgspec.py
Replaced Pyright-specific ignore comments (e.g., reportAssignmentType, reportIncompatibleVariableOverride) with generic ty: ignore annotations on ConfigDict usage, field validators, and model configuration declarations
Utility & Core Modules
src/datamodel_code_generator/__main__.py, src/datamodel_code_generator/format.py, src/datamodel_code_generator/http.py, src/datamodel_code_generator/input_model.py, src/datamodel_code_generator/types.py, src/datamodel_code_generator/util.py, src/datamodel_code_generator/pydantic_patch.py, src/datamodel_code_generator/reference.py, src/datamodel_code_generator/watch.py
Updated type-ignore directives from Pyright-specific comments to generic ty: ignore across validators, return statements, config assignments, and function call annotations

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Suggested labels

safe-to-fix

Suggested reviewers

  • ilovelinux

Poem

🐰 A rabbit hops through code so bright,
From Pyright's gaze to Ty's keen sight!
Each # ty: ignore placed with care,
Type-checking tools beyond compare,
The migration's done—hooray, hooray! ✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and directly describes the main change: migrating the project from pyright to ty type checker, which is reflected across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 84.78% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 5, 2026

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Jan 5, 2026

CodSpeed Performance Report

Merging #2928 will not alter performance

Comparing migrate-pyright-to-ty (9cd72a3) with main (5acb178)

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

Summary

✅ 11 untouched
⏩ 98 skipped1

Footnotes

  1. 98 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/datamodel_code_generator/util.py (1)

185-201: Clean up unused noqa on validator import

Ruff flags the # noqa: PLC0415 on the validator import as unused (rule not enabled), so it can be dropped without changing behavior. The adjacent # ty: ignore on the return lines is fine for the Pydantic v1/v2 shim.

[static_analysis_hint]

Suggested diff to remove unused # noqa
-        from pydantic import validator  # noqa: PLC0415  # pragma: no cover
+        from pydantic import validator  # pragma: no cover
src/datamodel_code_generator/parser/openapi.py (1)

309-323: Discriminator field augmentation and response typing: behavior OK; drop unused PLW2901 noqa

  • Using properties.get(field_obj.original_name) with # ty: ignore and then guarding via isinstance(field, JsonSchemaObject) is safe and leaves runtime unchanged.
  • Similarly, data_types[status_code][content_type] = data_type # ty: ignore only suppresses a key-type check on the defaultdict.

Ruff also reports # noqa: PLW2901 here as unused; you can simplify the line to keep only the Ty suppression:

[static_analysis_hint]

Suggested diff for PLW2901 # noqa cleanup
-                field_obj = self.data_model_field_type(**{  # noqa: PLW2901  # ty: ignore
+                field_obj = self.data_model_field_type(**{  # ty: ignore

Also applies to: 480-484

🧹 Nitpick comments (5)
src/datamodel_code_generator/watch.py (1)

16-16: Address the unused noqa directive.

The static analysis tool correctly identifies that the noqa: PLC0415 directive is no longer needed (Ruff reports it as unused). Consider removing it.

🔎 Proposed fix
-        import watchfiles  # noqa: PLC0415  # ty: ignore
+        import watchfiles  # ty: ignore
src/datamodel_code_generator/reference.py (1)

1234-1240: Optional: tighten get_singular_name to avoid ty ignores

You can remove the # ty: ignore markers here by explicitly narrowing the return type from inflect:

Proposed refactor for get_singular_name
 @lru_cache
 def get_singular_name(name: str, suffix: str = SINGULAR_NAME_SUFFIX) -> str:
     """Convert a plural name to singular form."""
-    singular_name = _get_inflect_engine().singular_noun(cast("inflect.Word", name))  # ty: ignore
-    if singular_name is False:
-        singular_name = f"{name}{suffix}"
-    return singular_name  # ty: ignore
+    singular = _get_inflect_engine().singular_noun(cast("inflect.Word", name))
+    if singular is False:
+        return f"{name}{suffix}"
+    return cast(str, singular)
src/datamodel_code_generator/parser/openapi.py (1)

209-213: Config-derived attributes with ty-ignores are fine, but could later be tightened

Reading openapi_scopes, include_path_parameters, use_status_code_in_response_name, and openapi_include_paths from self.config with # ty: ignore is consistent with how Parser.config is used elsewhere, and doesn’t affect behavior. If you ever want to drop these ignores, a small runtime assertion (assert self.config is not None) or a narrowed attribute type on Parser.config would be enough.

src/datamodel_code_generator/__init__.py (1)

758-758: Clean up unused noqa directive.

Ruff correctly identifies an unused noqa directive on line 758. The comment # noqa: PLC0415 can be removed as the rule is not currently enabled.

🔎 Proposed fix
-        from datamodel_code_generator.parser.jsonschema import JsonSchemaParser  # noqa: PLC0415
+        from datamodel_code_generator.parser.jsonschema import JsonSchemaParser
src/datamodel_code_generator/__main__.py (1)

265-456: Clean up unused noqa directives.

Ruff identifies several unused # noqa: N805 directives on the following lines:

  • Line 265
  • Line 273
  • Line 429
  • Line 436
  • Line 456

These can be safely removed as the N805 rule is not currently enabled.

🔎 Proposed fix
-    @model_validator(mode="before")  # ty: ignore
-    def validate_additional_imports(cls, values: dict[str, Any]) -> dict[str, Any]:  # noqa: N805
+    @model_validator(mode="before")  # ty: ignore
+    def validate_additional_imports(cls, values: dict[str, Any]) -> dict[str, Any]:

Apply similar changes to lines 273, 429, 436, and 456.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5acb178 and 0d090d5.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock, !**/*.lock and included by none
📒 Files selected for processing (23)
  • pyproject.toml
  • src/datamodel_code_generator/__init__.py
  • src/datamodel_code_generator/__main__.py
  • src/datamodel_code_generator/format.py
  • src/datamodel_code_generator/http.py
  • src/datamodel_code_generator/input_model.py
  • src/datamodel_code_generator/model/base.py
  • src/datamodel_code_generator/model/msgspec.py
  • src/datamodel_code_generator/model/pydantic/__init__.py
  • src/datamodel_code_generator/model/pydantic/base_model.py
  • src/datamodel_code_generator/model/pydantic/types.py
  • src/datamodel_code_generator/model/pydantic_v2/base_model.py
  • src/datamodel_code_generator/model/pydantic_v2/dataclass.py
  • src/datamodel_code_generator/parser/base.py
  • src/datamodel_code_generator/parser/graphql.py
  • src/datamodel_code_generator/parser/jsonschema.py
  • src/datamodel_code_generator/parser/openapi.py
  • src/datamodel_code_generator/pydantic_patch.py
  • src/datamodel_code_generator/reference.py
  • src/datamodel_code_generator/types.py
  • src/datamodel_code_generator/util.py
  • src/datamodel_code_generator/watch.py
  • tox.ini
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-01-02T08:25:19.839Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2890
File: tests/data/expected/main/jsonschema/ref_nullable_with_constraint.py:14-15
Timestamp: 2026-01-02T08:25:19.839Z
Learning: The datamodel-code-generator currently generates RootModel subclasses with an explicit `root` field annotation (e.g., `class StringType(RootModel[str]): root: str`). This is existing behavior of the code generator and should not be flagged as an issue introduced by new changes.

Applied to files:

  • src/datamodel_code_generator/parser/base.py
  • src/datamodel_code_generator/model/pydantic/base_model.py
  • src/datamodel_code_generator/model/base.py
  • src/datamodel_code_generator/__init__.py
  • src/datamodel_code_generator/reference.py
  • src/datamodel_code_generator/model/pydantic/types.py
  • src/datamodel_code_generator/watch.py
  • src/datamodel_code_generator/format.py
  • src/datamodel_code_generator/model/pydantic_v2/dataclass.py
  • src/datamodel_code_generator/util.py
  • src/datamodel_code_generator/types.py
  • src/datamodel_code_generator/model/pydantic_v2/base_model.py
  • src/datamodel_code_generator/parser/openapi.py
  • src/datamodel_code_generator/__main__.py
📚 Learning: 2025-12-25T09:23:08.506Z
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/util.py:49-66
Timestamp: 2025-12-25T09:23:08.506Z
Learning: In datamodel-code-generator, the is_pydantic_v2() and is_pydantic_v2_11() functions in src/datamodel_code_generator/util.py intentionally use global variable caching (_is_v2, _is_v2_11) on top of lru_cache for performance optimization. This dual-layer caching eliminates function call overhead and cache lookup overhead for frequently-called version checks. The PLW0603 linter warnings should be suppressed with # noqa: PLW0603 as this is a deliberate design choice.

Applied to files:

  • src/datamodel_code_generator/model/base.py
  • src/datamodel_code_generator/reference.py
  • src/datamodel_code_generator/util.py
  • src/datamodel_code_generator/types.py
  • src/datamodel_code_generator/__main__.py
🧬 Code graph analysis (12)
src/datamodel_code_generator/model/pydantic/base_model.py (2)
src/datamodel_code_generator/util.py (1)
  • model_validate (302-306)
src/datamodel_code_generator/model/pydantic/__init__.py (1)
  • Config (26-47)
src/datamodel_code_generator/model/base.py (1)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
  • ConfigDict (29-55)
src/datamodel_code_generator/parser/jsonschema.py (2)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
  • ConfigDict (29-55)
src/datamodel_code_generator/config.py (1)
  • JSONSchemaParserConfig (350-351)
src/datamodel_code_generator/input_model.py (2)
src/datamodel_code_generator/model/base.py (1)
  • model_rebuild (201-208)
src/datamodel_code_generator/types.py (1)
  • model_rebuild (415-422)
src/datamodel_code_generator/reference.py (2)
src/datamodel_code_generator/util.py (4)
  • model_validator (125-130)
  • model_validator (134-140)
  • model_validator (144-148)
  • model_validator (151-182)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
  • ConfigDict (29-55)
src/datamodel_code_generator/model/msgspec.py (2)
src/datamodel_code_generator/types.py (2)
  • chain_as_tuple (186-193)
  • imports (620-674)
src/datamodel_code_generator/model/base.py (2)
  • imports (326-351)
  • imports (823-828)
src/datamodel_code_generator/model/pydantic_v2/dataclass.py (3)
src/datamodel_code_generator/model/msgspec.py (1)
  • Constraints (212-217)
src/datamodel_code_generator/model/pydantic/base_model.py (1)
  • Constraints (38-50)
src/datamodel_code_generator/model/pydantic_v2/base_model.py (1)
  • Constraints (56-74)
src/datamodel_code_generator/util.py (2)
src/datamodel_code_generator/reference.py (1)
  • _BaseModel (82-139)
src/datamodel_code_generator/__main__.py (1)
  • parse_obj (161-163)
src/datamodel_code_generator/types.py (1)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
  • ConfigDict (29-55)
src/datamodel_code_generator/model/pydantic_v2/base_model.py (4)
src/datamodel_code_generator/util.py (5)
  • model_validator (125-130)
  • model_validator (134-140)
  • model_validator (144-148)
  • model_validator (151-182)
  • model_validate (302-306)
src/datamodel_code_generator/model/msgspec.py (1)
  • Constraints (212-217)
src/datamodel_code_generator/model/pydantic/base_model.py (1)
  • Constraints (38-50)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
  • ConfigDict (29-55)
src/datamodel_code_generator/parser/openapi.py (5)
src/datamodel_code_generator/parser/base.py (1)
  • _create_default_config (699-727)
src/datamodel_code_generator/parser/jsonschema.py (1)
  • _create_default_config (659-683)
src/datamodel_code_generator/_types/parser_config_dicts.py (1)
  • OpenAPIParserConfigDict (173-177)
src/datamodel_code_generator/config.py (1)
  • OpenAPIParserConfig (354-360)
src/datamodel_code_generator/enums.py (1)
  • OpenAPIScope (70-78)
src/datamodel_code_generator/__main__.py (2)
src/datamodel_code_generator/model/pydantic_v2/__init__.py (1)
  • ConfigDict (29-55)
src/datamodel_code_generator/util.py (4)
  • model_validator (125-130)
  • model_validator (134-140)
  • model_validator (144-148)
  • model_validator (151-182)
🪛 Ruff (0.14.10)
src/datamodel_code_generator/__init__.py

758-758: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/watch.py

16-16: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/util.py

197-197: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/model/pydantic_v2/base_model.py

125-125: Unused noqa directive (non-enabled: UP045)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/parser/openapi.py

318-318: Unused noqa directive (non-enabled: PLW2901)

Remove unused noqa directive

(RUF100)

src/datamodel_code_generator/__main__.py

265-265: Unused noqa directive (non-enabled: N805)

Remove unused noqa directive

(RUF100)


273-273: Unused noqa directive (non-enabled: N805)

Remove unused noqa directive

(RUF100)


429-429: Unused noqa directive (non-enabled: N805)

Remove unused noqa directive

(RUF100)


436-436: Unused noqa directive (non-enabled: N805)

Remove unused noqa directive

(RUF100)


456-456: Unused noqa directive (non-enabled: N805)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: 3.10 on macOS
  • GitHub Check: 3.10 on Windows
  • GitHub Check: benchmarks
  • GitHub Check: 3.14 on Windows
  • GitHub Check: 3.12 on Windows
  • GitHub Check: 3.11 on macOS
  • GitHub Check: 3.13 on macOS
  • GitHub Check: 3.13 on Windows
  • GitHub Check: 3.11 on Windows
  • GitHub Check: Analyze (python)
🔇 Additional comments (31)
tox.ini (1)

134-134: LGTM! Command updated correctly for Ty type checker.

The type checking command has been properly updated from pyright src to ty check src, which is the expected change for this migration.

src/datamodel_code_generator/parser/base.py (2)

1399-1399: Type suppressions added for various operations.

Additional ty: ignore directives have been added for:

  • Discriminator path checking (lines 1399, 1404)
  • Enum member alias assignment (line 1956)
  • Resolver model name generation (line 2071)

These changes are consistent with the Pyright to Ty migration pattern seen throughout the codebase.

Also applies to: 1404-1404, 1956-1956, 2071-2071


699-699: Multiple ty: ignore suppressions need evaluation.

The codebase has added ty: ignore directives across several locations (lines 699, 726, 751, 1399, 1404, 1956, 2071). While Ty is the official type checker for this project, the underlying type issues causing these suppressions should be evaluated to determine whether they represent legitimate type incompatibilities that require suppression, or whether the code should be refactored instead.

Consider:

  • Reviewing what specific type warnings Ty reports for these lines
  • Whether improved type annotations or method signatures could eliminate the need for suppressions
  • Using more granular type ignore codes if Ty supports them
src/datamodel_code_generator/http.py (1)

43-43: LGTM! Type suppression migrated correctly.

The type ignore directive has been updated to use the generic ty: ignore format, consistent with the migration from Pyright to Ty.

src/datamodel_code_generator/pydantic_patch.py (1)

29-29: LGTM! Migrated from Pyright-specific ignore.

The type suppression has been correctly updated from the Pyright-specific type: ignore[reportAttributeAccessIssue] to the generic ty: ignore format.

src/datamodel_code_generator/model/pydantic/types.py (1)

324-324: Type suppression added for strict kwarg assignment.

The ty: ignore directive has been added when setting the strict keyword argument. This is consistent with the migration pattern, though the suppression is new rather than a replacement of an existing Pyright-specific ignore.

src/datamodel_code_generator/model/pydantic/base_model.py (1)

421-421: LGTM! Type-ignore directive successfully migrated.

The change from a Pyright-specific ignore to a generic ty: ignore directive is consistent with the PR's migration objective. The runtime behavior remains unchanged.

src/datamodel_code_generator/watch.py (1)

16-16: LGTM! Type-ignore directives successfully migrated.

Both changes from Pyright-specific ignores to generic ty: ignore directives are consistent with the PR's migration objective. The runtime behavior remains unchanged.

Also applies to: 53-53

src/datamodel_code_generator/types.py (1)

407-407: LGTM! Type-ignore directives successfully migrated.

Both changes from Pyright-specific ignores to generic ty: ignore directives are consistent with the PR's migration objective. The changes are annotation-only and preserve the existing runtime behavior in the core type system.

Also applies to: 602-602

src/datamodel_code_generator/model/msgspec.py (1)

84-84: LGTM! Type-ignore directives successfully migrated.

All three changes from Pyright-specific ignores to generic ty: ignore directives are consistent with the PR's migration objective. The changes are annotation-only within the import_extender decorator and preserve existing functionality.

Also applies to: 103-103, 105-105

src/datamodel_code_generator/model/pydantic_v2/dataclass.py (1)

154-154: LGTM! Type-ignore directive successfully migrated.

The change from a Pyright-specific reportIncompatibleVariableOverride ignore to a generic ty: ignore directive is consistent with the PR's migration objective. This addresses the known constraints field override pattern in Pydantic v2, and the runtime behavior remains unchanged.

src/datamodel_code_generator/parser/graphql.py (1)

50-52: Ty ignores around GraphQL introspection and parser dispatch look appropriate

The added # ty: ignore markers are confined to dynamic graphql-core introspection (TypeResolvers/TypeFields), Pydantic v1 defaults construction, and parser method overrides/call-sites where static typing can’t model the runtime protocol. They don’t alter behavior and are consistent with the patterns used elsewhere in the parsers.

Also applies to: 115-115, 220-222, 240-247, 452-452, 468-480, 528-528

src/datamodel_code_generator/parser/jsonschema.py (1)

368-372: Pydantic-related ty ignores are localized and consistent with existing patterns

Using # ty: ignore on ConfigDict(...), JsonSchemaObject.get_fields(), and the __fields__/defaults.update(options) path keeps the type checker out of Pydantic internals while matching the same pattern used in Parser._create_default_config and other parsers. No runtime semantics change and the config/default handling remains intact.

Also applies to: 541-549, 681-683

src/datamodel_code_generator/reference.py (3)

99-139: Custom BaseModel.dict ty-ignores are reasonable around include/skip_defaults

The extra # ty: ignore on include= and skip_defaults= in both v2 and v1 dict() implementations are constrained to version-compatibility shims and don’t affect behavior. Given these signatures mirror Pydantic and are already wrapped in non-TYPE_CHECKING blocks, this is a pragmatic way to keep Ty happy.


157-177: Reference.model_validator and model_config ty-ignores match the util helpers

Applying # ty: ignore on @model_validator(mode="before") and ConfigDict(...) aligns with the generic decorator/ConfigDict proxies in util.py. These are narrow, Pydantic-version-compatibility spots and the runtime behavior of validate_original_name and the config flags is unchanged.


649-655: context_variable usages with ty-ignores are acceptable for now

The # ty: ignore on context_variable(...) calls for current_base_path, base_url, and current_root are only suppressing generic-type mismatches between the setter and current value/new value. The runtime behavior (push/pop of these context values) is unchanged and already well-encapsulated.

Also applies to: 667-669, 682-685

src/datamodel_code_generator/util.py (2)

120-120: Ty-ignores on Model TypeVar and model_validator are acceptable in this compatibility shim

Binding Model to _BaseModel with # ty: ignore and marking the concrete model_validator(...) implementation with # ty: ignore are localized to the decorator glue that unifies Pydantic v1/v2. They don’t affect runtime and keep type-checker friction away from a heavily overloaded API.

Also applies to: 151-183


295-320: Pydantic v1/v2 helpers (model_dump/validate/fields_set/copy) use ty-ignores appropriately

The added # ty: ignore annotations on model_dump, model_validate, get_fields_set, and model_copy calls are restricted to places where the union of Pydantic v1/v2 APIs can’t be fully expressed in the stubs. The runtime branching on is_pydantic_v2() remains unchanged, so these are safe suppressions.

src/datamodel_code_generator/parser/openapi.py (1)

170-196: OpenAPIParser._create_default_config Pydantic v2 branch mirrors JSONSchema logic

The new _create_default_config correctly follows the JSONSchema/OpenAPI pattern: for v2 it calls model_rebuild(... _types_namespace=...) then model_validate(options), and for v1 it uses update_forward_refs, builds defaults from __fields__, updates with options (# ty: ignore), and constructs the config. This matches the other parsers and keeps behavior backward compatible.

src/datamodel_code_generator/__init__.py (1)

746-761: LGTM: Type checker migration applied consistently.

The migration from Pyright-specific ignores to generic ty: ignore directives is correctly applied to all three parser instantiations.

src/datamodel_code_generator/input_model.py (1)

301-920: LGTM: Type checker migration applied correctly.

The ty: ignore directives are appropriately placed on:

  • Type operations with potential ambiguity (lines 301, 303, 313, 360)
  • Pydantic v1/v2 compatibility paths in model_rebuild calls (lines 600, 609, 611)
  • Conditional checks (line 920)

All changes align with the project-wide migration from Pyright to Ty.

pyproject.toml (2)

259-266: LGTM: Ty configuration is well-structured.

The Ty type checker configuration is properly organized with:

  • Source path inclusion (src)
  • Python version targeting (3.10)
  • Sensible rule configuration (deprecated warnings ignored)

This aligns well with the project's existing setup.


99-99: The dependency constraint is appropriate.

The latest stable version of the ty type checker is 0.0.9 (released January 5, 2026), not 0.0.8. However, the constraint ty>=0.0.8 is correct as written—it will accept the latest version 0.0.9 during installation. No change needed.

src/datamodel_code_generator/__main__.py (1)

150-954: LGTM: Type checker migration applied comprehensively.

The migration from Pyright to Ty is correctly applied across all Pydantic validators and model configuration. The ty: ignore directives properly handle type checking complexities in:

  • Model configuration (lines 150, 158)
  • Model validators for both Pydantic v1 and v2 (lines 264-462)
  • Field validators (lines 398, 406)
  • Function arguments (line 954)
src/datamodel_code_generator/model/base.py (1)

107-158: LGTM: Type checker migration applied to Pydantic configurations.

The ty: ignore directives are correctly placed on model_config declarations for both ConstraintsBase and DataModelFieldBase classes, handling Pydantic v2 ConfigDict type checking requirements.

src/datamodel_code_generator/format.py (1)

83-83: LGTM! Type annotation updates align with migration objective.

The addition of # ty: ignore comments to these Python version comparison methods is consistent with the migration from pyright to ty type checker. No functional changes are introduced.

Also applies to: 87-87, 91-91, 95-100

src/datamodel_code_generator/model/pydantic/__init__.py (1)

41-41: LGTM! Type annotation update aligns with migration.

The # ty: ignore comment on the return type annotation is consistent with the existing # type: ignore[override] decorator and supports the migration from pyright to ty type checker.

src/datamodel_code_generator/model/pydantic_v2/base_model.py (4)

63-63: LGTM! Decorator annotation update aligns with migration.

The # ty: ignore comment on the @model_validator decorator is consistent with the migration from pyright to ty type checker.


308-308: LGTM! Assignment annotation aligns with migration.

The # ty: ignore comment on the model_validate call is consistent with the migration from pyright to ty type checker.


398-398: LGTM! Assignment annotation aligns with migration.

The # ty: ignore comment on the prepared_validators assignment is consistent with the migration from pyright to ty type checker.


125-125: Remove unused noqa directive.

The # noqa: UP045 directive is unused according to static analysis. Consider removing it while keeping the # ty: ignore comment.

🔎 Proposed fix
-    constraints: Optional[Constraints] = None  # ty: ignore  # noqa: UP045
+    constraints: Optional[Constraints] = None  # ty: ignore
⛔ Skipped due to learnings
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2799
File: src/datamodel_code_generator/model/pydantic/__init__.py:43-43
Timestamp: 2025-12-25T09:22:22.481Z
Learning: In datamodel-code-generator project, defensive `# noqa: PLC0415` directives should be kept on lazy imports (imports inside functions/methods) even when Ruff reports them as unused via RUF100, to prepare for potential future Ruff configuration changes that might enable the import-outside-top-level rule.
Learnt from: koxudaxi
Repo: koxudaxi/datamodel-code-generator PR: 2681
File: tests/cli_doc/test_cli_doc_coverage.py:82-82
Timestamp: 2025-12-18T13:43:16.235Z
Learning: In datamodel-code-generator project, Ruff preview mode is enabled via `lint.preview = true` in pyproject.toml. This enables preview rules like PLR6301 (no-self-use), so `noqa: PLR6301` directives are necessary and should not be removed even if RUF100 suggests they are unused.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (5acb178) to head (9cd72a3).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #2928   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           92        92           
  Lines        16957     16957           
  Branches      1976      1976           
=========================================
  Hits         16957     16957           
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@koxudaxi koxudaxi merged commit 61c08d6 into main Jan 5, 2026
39 checks passed
@koxudaxi koxudaxi deleted the migrate-pyright-to-ty branch January 5, 2026 16:56
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 5, 2026

Breaking Change Analysis

Result: No breaking changes detected

Reasoning: This PR migrates the project's internal type checker from pyright to ty. The changes are purely internal development tooling:

  1. Development dependency change: Replaced pyright>=1.1.393 with ty>=0.0.8 in pyproject.toml's type dependency group
  2. Type ignore comment changes: Changed # pyright: ignore[...] to # ty: ignore throughout the codebase - these are internal type checker suppression comments that don't affect runtime behavior
  3. Configuration changes: Replaced [tool.pyright] section with [tool.ty.*] sections in pyproject.toml
  4. Tox command change: Changed pyright src to ty check src in tox.ini

None of these changes affect:

  • Code generation output (no changes to templates or generation logic)
  • CLI options or Python API
  • Default behavior of the tool
  • Python version support
  • Error handling
  • Custom templates

The only people who would notice this change are contributors who run type checking, not end users of the datamodel-code-generator tool.


This analysis was performed by Claude Code Action

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jan 5, 2026

🎉 Released in 0.52.2

This PR is now available in the latest release. See the release notes for details.

@nbro10
Copy link
Copy Markdown
Contributor

nbro10 commented Jan 9, 2026

Is ty really better than pyright or mypy at this point, except for speed? I'd like to know the reasons why people are migrating from the standard/stable tools to experimental tools (including poetry -> uv), other than the supposed better speed. ty is at version 0.0.10, which means it's very experimental. Don't get me wrong, I actually like very much Rust and appreciate speed, but migrating from a very stable tool to basically an experimental tool that could die tomorrow doesn't make much sense, if the only advantage is a relatively insignificant speed up during development.

@ilovelinux
Copy link
Copy Markdown
Collaborator

Premise: I have no experience with ty yet.

I partially agree with @nbro10, ty is a bleeding edge tool and it isn't going to compete with more stable and vastly (functionally) better tools. But it's a type checker, it's not going to impact that much the development of the project (except code quality in the long term).

I appreciate using the new bleeding edge tools, as it has been done replacing pre-commit with prek and adding support for a new YAML parsing library proposed in #2785. However it may be a good idea to try bleeding-edge tools like type checkers & linters in tandem with more stable and widely supported tools, at least a certain point of the development (e.g. for non-draft PRs, only in CI)

@koxudaxi how's your experience been so far?

@koxudaxi
Copy link
Copy Markdown
Owner Author

@nbro10

Thank you for noticing the recent changes to this project. I really appreciate it.
Your point is valid.

Is ty really better than pyright or mypy at this point, except for speed?

It is precisely for speed.
This project is OSS and I do not get paid for it, so I dedicate a significant amount of my free time to development. This was a trade-off decision to speed up development iterations and save time.
I have not counted precisely, but I have merged over 300 PRs in the past month or so.
Even small time savings add up to significant time and stress reduction. (Of course, I run the type checker multiple times per PR.)

I ran a very simple benchmark again here.

These are the results on my Ryzen 5 7600X (OC), which I have been using for development recently.

$ time uv tool run pyright src > /dev/null
uv tool run pyright src > /dev/null  6.54s user 0.15s system 237% cpu 2.815 total

$ time uv tool run ty check src > /dev/null
uv tool run ty check src > /dev/null  0.52s user 0.07s system 432% cpu 0.137 total

On my most powerful machine, an M4 Max (Mac Studio):

$ time uv tool run pyright src > /dev/null
uv tool run pyright src > /dev/null  4.30s user 0.23s system 192% cpu 2.356 total

$ time uv tool run ty check src > /dev/null
uv tool run ty check src > /dev/null  0.47s user 0.07s system 403% cpu 0.136 total

It is 10x faster, but we are talking about just a few seconds. However, when you run this many times per PR, the accumulated time becomes quite stressful.
(Regarding Poetry → uv, there were also issues such as the inability to handle complex locks properly, and lock times sometimes took several minutes to tens of minutes.)

but migrating from a very stable tool to basically an experimental tool that could die tomorrow doesn't make much sense,

Of course that possibility exists, but switching back to pyright would not take much time.

As for functional advantages, type checking stability, and accuracy beyond speed, I am not too concerned about these.
This project does not use very complex type definitions (such as complex generics or continuous type inference using ParamSpec with decorators).
For this project, I ensure type stability at the runtime level by using Pydantic for external interactions (CLI and parser inputs). (It seems that ty does not have perfect support for Pydantic yet, but I am not too worried since we have runtime checks.)
Additionally, I enhance type safety in internal implementations by adding type annotations to functions.
Therefore, difficult type inference should not be necessary here, and I believe there are few problems that the experimental ty cannot solve.

Furthermore, this project has an extensive set of e2e tests. We have combinations of input + params and expected Python file outputs. Even if there are type-related issues, I believe we can prevent critical bugs and regressions.

I think this OSS is used in many production systems, so I felt it would be good to share this kind of information more openly.
Please feel free to comment if you have any concerns or opinions.

@ilovelinux

However it may be a good idea to try bleeding-edge tools like type checkers & linters in tandem with more stable and widely supported tools, at least a certain point of the development (e.g. for non-draft PRs, only in CI)

I considered this approach initially, but it felt a bit excessive for this project, so I did not adopt it.
I think this is also a trade-off between development speed (time spent locally and in CI) and safety, so I would like to find the appropriate balance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants