Skip to content

Commit 51b9356

Browse files
committed
Review changes
* Added tests * Rename/refactor variables * Fixed issue with type strings e.g. "int | str"
1 parent 533202e commit 51b9356

File tree

4 files changed

+52
-27
lines changed

4 files changed

+52
-27
lines changed

mypy/fastparse.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@
3131
)
3232
from mypy.types import (
3333
Type, CallableType, AnyType, UnboundType, TupleType, TypeList, EllipsisType, CallableArgument,
34-
TypeOfAny, Instance, RawExpressionType, ProperType,
35-
UnionType, Pep604Syntax)
34+
TypeOfAny, Instance, RawExpressionType, ProperType, UnionType)
3635
from mypy import defaults
3736
from mypy import message_registry, errorcodes as codes
3837
from mypy.errors import Errors
@@ -241,8 +240,8 @@ def parse_type_comment(type_comment: str,
241240
converted = TypeConverter(errors,
242241
line=line,
243242
override_column=column,
244-
assume_str_is_unicode=assume_str_is_unicode
245-
).visit(typ.body, is_type_comment=True)
243+
assume_str_is_unicode=assume_str_is_unicode,
244+
is_type_comment=True).visit(typ.body)
246245
return ignored, converted
247246

248247

@@ -269,6 +268,8 @@ def parse_type_string(expr_string: str, expr_fallback_name: str,
269268
node.original_str_expr = expr_string
270269
node.original_str_fallback = expr_fallback_name
271270
return node
271+
elif isinstance(node, UnionType):
272+
return node
272273
else:
273274
return RawExpressionType(expr_string, expr_fallback_name, line, column)
274275
except (SyntaxError, ValueError):
@@ -1277,12 +1278,14 @@ def __init__(self,
12771278
line: int = -1,
12781279
override_column: int = -1,
12791280
assume_str_is_unicode: bool = True,
1281+
is_type_comment: bool = False,
12801282
) -> None:
12811283
self.errors = errors
12821284
self.line = line
12831285
self.override_column = override_column
12841286
self.node_stack = [] # type: List[AST]
12851287
self.assume_str_is_unicode = assume_str_is_unicode
1288+
self.is_type_comment = is_type_comment
12861289

12871290
def convert_column(self, column: int) -> int:
12881291
"""Apply column override if defined; otherwise return column.
@@ -1319,13 +1322,7 @@ def visit(self, node: ast3.expr) -> ProperType: ...
13191322
@overload
13201323
def visit(self, node: Optional[AST]) -> Optional[ProperType]: ...
13211324

1322-
@overload
1323-
def visit(self, node: ast3.expr, is_type_comment: bool) -> ProperType: ...
1324-
1325-
@overload
1326-
def visit(self, node: Optional[AST], is_type_comment: bool) -> Optional[ProperType]: ...
1327-
1328-
def visit(self, node: Optional[AST], is_type_comment: bool = False) -> Optional[ProperType]:
1325+
def visit(self, node: Optional[AST]) -> Optional[ProperType]:
13291326
"""Modified visit -- keep track of the stack of nodes"""
13301327
if node is None:
13311328
return None
@@ -1334,8 +1331,6 @@ def visit(self, node: Optional[AST], is_type_comment: bool = False) -> Optional[
13341331
method = 'visit_' + node.__class__.__name__
13351332
visitor = getattr(self, method, None)
13361333
if visitor is not None:
1337-
if visitor == self.visit_BinOp:
1338-
return visitor(node, is_type_comment)
13391334
return visitor(node)
13401335
else:
13411336
return self.invalid_type(node)
@@ -1431,7 +1426,7 @@ def _extract_argument_name(self, n: ast3.expr) -> Optional[str]:
14311426
def visit_Name(self, n: Name) -> Type:
14321427
return UnboundType(n.id, line=self.line, column=self.convert_column(n.col_offset))
14331428

1434-
def visit_BinOp(self, n: ast3.BinOp, is_type_comment: bool = False) -> Type:
1429+
def visit_BinOp(self, n: ast3.BinOp) -> Type:
14351430
if not isinstance(n.op, ast3.BitOr):
14361431
return self.invalid_type(n)
14371432

@@ -1440,7 +1435,8 @@ def visit_BinOp(self, n: ast3.BinOp, is_type_comment: bool = False) -> Type:
14401435
return UnionType([left, right],
14411436
line=self.line,
14421437
column=self.convert_column(n.col_offset),
1443-
pep604_syntax=Pep604Syntax(True, is_type_comment))
1438+
is_evaluated=(not self.is_type_comment),
1439+
uses_pep604_syntax=True)
14441440

14451441
def visit_NameConstant(self, n: NameConstant) -> Type:
14461442
if isinstance(n.value, bool):

mypy/typeanal.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -605,9 +605,8 @@ def visit_star_type(self, t: StarType) -> Type:
605605
return StarType(self.anal_type(t.type), t.line)
606606

607607
def visit_union_type(self, t: UnionType) -> Type:
608-
if (t.pep604_syntax is not None
609-
and t.pep604_syntax.uses_pep604_syntax is True
610-
and t.pep604_syntax.is_type_comment is False
608+
if (t.uses_pep604_syntax is True
609+
and t.is_evaluated is True
611610
and self.api.is_stub_file is False
612611
and self.options.python_version < (3, 10)
613612
and self.api.is_future_flag_set('annotations') is False):

mypy/types.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,23 +1719,21 @@ def serialize(self) -> JsonDict:
17191719
assert False, "Synthetic types don't serialize"
17201720

17211721

1722-
Pep604Syntax = NamedTuple('Pep604Syntax', [
1723-
('uses_pep604_syntax', bool),
1724-
('is_type_comment', bool)])
1725-
1726-
17271722
class UnionType(ProperType):
17281723
"""The union type Union[T1, ..., Tn] (at least one type argument)."""
17291724

1730-
__slots__ = ('items', 'pep604_syntax')
1725+
__slots__ = ('items', 'is_evaluated', 'uses_pep604_syntax')
17311726

17321727
def __init__(self, items: Sequence[Type], line: int = -1, column: int = -1,
1733-
pep604_syntax: Optional[Pep604Syntax] = None) -> None:
1728+
is_evaluated: bool = True, uses_pep604_syntax: bool = False) -> None:
17341729
super().__init__(line, column)
17351730
self.items = flatten_nested_unions(items)
17361731
self.can_be_true = any(item.can_be_true for item in items)
17371732
self.can_be_false = any(item.can_be_false for item in items)
1738-
self.pep604_syntax = pep604_syntax
1733+
# is_evaluated should be set to false for type comments and string literals
1734+
self.is_evaluated = is_evaluated
1735+
# uses_pep604_syntax is True if Union uses OR syntax (X | Y)
1736+
self.uses_pep604_syntax = uses_pep604_syntax
17391737

17401738
def __hash__(self) -> int:
17411739
return hash(frozenset(self.items))

test-data/unit/check-union-or-syntax.test

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ def f(x: int | str) -> int | str:
1111
reveal_type(f) # N: Revealed type is 'def (x: Union[builtins.int, builtins.str]) -> Union[builtins.int, builtins.str]'
1212
[builtins fixtures/tuple.pyi]
1313

14+
1415
[case testUnionOrSyntaxWithThreeBuiltinsTypes]
1516
# flags: --python-version 3.10
1617
def f(x: int | str | float) -> int | str | float:
1718
reveal_type(x) # N: Revealed type is 'Union[builtins.int, builtins.str, builtins.float]'
1819
z: int | str | float = 0
1920
reveal_type(z) # N: Revealed type is 'Union[builtins.int, builtins.str, builtins.float]'
2021
return x
21-
2222
reveal_type(f) # N: Revealed type is 'def (x: Union[builtins.int, builtins.str, builtins.float]) -> Union[builtins.int, builtins.str, builtins.float]'
2323

24+
2425
[case testUnionOrSyntaxWithTwoTypes]
2526
# flags: --python-version 3.10
2627
class A: pass
@@ -32,6 +33,7 @@ def f(x: A | B) -> A | B:
3233
return x
3334
reveal_type(f) # N: Revealed type is 'def (x: Union[__main__.A, __main__.B]) -> Union[__main__.A, __main__.B]'
3435

36+
3537
[case testUnionOrSyntaxWithThreeTypes]
3638
# flags: --python-version 3.10
3739
class A: pass
@@ -44,36 +46,66 @@ def f(x: A | B | C) -> A | B | C:
4446
return x
4547
reveal_type(f) # N: Revealed type is 'def (x: Union[__main__.A, __main__.B, __main__.C]) -> Union[__main__.A, __main__.B, __main__.C]'
4648

49+
4750
[case testUnionOrSyntaxWithLiteral]
4851
# flags: --python-version 3.10
4952
from typing_extensions import Literal
5053
reveal_type(Literal[4] | str) # N: Revealed type is 'Any'
5154
[builtins fixtures/tuple.pyi]
5255

56+
5357
[case testUnionOrSyntaxWithBadOperator]
5458
# flags: --python-version 3.10
5559
x: 1 + 2 # E: Invalid type comment or annotation
5660

61+
5762
[case testUnionOrSyntaxWithBadOperands]
5863
# flags: --python-version 3.10
5964
x: int | 42 # E: Invalid type: try using Literal[42] instead?
6065
y: 42 | int # E: Invalid type: try using Literal[42] instead?
6166
z: str | 42 | int # E: Invalid type: try using Literal[42] instead?
6267

68+
69+
[case testUnionOrSyntaxWithGenerics]
70+
# flags: --python-version 3.10
71+
from typing import List
72+
x: List[int | str]
73+
reveal_type(x) # N: Revealed type is 'builtins.list[Union[builtins.int, builtins.str]]'
74+
[builtins fixtures/list.pyi]
75+
76+
77+
[case testUnionOrSyntaxWithQuotedTypes]
78+
# flags: --python-version 3.10
79+
from typing import Union
80+
def f(x: 'Union[int, str, None]') -> 'Union[int, None]':
81+
reveal_type(x) # N: Revealed type is 'Union[builtins.int, builtins.str, None]'
82+
return 42
83+
reveal_type(f) # N: Revealed type is 'def (x: Union[builtins.int, builtins.str, None]) -> Union[builtins.int, None]'
84+
85+
# flags: --python-version 3.10
86+
def g(x: "int | str | None") -> "int | None":
87+
reveal_type(x) # N: Revealed type is 'Union[builtins.int, builtins.str, None]'
88+
return 42
89+
reveal_type(g) # N: Revealed type is 'def (x: Union[builtins.int, builtins.str, None]) -> Union[builtins.int, None]'
90+
91+
6392
[case testUnionOrSyntaxInComment]
6493
# flags: --python-version 3.6
6594
x = 1 # type: int | str
6695

96+
6797
[case testUnionOrSyntaxFutureImport]
6898
# flags: --python-version 3.7
6999
from __future__ import annotations
70100
x: int | None
71101
[builtins fixtures/tuple.pyi]
72102

103+
73104
[case testUnionOrSyntaxMissingFutureImport]
74105
# flags: --python-version 3.9
75106
x: int | None # E: X | Y syntax for unions requires Python 3.10
76107

108+
77109
[case testUnionOrSyntaxInStubFile]
78110
# flags: --python-version 3.6
79111
from lib import x

0 commit comments

Comments
 (0)