Skip to content

Implement support for "mypy: ignore" comments #17875

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
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
652044c
hack to support "mypy: ignore" comments until the in-built compile fu…
wyattscarpenter Oct 4, 2024
af86125
Use re.sub instead of str.replace, so as to match type ignore behavio…
wyattscarpenter Oct 4, 2024
7831c70
Update fastparse.py: exclude start of line
wyattscarpenter Oct 4, 2024
44242fb
make the re.sub take str or bytes, like the function signature
wyattscarpenter Oct 4, 2024
d697114
use a (?![-_]) to further ignore inline-configs
wyattscarpenter Oct 4, 2024
573125e
use python's tokenize, in order to limit the replacement to only comm…
wyattscarpenter Oct 5, 2024
d3ebf5d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 5, 2024
9d02634
appease the typechecker
wyattscarpenter Oct 5, 2024
8868c63
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 5, 2024
6feee3c
appease shadow rule I guess
wyattscarpenter Oct 5, 2024
6bcdd3f
I guess it's always a string in the comprehension, actually
wyattscarpenter Oct 5, 2024
38d7cbc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 5, 2024
ef13623
implement workaround for \n{{ roundtripping bug
wyattscarpenter Oct 7, 2024
62f034d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 7, 2024
392a37c
refix {{ roundtrip, fix hasattr static check
wyattscarpenter Oct 7, 2024
0bb0886
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 7, 2024
69b0ce7
fix whitespace roundtripping?
wyattscarpenter Oct 7, 2024
8d07bb6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 7, 2024
f319d54
Merge branch 'python:master' into mypy-ignore
wyattscarpenter Oct 7, 2024
6a9b777
Merge branch 'master' into mypy-ignore
wyattscarpenter Jan 13, 2025
23afc30
oh right; I do need io
wyattscarpenter Jan 13, 2025
c0e0171
try adding one test case
wyattscarpenter Jan 13, 2025
069acb9
comments and documentation
wyattscarpenter Jan 13, 2025
461d6e9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 13, 2025
f4990be
fix undefined label: 'inline-configuration' [ref.ref]
wyattscarpenter Jan 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ may necessitate changing the location of a `# type: ignore` comment.
Contributed by Shantanu Jain (PR [18392](https://github.com/python/mypy/pull/18392),
PR [18397](https://github.com/python/mypy/pull/18397)).

### `mypy: ignore`

`mypy: ignore` comments are now honored by mypy exactly as though they said `type: ignore`.
This allows one to suppress type errors from mypy in particular, without suppressing other type checkers,
which can be desirable if mypy has a bug and other type checkers one runs on the same code do not.

Contributed by Wyatt S Carpenter (PR [17875](https://github.com/python/mypy/pull/17875)).

## Mypy 1.14

We’ve just uploaded mypy 1.14 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).
Expand Down
26 changes: 18 additions & 8 deletions docs/source/common_issues.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,21 +148,31 @@ error:
The second line is now fine, since the ignore comment causes the name
``frobnicate`` to get an implicit ``Any`` type.

.. note::

You can use the form ``# type: ignore[<code>]`` to only ignore
specific errors on the line. This way you are less likely to
silence unexpected errors that are not safe to ignore, and this
will also document what the purpose of the comment is. See
:ref:`error-codes` for more information.

.. note::

The ``# type: ignore`` comment will only assign the implicit ``Any``
type if mypy cannot find information about that particular module. So,
if we did have a stub available for ``frobnicate`` then mypy would
ignore the ``# type: ignore`` comment and typecheck the stub as usual.

You can use the form ``# type: ignore[<code>]`` to only ignore
specific errors on the line. This way you are less likely to
silence unexpected errors that are not safe to ignore, and this
will also document what the purpose of the comment is. See
:ref:`error-codes` for more information.

Instead of ``# type: ignore``, you may also use the mypy-specific
``# mypy: ignore``, to tell only mypy — and not other type checkers — to
ignore that line. This is mostly useful if mypy has a bug that produces
known-spurious error messages. You may also use the ``# mypy: ignore[<code>]``
form to only ignore specific errors. Indeed, it is often wisest to use
``# mypy: ignore[<code>]``, as this minimizes your chances of accidentally
suppressing other errors that you don't really want to ignore!

(``# mypy: ignore`` comments are per-line and should not be confused with
:ref:`inline-config` comments, which are per-file configuration directives
that just happen to be formatted a similar way.)

Another option is to explicitly annotate values with type ``Any`` --
mypy will let you perform arbitrary operations on ``Any``
values. Sometimes there is no more precise type you can use for a
Expand Down
58 changes: 52 additions & 6 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from __future__ import annotations

import io
import re
import sys
import tokenize
import warnings
from collections.abc import Sequence
from typing import Any, Callable, Final, Literal, Optional, TypeVar, Union, cast, overload
Expand Down Expand Up @@ -137,13 +139,57 @@
def ast3_parse(
source: str | bytes, filename: str, mode: str, feature_version: int = PY_MINOR_VERSION
) -> AST:
return ast3.parse(
source,
filename,
mode,
type_comments=True, # This works the magic
feature_version=feature_version,
"""This function is just a convenience wrapper around ast.parse, with default flags useful to Mypy.
It also handles `# mypy: ignore` comments, by turning them into `# type: ignore` comments.
"""
# Hack to support "mypy: ignore" comments until the builtin compile function changes to allow us to detect it otherwise:
# (Note: completely distinct from https://mypy.readthedocs.io/en/stable/inline_config.html ; see also, util.get_mypy_comments in this codebase.)

# We make the substitution in comments, and to find those comments we use Python's `tokenize`.
# https://docs.python.org/3/library/tokenize.html has a big red **Warning:**
# Note that the functions in this module are only designed to parse syntactically valid Python
# code (code that does not raise when parsed using ast.parse()). The behavior of the functions
# in this module is **undefined** when providing invalid Python code and it can change at any point.
# So, we cannot rely on roundtrip behavior in tokenize iff ast.parse would throw when given `source`.
# The simplest way to deal with that is just to call ast.parse twice, once before and once after!
def p() -> AST:
return ast3.parse(
source, filename, mode, type_comments=True, feature_version=feature_version
)

p() # Call to assure syntactic validity (will throw an exception otherwise, exiting this function).
if isinstance(source, str):
tokens = tokenize.generate_tokens(io.StringIO(source).readline)
else:
tokens = tokenize.tokenize(io.BytesIO(source).readline)
# We do a workaround for a roundtripping error (https://github.com/python/cpython/issues/125008)
# that was introduced in 3.12.3 (https://github.com/python/cpython/blob/v3.12.3/Lib/tokenize.py#L205)
# and fixed in 3.12.8 (https://github.com/python/cpython/blob/v3.12.8/Lib/tokenize.py#L205).
# Luckily, it was caught before the first official (non-rc) release of python 3.13.
is_defective_version = (3, 12, 3) <= sys.version_info[:3] <= (3, 12, 7)
# This is a gnarly list comprehension, but that's basically just how untokenize is supposed to work.
source = tokenize.untokenize(
(
t,
(
re.sub(r"#\s*mypy:\s*ignore(?![-_])", "# type: ignore", s)
if t == tokenize.COMMENT
else (
s[:-1]
if is_defective_version
# Technically redundant hasattr check as all the defective versions are versions that have this attribute, but we'd like to appease the typechecker here:
and hasattr(tokenize, "FSTRING_MIDDLE")
and t == tokenize.FSTRING_MIDDLE
and s.startswith("\\")
and s.endswith("{")
else s
)
),
*_, # Including this bit improves whitespace roundtripping (possibly always making it perfect).
)
for t, s, *_ in tokens
)
return p() # `source` has changed, so this result is different than the first call.


NamedExpr = ast3.NamedExpr
Expand Down
5 changes: 5 additions & 0 deletions test-data/unit/check-ignore.test
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ x = 1
x() # type: ignore
x() # E: "int" not callable

[case testMypyIgnoreTypeError]
x = 1
x() # mypy: ignore
x() # E: "int" not callable

[case testIgnoreUndefinedName]
x = 1
y # type: ignore
Expand Down
Loading