Skip to content

refactor: replace mypy with ty for static type checking#1932

Open
bearomorphism wants to merge 5 commits intomasterfrom
replace-mypy-with-ty
Open

refactor: replace mypy with ty for static type checking#1932
bearomorphism wants to merge 5 commits intomasterfrom
replace-mypy-with-ty

Conversation

@bearomorphism
Copy link
Copy Markdown
Collaborator

@bearomorphism bearomorphism commented Apr 12, 2026

Switch the linters group and poe lint from mypy to Astral ty, add [tool.ty] configuration, and adjust code and tests for ty semantics.

Made-with: Cursor, GitHub Copilot CLI

Description

Replace mypy with ty (Astral's Python type checker) for static type checking. ty is significantly faster and integrates well with the existing ruff toolchain.

Key changes

  • Replace mypy with ty in the linters dependency group and poe lint task
  • Add [tool.ty] configuration in pyproject.toml
  • Bump ty minimum to >=0.0.33 to leverage declared-type preference (removes unnecessary cast() workarounds)
  • Remove cast() calls where ty 0.0.33 can infer types from annotated assignments
  • Adjust # type: ignore comments to use bare form (ty doesn't support mypy-specific error codes like [arg-type])
  • Update AGENTS.md references from mypy to ty

Why each change is necessary

All changes in this PR are required for ty compatibility — no cosmetic-only changes:

File(s) Change Reason
pyproject.toml Replace mypy with ty, add [tool.ty] config, bump to >=0.0.33 Core migration
commitizen/commands/bump.py Remove 2 cast() calls, add TODO on remaining ty 0.0.33 declared-type preference makes them unnecessary
commitizen/commands/changelog.py Replace cast() with annotated assignment ty 0.0.33 declared-type preference
commitizen/version_schemes.py Replace cast() with annotated assignment, update comment ty 0.0.33 declared-type preference; "mypy check" → "static type checking"
scripts/gen_cli_help_screenshots.py Replace 2 cast() calls with annotated assignments ty 0.0.33 declared-type preference
commitizen/changelog_formats/__init__.py Refactor name and X or Y to explicit if/else; rename formatformat_cls ty can't narrow the short-circuit and/or pattern; avoids shadowing Python builtin
commitizen/changelog.py Add isinstance(msg, dict) guard ty requires explicit narrowing for dict type in mixed-type iteration
commitizen/cz/customize/customize.py Reformat multiline to separate # type: ignore from # pragma: no cover ty doesn't support [no-any-return] error code
commitizen/providers/*.py Change # type: ignore[index]# type: ignore # noqa: PGH003; rename pyprojectdocument in poetry_provider to match parent class ty doesn't support mypy error codes; consistency with base_provider.py
commitizen/cli.py, commitizen/git.py, commitizen/config/*.py, commitizen/cz/** Adjust # type: ignore comments ty uses bare # type: ignore (no mypy-specific codes)
tests/test_cli.py Change ""None in commitizen_excepthook() calls ty correctly catches "" is not TracebackType | None
tests/test_cz_search_filter.py Change path="not_exist.toml"path=Path("not_exist.toml") ty correctly catches string is not Path
tests/test_changelog.py Add cast() for release["changes"] lookups, add test for non-dict skip Fix 4 pre-existing diagnostics; cover isinstance guard added for ty
tests/commands/test_version_command.py Fix # type: ignore[arg-type] → bare form, remove orphaned TYPE_CHECKING ty syntax compatibility
docs/contributing/contributing_tldr.md Update mypy references to ty Documentation accuracy
AGENTS.md Update 3 mypy references to ty Project guidelines accuracy

Checklist

Was generative AI tooling used to co-author this PR?

  • Yes — Cursor, GitHub Copilot CLI

Code Changes

  • Run uv run poe all locally to ensure this change passes linter check and tests
  • Manually test the changes:
    • Verify uv run ty check passes with zero diagnostics
    • Verify uv run ruff check passes
    • Ensure backward compatibility is maintained (no runtime behavior changes — casts are no-ops, test fixes are type-correct)
  • Update the documentation for the changes (docs/contributing/contributing_tldr.md, AGENTS.md)

Expected Behavior

  • uv run poe lint runs ruff check + ty check instead of ruff check + mypy
  • uv run ty check reports zero diagnostics
  • All existing tests continue to pass unchanged
  • No runtime behavior changes (type annotations and casts are purely static; test argument fixes pass correct types)

Steps to Test This Pull Request

  1. uv sync --group linters
  2. uv run ty check — should report zero diagnostics
  3. uv run ruff check — should pass
  4. uv run poe test — all tests pass

Additional Context

  • ty 0.0.33 (released April 28, 2026) introduced a "declared-type preference" feature that allows removing cast() workarounds where the RHS returns Any/Unknown but an annotation is present.
  • Remaining casts are for cases ty genuinely cannot infer: protocol conformance (VersionScheme), TypedDict construction from spread dicts, and union narrowing in loops.
  • One TODO remains in commitizen/commands/bump.py for a cast that can be removed once self.file_name is narrowed to str (currently str | None).

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.11%. Comparing base (b4f4209) to head (747e04f).
⚠️ Report is 3 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1932   +/-   ##
=======================================
  Coverage   98.10%   98.11%           
=======================================
  Files          61       61           
  Lines        2748     2754    +6     
=======================================
+ Hits         2696     2702    +6     
  Misses         52       52           

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

@bearomorphism bearomorphism force-pushed the replace-mypy-with-ty branch 3 times, most recently from 643d2eb to 0a22b49 Compare April 12, 2026 14:04
@bearomorphism bearomorphism requested a review from Copilot April 12, 2026 14:05
@bearomorphism bearomorphism changed the title build: replace mypy with ty for static type checking refactor: replace mypy with ty for static type checking Apr 12, 2026
@bearomorphism bearomorphism marked this pull request as ready for review April 12, 2026 14:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Switches the project’s static type checking from mypy to Astral’s ty, updates configuration accordingly, and adjusts code/tests to satisfy ty’s typing semantics.

Changes:

  • Replace mypy with ty in dependency groups and poe lint, and add [tool.ty] configuration.
  • Update codebase to align with ty typing rules (casts, TypedDict interactions, ignore directives).
  • Add/adjust tests to match new typing-related behavior (notably changelog hook behavior).

Reviewed changes

Copilot reviewed 25 out of 26 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
uv.lock Removes mypy (and related deps) and adds ty to locked dependencies.
pyproject.toml Replaces mypy config with ty config; updates poe lint to run ty check.
docs/contributing/contributing_tldr.md Updates contributor instructions from mypy to ty.
commitizen/version_schemes.py Adjusts typing notes/ignores and casts for scheme types.
commitizen/providers/uv_provider.py Updates ignore directives for ty/ruff semantics around TOML indexing.
commitizen/providers/poetry_provider.py Renames params and updates ignore directives for TOML indexing.
commitizen/providers/cargo_provider.py Updates ignore directives for TOML indexing.
commitizen/providers/base_provider.py Updates ignore directives for TOML indexing.
commitizen/git.py Updates smart_open typing/ignores (currently incomplete for ruff rules).
commitizen/cz/customize/customize.py Adjusts ignores and formatting around template substitution/rendering (introduces a typing issue).
commitizen/cz/conventional_commits/conventional_commits.py Updates override-related ignore style for ty/ruff.
commitizen/config/toml_config.py Updates ignore directives for TOML mutation/indexing.
commitizen/config/base_config.py Updates ignore directive for path property return.
commitizen/commands/bump.py Adds ChangelogArgs typing and casts for TypedDict compatibility.
commitizen/cli.py Updates ignore directive for args.func(...) call.
commitizen/changelog.py Makes hook processing robust to non-dict entries; tweaks incremental build guard.
commitizen/changelog_formats/init.py Refactors changelog-format resolution logic for clearer typing.
scripts/gen_cli_help_screenshots.py Adds casts to satisfy static typing when iterating CLI command config.
tests/test_cz_search_filter.py Uses Path for TomlConfig.path to match type expectations.
tests/test_cz_conventional_commits.py Adds casts / imports to satisfy ty for answer dict typing.
tests/test_conf.py Adjusts read_cfg call argument type.
tests/test_cli.py Passes None traceback to match commitizen_excepthook signature.
tests/test_changelog.py Adds casts/imports and new test for skipping non-dict hook entries.
tests/commands/test_version_command.py Removes mypy-specific ignore; now relies on ty behavior.
tests/commands/test_changelog_command.py Updates ignore style for ruff PGH003.
tests/commands/test_bump_command.py Updates ignore style for ruff PGH003.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread commitizen/git.py Outdated
Comment thread commitizen/cz/customize/customize.py
Comment thread commitizen/commands/bump.py
Comment thread tests/commands/test_version_command.py Outdated
@bearomorphism
Copy link
Copy Markdown
Collaborator Author

This one is ready for review

Copy link
Copy Markdown
Member

@Lee-W Lee-W left a comment

Choose a reason for hiding this comment

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

I'm not 100% sure we want to migrate to ty now, given the limitations. but some fixed are valid though

Comment thread tests/test_cz_conventional_commits.py Outdated
Comment thread tests/test_conf.py Outdated
Comment thread commitizen/commands/bump.py Outdated
Comment thread commitizen/changelog.py
Comment thread commitizen/version_schemes.py
bearomorphism added a commit that referenced this pull request May 3, 2026
- tests/test_cz_conventional_commits.py: use type annotations instead of
  cast() for answers dicts (avoids ty limitation with dict literal inference)
- config/__init__.py: widen read_cfg to accept str | Path | None
- tests/test_conf.py: revert str() wrapper now that read_cfg accepts Path
- changelog.py: add comment explaining ty limitation requiring cast
- version_schemes.py: add comment explaining ty limitation requiring cast

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Member

@Lee-W Lee-W left a comment

Choose a reason for hiding this comment

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

I kinda feel like I'm -0 to this (possible +1 when ty's getting better). would love to hear how @woile think :)

@woile
Copy link
Copy Markdown
Member

woile commented May 3, 2026

Same boat, mypy for this project has been good enough, if ty is complete, the speedup would be welcome

bearomorphism added a commit that referenced this pull request May 3, 2026
- tests/test_cz_conventional_commits.py: use type annotations instead of
  cast() for answers dicts (avoids ty limitation with dict literal inference)
- config/__init__.py: widen read_cfg to accept str | Path | None
- tests/test_conf.py: revert str() wrapper now that read_cfg accepts Path
- changelog.py: add comment explaining ty limitation requiring cast
- version_schemes.py: add comment explaining ty limitation requiring cast

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bearomorphism bearomorphism force-pushed the replace-mypy-with-ty branch from f39e15e to cd7c463 Compare May 3, 2026 07:05
bearomorphism and others added 4 commits May 3, 2026 15:05
- Use Astral ty in linters, poe lint, and [tool.ty] instead of mypy.
- Align typings, type ignores, and tests with ty; cover changelog hook list
  entries that are not dicts for patch coverage.
- Add type annotations to smart_open in git.py
- Add type: ignore[attr-defined] for Template.substitute call
- Cast dict to ChangelogArgs in bump.py
- Cast dynamic key dict to VersionArgs in test_version_command.py

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- tests/test_cz_conventional_commits.py: use type annotations instead of
  cast() for answers dicts (avoids ty limitation with dict literal inference)
- config/__init__.py: widen read_cfg to accept str | Path | None
- tests/test_conf.py: revert str() wrapper now that read_cfg accepts Path
- changelog.py: add comment explaining ty limitation requiring cast
- version_schemes.py: add comment explaining ty limitation requiring cast

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bearomorphism bearomorphism force-pushed the replace-mypy-with-ty branch 4 times, most recently from 920e968 to a61fd94 Compare May 3, 2026 08:01
ty 0.0.33 prefers the declared type on annotated assignments when the
RHS returns Any/Unknown, removing the need for cast() workarounds.

- Bump ty minimum to >=0.0.33
- Replace cast() with type annotations where applicable

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bearomorphism bearomorphism force-pushed the replace-mypy-with-ty branch from a61fd94 to 747e04f Compare May 3, 2026 08:04
@bearomorphism
Copy link
Copy Markdown
Collaborator Author

upgraded ty and the casts are removed, only one cast left

Is it good enough? wdyt

@bearomorphism
Copy link
Copy Markdown
Collaborator Author

pr description updated

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.

4 participants