Skip to content

Enable Pyrefly type checking#3474

Merged
vfdev-5 merged 4 commits intopytorch:masterfrom
rchen152:pyrefly
Dec 10, 2025
Merged

Enable Pyrefly type checking#3474
vfdev-5 merged 4 commits intopytorch:masterfrom
rchen152:pyrefly

Conversation

@rchen152
Copy link
Copy Markdown
Contributor

@rchen152 rchen152 commented Dec 6, 2025

Ref #3473

Description:

Enables Pyrefly, a fast type checker. I ported the mypy.ini file to a pyrefly.toml using the pyrefly init command and added # pyrefly: ignore comments to suppress new type errors.

Since this PR already touches a lot of files due to the suppressions, I opted to keep it focused on adding Pyrefly. If it's accepted, I'll follow up with another PR to remove Mypy, update documentation and other references, and clean up easy-to-fix suppressions.

@github-actions github-actions bot added module: engine Engine module module: metrics Metrics module module: handlers Core Handlers module module: distributed Distributed module module: contrib Contrib module ci CI labels Dec 6, 2025
@rchen152 rchen152 mentioned this pull request Dec 6, 2025
Copy link
Copy Markdown
Collaborator

@vfdev-5 vfdev-5 left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @rchen152!
I was going through the ignored errors trying to understand the issues and left a number of questions (I haven't checked all of them). It would be nice to be able to reduce the number of # pyrefly: ignore lines.

uv pip install -r requirements-dev.txt
uv pip install .
uv pip install mypy
uv pip install pyrefly==0.44.2
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we install the latest (and avoid upgrading the version)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. Fair warning that we release new pyrefly versions 1-2 times a week at the moment, so not pinning the pyrefly version may lead to ignite picking up new features (that may generate new type errors) rather often.


@trainer.on(Events.EPOCH_STARTED)
def distrib_set_epoch(engine: Engine) -> None:
# pyrefly: ignore [missing-attribute]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@rchen152 do you know why there is an error here?

train_sampler is checked above to be a DistributedSampler, which has set_epoch method: https://github.com/pytorch/pytorch/blob/d38164a545b4a4e4e0cf73ce67173f70574890b6/torch/utils/data/distributed.py#L139

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This error is due to pyrefly treating the type of train_sampler as DistributedSampler | None inside the nested function, to guard against problems like this:

def f(train_sampler: DistributedSampler | None):
    if train_sampler is not None:
        def g():
            train_sampler.set_epoch(...)
        train_sampler = None
        g()  # oops! This would crash

With that said, we may be able to make this check less conservative. We have facebook/pyrefly#40 open to do some additional analysis to figure out when we can not emit this error.

# From pytorch/xla if `torch_xla.distributed.parallel_loader.MpDeviceLoader` is not available
def __init__(self, loader: Any, device: torch.device, **kwargs: Any) -> None:
self._loader = loader
# pyrefly: ignore [read-only]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@rchen152 can you please explain this error? I'm not totally sure what is read-only here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This one took me a while to figure out! It's a bug in pyrefly (facebook/pyrefly#1803) caused by treating torch.device as a descriptor.

self._init_from_context()

def _create_from_backend(self, backend: str, **kwargs: Any) -> None:
# pyrefly: ignore [bad-override]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same here, rather unclear what is bad-override here and below.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The bad-override error is caused by setting the type of _backend (and the type signature of spawn below) to a different type than in the ComputationModel base class. Pyrefly errors on this to prevent problems like this:

class ComputationModel:
    def __init__(self):
        self._backend: str | None = ...
class _HorovodDistModel(ComputationModel):
    def _create_from_backend(self):
        self._backend: str = ...

def clear_backend(model: ComputationModel):
    # This is accepted by a type checker because `_backend` in `ComputationModel` is annotated as `str | None`.
    model._backend = None

# This is accepted by a type checker because `_HorovodDistModel` is a subclass of `ComputationModel`.
# But it would lead to `_backend` being set to `None`, and any code in `_HorovodDistModel` that assumes
# `_backend` to be `str` would crash.
clear_backend(_HorovodDistModel())

try:
import numpy as np

# pyrefly: ignore [bad-argument-type]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do you know what's wrong here?

Copy link
Copy Markdown
Contributor Author

@rchen152 rchen152 Dec 10, 2025

Choose a reason for hiding this comment

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

This is down to a difference between mypy and pyrefly's type inference strategies. Mypy says that output has type List[object] because random.getstate() and torch.get_rng_state() return types with no common superclass except object, whereas Pyrefly sets the list's contained type to the union of the two types. I removed this suppression and added an explicit annotation, so that no inference is needed.

"https://github.com/pytorch/ignite/issues/new/choose"
)

