Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,26 @@ necessary.
Contributed by Marc Mueller (PR [20410](https://github.com/python/mypy/pull/20410))
and (PR [20405](https://github.com/python/mypy/pull/20405)).

### Added optional error code `type-comment`

A new disabled by default error code `type-comment` was added. If enabled with
`--enable-error-code type-comment`, mypy will generate errors if legacy type comments instead of
type annotations are used.

```py
a = 2 # type: int
a: int = 2

def func(a, b):
# type: (int, str) -> bool
...

def func(a: int, b: str) -> bool:
...
```

Contributed by Marc Mueller (PR [20616](https://github.com/python/mypy/pull/20616)).

## Mypy 1.19

We’ve just uploaded mypy 1.19.0 to the Python Package Index ([PyPI](https://pypi.org/project/mypy/)).
Expand Down
26 changes: 26 additions & 0 deletions docs/source/error_code_list2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -699,3 +699,29 @@ Example:
@printing_decorator # E: Untyped decorator makes function "add_forty_two" untyped [untyped-decorator]
def add_forty_two(value: int) -> int:
return value + 42

.. _code-type-comment:

Check that no legacy type comments are used [type-comment]
----------------------------------------------------------

If enabled with :option:`--enable-error-code type-comment <mypy --enable-error-code>`,
mypy generates an error if legacy type comments are used. Tools like
[com2ann](https://github.com/ilevkivskyi/com2ann) can help with translating type comments to
type annotations.

More information about type comments are available in the
[typing specification](https://typing.python.org/en/latest/spec/historical.html#type-comments).

Example:

.. code-block:: python

o = 2 # type: int

for x, y in points: # type: float, float
...

def func(a, b):
# type: (str, int) -> bool
...
6 changes: 6 additions & 0 deletions mypy/errorcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,12 @@ def __hash__(self) -> int:
"Error when a string is used where a TypeForm is expected but a string annotation cannot be recognized",
"General",
)
TYPE_COMMENT: Final = ErrorCode(
"type-comment",
"Error when legacy type comments are used instead of type annotations",
"General",
default_enabled=False,
)

# Syntax errors are often blocking.
SYNTAX: Final = ErrorCode("syntax", "Report syntax errors", "General")
Expand Down
46 changes: 45 additions & 1 deletion mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,9 @@ def do_func_def(
arg_types = [None] * len(args)
return_type = None
elif n.type_comment is not None:
self.fail(
message_registry.TYPE_COMMENT_SOFT_DEPRECATED, lineno, n.col_offset, blocker=False
)
try:
func_type_ast = ast3_parse(n.type_comment, "<func_type>", "func_type")
assert isinstance(func_type_ast, FunctionType)
Expand Down Expand Up @@ -1138,7 +1141,13 @@ def make_argument(
arg_type = None
if annotation is not None:
arg_type = TypeConverter(self.errors, line=arg.lineno).visit(annotation)
else:
elif type_comment is not None:
self.fail(
message_registry.TYPE_COMMENT_SOFT_DEPRECATED,
arg.lineno,
arg.col_offset,
blocker=False,
)
arg_type = self.translate_type_comment(arg, type_comment)
if argument_elide_name(arg.arg):
pos_only = True
Expand Down Expand Up @@ -1266,6 +1275,13 @@ def visit_Delete(self, n: ast3.Delete) -> DelStmt:
def visit_Assign(self, n: ast3.Assign) -> AssignmentStmt:
lvalues = self.translate_expr_list(n.targets)
rvalue = self.visit(n.value)
if n.type_comment is not None:
self.fail(
message_registry.TYPE_COMMENT_SOFT_DEPRECATED,
n.lineno,
n.col_offset,
blocker=False,
)
typ = self.translate_type_comment(n, n.type_comment)
s = AssignmentStmt(lvalues, rvalue, type=typ, new_syntax=False)
return self.set_line(s, n)
Expand Down Expand Up @@ -1293,6 +1309,13 @@ def visit_AugAssign(self, n: ast3.AugAssign) -> OperatorAssignmentStmt:

# For(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
def visit_For(self, n: ast3.For) -> ForStmt:
if n.type_comment is not None:
self.fail(
message_registry.TYPE_COMMENT_SOFT_DEPRECATED,
n.lineno,
n.col_offset,
blocker=False,
)
target_type = self.translate_type_comment(n, n.type_comment)
node = ForStmt(
self.visit(n.target),
Expand All @@ -1305,6 +1328,13 @@ def visit_For(self, n: ast3.For) -> ForStmt:

# AsyncFor(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
def visit_AsyncFor(self, n: ast3.AsyncFor) -> ForStmt:
if n.type_comment is not None:
self.fail(
message_registry.TYPE_COMMENT_SOFT_DEPRECATED,
n.lineno,
n.col_offset,
blocker=False,
)
target_type = self.translate_type_comment(n, n.type_comment)
node = ForStmt(
self.visit(n.target),
Expand Down Expand Up @@ -1332,6 +1362,13 @@ def visit_If(self, n: ast3.If) -> IfStmt:

# With(withitem* items, stmt* body, string? type_comment)
def visit_With(self, n: ast3.With) -> WithStmt:
if n.type_comment is not None:
self.fail(
message_registry.TYPE_COMMENT_SOFT_DEPRECATED,
n.lineno,
n.col_offset,
blocker=False,
)
target_type = self.translate_type_comment(n, n.type_comment)
node = WithStmt(
[self.visit(i.context_expr) for i in n.items],
Expand All @@ -1343,6 +1380,13 @@ def visit_With(self, n: ast3.With) -> WithStmt:

# AsyncWith(withitem* items, stmt* body, string? type_comment)
def visit_AsyncWith(self, n: ast3.AsyncWith) -> WithStmt:
if n.type_comment is not None:
self.fail(
message_registry.TYPE_COMMENT_SOFT_DEPRECATED,
n.lineno,
n.col_offset,
blocker=False,
)
target_type = self.translate_type_comment(n, n.type_comment)
s = WithStmt(
[self.visit(i.context_expr) for i in n.items],
Expand Down
4 changes: 4 additions & 0 deletions mypy/message_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,10 @@ def with_additional_msg(self, info: str) -> ErrorMessage:
TYPE_COMMENT_SYNTAX_ERROR_VALUE: Final = ErrorMessage(
'Syntax error in type comment "{}"', codes.SYNTAX
)
TYPE_COMMENT_SOFT_DEPRECATED: Final = ErrorMessage(
"Using type comments with mypy is (soft-)deprecated, use inline annotations instead",
codes.TYPE_COMMENT,
)
ELLIPSIS_WITH_OTHER_TYPEPARAMS: Final = ErrorMessage(
"Ellipses cannot accompany other parameter types in function type signature", codes.SYNTAX
)
Expand Down
25 changes: 25 additions & 0 deletions test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -1335,3 +1335,28 @@ def process(response1: int,response2: int) -> int: # E: Overloaded function sign

def process(response1,response2)-> Union[float,int]:
return response1 + response2

[case testLegacyTypeComments]
# flags: --enable-error-code type-comment
def f(): ...
async def g(): ...

a = 2 # type: int # E: Using type comments with mypy is (soft-)deprecated, use inline annotations instead [type-comment]

for b in (): # type: int # E: Using type comments with mypy is (soft-)deprecated, use inline annotations instead [type-comment]
...

with f() as foo: # type: int # E: Using type comments with mypy is (soft-)deprecated, use inline annotations instead [type-comment]
...

def func(d, e): # E: Using type comments with mypy is (soft-)deprecated, use inline annotations instead [type-comment]
# type: (str, int) -> bool
...

async def func2(): # E: Using type comments with mypy is (soft-)deprecated, use inline annotations instead [type-comment]
# type: () -> None

async for c in g(): # type: int # E: Using type comments with mypy is (soft-)deprecated, use inline annotations instead [type-comment]
...

[builtins fixtures/tuple.pyi]