Skip to content

Type error randomly raised when using a decorator and a type: ignore comment #18125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Viicos opened this issue Nov 7, 2024 · 3 comments
Open
Labels
bug mypy got something wrong

Comments

@Viicos
Copy link
Contributor

Viicos commented Nov 7, 2024

To Reproduce

  1. Create a new environment with pydantic==2.9.2 installed.
  2. Create a file t.py:
from typing import Any

from typing_extensions import Self

from pydantic import BaseModel, model_validator
from pydantic.functional_validators import ModelWrapValidatorHandler


class WrapModelValidator(BaseModel):
    @model_validator(mode='wrap')  # type: ignore[arg-type]  # pyright: ignore[reportArgumentType]
    def no_classmethod(cls, value: Any, handler: ModelWrapValidatorHandler[Self]) -> Self: ...
  1. Run mypy --cache-dir=/dev/null --python-version 3.10 --disable-error-code empty-body --warn-unused-ignores t.py

Mypy will randomly raise:

t.py:10: error: Unused "type: ignore" comment  [unused-ignore]

And sometimes suceed.

Sorry I couldn't provide more details, the issue is really weird and it seems like mypy doesn't produce fully reproducible results in this case.

Your Environment

  • Mypy version used: 1.13.0
  • Mypy command-line flags: --cache-dir=/dev/null --python-version 3.10 --disable-error-code empty-body --warn-unused-ignores
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used: 3.12
@brianschubert
Copy link
Collaborator

Thanks! I can reproduce this locally. This is certainly a weird one!

So far I've narrowed this down to some non-deterministic behavior of is_subtype (specifically during this invocation).

What's quite interesting is that the nondeterministic behavior isn't just between mypy runs, but also between invocations of is_subtype with the same arguments during the same run (here's a video demo in my debugger).

@sterliakov
Copy link
Collaborator

Okay, this is really indeterminate. mypy.solve.solve_one is order-sensitive and iterates over sets. The following patch (not a fix, but only a proof) makes mypy output on this snippet completely reproducible.

diff --git a/mypy/solve.py b/mypy/solve.py
index 57988790a..a6d9cfcd7 100644
--- a/mypy/solve.py
+++ b/mypy/solve.py
@@ -214,7 +214,7 @@ def solve_iteratively(
             break
         # Solve each solvable type variable separately.
         s_batch.remove(solvable_tv)
-        result = solve_one(lowers[solvable_tv], uppers[solvable_tv])
+        result = solve_one(sorted(lowers[solvable_tv], key=str), sorted(uppers[solvable_tv], key=str))
         solutions[solvable_tv] = result
         if result is None:
             # TODO: support backtracking lower/upper bound choices and order within SCCs.

The problematic case is solving the following set of constraints:

[1677 :> type[Self`0], 1677 :> Self`0, 1677 <: Self`0]

for Self`1667 where Self`0 has default=Any, upper_bound=p.WrapModelValidator. If the iteration starts with Self`0, we solve 1677 to Any, otherwise we conclude that deps can't be satisfied (None).

This problem stems from non-commutative join and meet implementation.

@hauntsaninja
Copy link
Collaborator

Ah I ran into that in #16979 (comment) as well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

4 participants