Skip to content

1.16 regression: --warn-unused-ignore now complains in ignored-but-correct places #19237

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
asottile-sentry opened this issue Jun 4, 2025 · 11 comments
Labels
bug mypy got something wrong pending Issues that may be closed topic-error-reporting How we report errors topic-type-ignore # type: ignore comments topic-usability

Comments

@asottile-sentry
Copy link

this appears to have changed behaviour in mypy 1.15 -- I personally prefer the behaviour in 1.15 but would understand if it needed to change. I don't see it mentioned in the changelog however

Bug Report

we're working on incrementally typing a large codebase -- often this means fixing a particular callsite or file at a time (sometimes by intentionally ignoring errors unfortunately)

it seems that in a module with a particular error code (temporarily) disabled, --warn-unused-ignores will complain about a "correct" type ignore (I say "correct" here as it will complain when the file is no longer blocklisted) -- will provide a small example below

To Reproduce

# t.py
def f() -> None:
    x: int = 'hi'  # type: ignore[assignment]
[mypy]
warn_unused_ignores = true
[mypy-t]
disable_error_code = assignment

Expected Behavior

$ mypy --version
mypy 1.15.0 (compiled: yes)
$ mypy t.py
Success: no issues found in 1 source file

Actual Behavior

$ mypy --version
mypy 1.16.0 (compiled: yes)
$ mypy t.py
t.py:2: error: Unused "type: ignore" comment  [unused-ignore]
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.16.0 (regression from 1.15.0)
  • Mypy command-line flags: (see above)
  • Mypy configuration options from mypy.ini (and other config files): (see above)
  • Python version used: 3.13.1
@asottile-sentry asottile-sentry added the bug mypy got something wrong label Jun 4, 2025
@asottile-sentry
Copy link
Author

this seems intentional via #11059 and #18849 (bisected to 6b68661)

I'd still argue this is worse than before for my use case and I'm not quite sure what the motivation was in the original issue -- cc @Akuli @brianschubert

@asottile-sentry
Copy link
Author

actually I do see it in the notes now -- I was searching for the wrong thing (warn-unused-ignore)

@brianschubert
Copy link
Collaborator

Yeah, this was an intentional change. The ignore is unused in the sense it isn't necessary under the current configuration and removing it would not result in a diagnostic being emitted. This is generally a useful behavior, since it lets you detect # type: ignore comments that become no longer relevant after a change in configuration.

As a workaround, you can try disabling the unused-ignore error code on a per-file or line-by-line basis. For example, mypy will accept the following regardless of whether the assignment error code is enabled or not:

def f() -> None:
    x: int = 'hi'  # type: ignore[assignment,unused-ignore]

I'll note that the previous behavior was buggy in that only some errors could be both ignored and disabled without setting off --warn-unused-ignores. This has to do with how disabled error codes are internally handled in mypy, where in some cases mypy will check whether an error code is enabled before doing some analysis, while in other cases mypy will unconditionally do some analysis and then latter suppress errors if the associated error code is disabled. Previously, checks of the former type would set off --warn-unused-ignores when the error code was disabled while checks of the latter type would not. Now, both behave in the same way.

Also FWIW, the current behavior is consistent with how other static analysis tools I've used work. For example, ruff's RUF100 rule handles unused # noqa comments in much the same way. In ruff, if you silence an error with # noqa and later disable the rule that was associated with that error, you'll get a new error suggesting to remove the now defunct # noqa comment:

ruff unused noqa behavior
$ cat t1.py
def f() -> None:
    x: int = 'hi'

$ ruff check --select=F,RUF t1.py
t1.py:2:5: F841 Local variable `x` is assigned to but never used
  |
1 | def f() -> None:
2 |     x: int = 'hi'
  |     ^ F841
  |
  = help: Remove assignment to unused variable `x`

# Add '# noqa' comment
$ cat t2.py
def f() -> None:
    x: int = 'hi'  # noqa: F841

# Linting now passes
$ ruff check --select=F,RUF t2.py
All checks passed!

# Now disable the rule with '--ignore=F841'
# Ruff now warns you about the unused '# noqa' comment
$ ruff check --select=F,RUF --ignore=F841 t2.py
t2.py:2:20: RUF100 [*] Unused `noqa` directive (non-enabled: `F841`)
  |
1 | def f() -> None:
2 |     x: int = 'hi'  # noqa: F841
  |                    ^^^^^^^^^^^^ RUF100
  |
  = help: Remove unused `noqa` directive

Found 1 error.
[*] 1 fixable with the `--fix` option.

@brianschubert brianschubert added topic-usability topic-type-ignore # type: ignore comments topic-error-reporting How we report errors pending Issues that may be closed labels Jun 4, 2025
@asottile-sentry
Copy link
Author

tagging with unused-ignore doesn't really help though -- since now it could live there ~forever even if the actual thing gets fixed

I don't think ruff's behaviour is particularly relevant -- incremental application isn't a common approach there.

@wyattscarpenter
Copy link
Contributor

@asottile-sentry how does adding warn_unused_ignores = false to [mypy-t] along with the disable_error_code strike you as a solution (to your particular problem)?

@asottile-sentry
Copy link
Author

again that defeats the whole point for me

@wyattscarpenter
Copy link
Contributor

But then the type-ignores won't live forever, only until you remove the pair of configurations from your configuration file.

@asottile-sentry
Copy link
Author

it won't but it lets other completely wrong type ignores creep in / stick around

@wyattscarpenter
Copy link
Contributor

I see, that's true.

@wyattscarpenter
Copy link
Contributor

Besides the obvious solutions (the one I just mentioned, the one brianschubert mentioned, changing the behavior of warn-unused-ignores back, introducing a new configuration warn_unused_ignores_except, & just removing the type ignores), which all have their drawbacks, you could also check out basedmypy's baselining feature https://kotlinisland.github.io/basedmypy/baseline. Basedmypy is a fork of mypy and has slightly different behavior sometimes (and different things enabled by default), but there's a slight chance that it's exactly what you need.

@asottile-sentry
Copy link
Author

yeah I'm never using anything by that author or with "based" in the name so that's not really a fix to mypy's behaviour change

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong pending Issues that may be closed topic-error-reporting How we report errors topic-type-ignore # type: ignore comments topic-usability
Projects
None yet
Development

No branches or pull requests

3 participants