Skip to content

Commit ba75620

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

File tree

4 files changed

+53
-27
lines changed

4 files changed

+53
-27
lines changed

mypy/fastparse.py

+12-15
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
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,
35+
)
3636
from mypy import defaults
3737
from mypy import message_registry, errorcodes as codes
3838
from mypy.errors import Errors
@@ -241,8 +241,8 @@ def parse_type_comment(type_comment: str,
241241
converted = TypeConverter(errors,
242242
line=line,
243243
override_column=column,
244-
assume_str_is_unicode=assume_str_is_unicode
245-
).visit(typ.body, is_type_comment=True)
244+
assume_str_is_unicode=assume_str_is_unicode,
245+
is_type_comment=True).visit(typ.body)
246246
return ignored, converted
247247

248248

@@ -269,6 +269,8 @@ def parse_type_string(expr_string: str, expr_fallback_name: str,
269269
node.original_str_expr = expr_string
270270
node.original_str_fallback = expr_fallback_name
271271
return node
272+
elif isinstance(node, UnionType):
273+
return node
272274
else:
273275
return RawExpressionType(expr_string, expr_fallback_name, line, column)
274276
except (SyntaxError, ValueError):
@@ -1277,12 +1279,14 @@ def __init__(self,
12771279
line: int = -1,
12781280
override_column: int = -1,
12791281
assume_str_is_unicode: bool = True,
1282+
is_type_comment: bool = False,
12801283
) -> None:
12811284
self.errors = errors
12821285
self.line = line
12831286
self.override_column = override_column
12841287
self.node_stack = [] # type: List[AST]
12851288
self.assume_str_is_unicode = assume_str_is_unicode
1289+
self.is_type_comment = is_type_comment
12861290

12871291
def convert_column(self, column: int) -> int:
12881292
"""Apply column override if defined; otherwise return column.
@@ -1319,13 +1323,7 @@ def visit(self, node: ast3.expr) -> ProperType: ...
13191323
@overload
13201324
def visit(self, node: Optional[AST]) -> Optional[ProperType]: ...
13211325

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]:
1326+
def visit(self, node: Optional[AST]) -> Optional[ProperType]:
13291327
"""Modified visit -- keep track of the stack of nodes"""
13301328
if node is None:
13311329
return None
@@ -1334,8 +1332,6 @@ def visit(self, node: Optional[AST], is_type_comment: bool = False) -> Optional[
13341332
method = 'visit_' + node.__class__.__name__
13351333
visitor = getattr(self, method, None)
13361334
if visitor is not None:
1337-
if visitor == self.visit_BinOp:
1338-
return visitor(node, is_type_comment)
13391335
return visitor(node)
13401336
else:
13411337
return self.invalid_type(node)
@@ -1431,7 +1427,7 @@ def _extract_argument_name(self, n: ast3.expr) -> Optional[str]:
14311427
def visit_Name(self, n: Name) -> Type:
14321428
return UnboundType(n.id, line=self.line, column=self.convert_column(n.col_offset))
14331429

1434-
def visit_BinOp(self, n: ast3.BinOp, is_type_comment: bool = False) -> Type:
1430+
def visit_BinOp(self, n: ast3.BinOp) -> Type:
14351431
if not isinstance(n.op, ast3.BitOr):
14361432
return self.invalid_type(n)
14371433

@@ -1440,7 +1436,8 @@ def visit_BinOp(self, n: ast3.BinOp, is_type_comment: bool = False) -> Type:
14401436
return UnionType([left, right],
14411437
line=self.line,
14421438
column=self.convert_column(n.col_offset),
1443-
pep604_syntax=Pep604Syntax(True, is_type_comment))
1439+
is_evaluated=(not self.is_type_comment),
1440+
uses_pep604_syntax=True)
14441441

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

mypy/typeanal.py

+2-3
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

+6-8
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

+33-1
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)