# pyrefly: ignore [unbound-name]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Here, I agree with pyrefly, in a follow-up PR we can put start_time before the try/except clauses.

raise ValueError("Argument every should be integer and greater than zero")

if once is not None:
# pyrefly: ignore [unsupported-operation]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

A bit unclear what's wrong here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The text of the error message is:

`>` is not supported between `Integral` and `Literal[0]`

Interestingly, mypy emits the same error if you make it check an operation between Integral and int directly. The reason it doesn't error here seems to be that it (incorrectly) treats the code as unreachable: playground.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I see, thanks! It's true that previously we had also hard time with typing and numbers. Maybe we can safely replace the check as:

c1 = isinstance(once, int) and once > 0

@rchen152
Copy link
Copy Markdown
Contributor Author

@vfdev-5 Thanks for the review! I believe I've addressed all the comments.

Copy link
Copy Markdown
Collaborator

@vfdev-5 vfdev-5 left a comment

Choose a reason for hiding this comment

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

Thanks a lot @rchen152 for checking the comments and fixing the code!
Maybe, we can move forward in multiple PRs to reduce the number of type ignore comments.
I left few other comments, but overall looks good to me

result = (
torch.einsum("i,j->ij", kernel_x, kernel_y)
if ndims == 2
# pyrefly: ignore [unbound-name]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we get rid of the ignore if we rewrite it as?

result = (
    torch.einsum("i,j,k->ijk", kernel_x, kernel_y, kernel_z)
    if ndims == 3
    else torch.einsum("i,j->ij", kernel_x, kernel_y)
)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That didn't work, unfortunately (I think the control flow was too complex for pyrefly to follow); I added kernel_z = None branches to make sure kernel_z is always defined instead.

raise ValueError("Argument every should be integer and greater than zero")

if once is not None:
# pyrefly: ignore [unsupported-operation]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I see, thanks! It's true that previously we had also hard time with typing and numbers. Maybe we can safely replace the check as:

c1 = isinstance(once, int) and once > 0

Copy link
Copy Markdown
Collaborator

@vfdev-5 vfdev-5 left a comment

Choose a reason for hiding this comment

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

Thanks @rchen152 !

@vfdev-5 vfdev-5 merged commit 96f9958 into pytorch:master Dec 10, 2025
21 of 24 checks passed
@rchen152 rchen152 deleted the pyrefly branch December 10, 2025 23:42
vfdev-5 pushed a commit that referenced this pull request Dec 16, 2025
Fixes #3473

This is a follow-up to #3474,
which added the pyrefly type checker. This PR removes mypy and cleans up
some ignore comments.
* Removes mypy from CI, deletes `mypy.ini`, and switches documentation
over to reference pyrefly.
* Removes 19 `# type: ignore` comments that mypy used and pyrefly
doesn't.
* Refactors the code a bit and tweaks some type annotations, allowing us
to remove 24 `# pyrefly: ignore` comments.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci CI module: contrib Contrib module module: distributed Distributed module module: engine Engine module module: handlers Core Handlers module module: metrics Metrics module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants