Skip to content

Commit 2ebd51e

Browse files
authored
Teach stubgen to work with complex and unary expressions (#15661)
1 parent 3983381 commit 2ebd51e

File tree

2 files changed

+89
-5
lines changed

2 files changed

+89
-5
lines changed

mypy/stubgen.py

+44-2
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
CallExpr,
8181
ClassDef,
8282
ComparisonExpr,
83+
ComplexExpr,
8384
Decorator,
8485
DictExpr,
8586
EllipsisExpr,
@@ -1396,6 +1397,8 @@ def is_private_member(self, fullname: str) -> bool:
13961397
def get_str_type_of_node(
13971398
self, rvalue: Expression, can_infer_optional: bool = False, can_be_any: bool = True
13981399
) -> str:
1400+
rvalue = self.maybe_unwrap_unary_expr(rvalue)
1401+
13991402
if isinstance(rvalue, IntExpr):
14001403
return "int"
14011404
if isinstance(rvalue, StrExpr):
@@ -1404,8 +1407,13 @@ def get_str_type_of_node(
14041407
return "bytes"
14051408
if isinstance(rvalue, FloatExpr):
14061409
return "float"
1407-
if isinstance(rvalue, UnaryExpr) and isinstance(rvalue.expr, IntExpr):
1408-
return "int"
1410+
if isinstance(rvalue, ComplexExpr): # 1j
1411+
return "complex"
1412+
if isinstance(rvalue, OpExpr) and rvalue.op in ("-", "+"): # -1j + 1
1413+
if isinstance(self.maybe_unwrap_unary_expr(rvalue.left), ComplexExpr) or isinstance(
1414+
self.maybe_unwrap_unary_expr(rvalue.right), ComplexExpr
1415+
):
1416+
return "complex"
14091417
if isinstance(rvalue, NameExpr) and rvalue.name in ("True", "False"):
14101418
return "bool"
14111419
if can_infer_optional and isinstance(rvalue, NameExpr) and rvalue.name == "None":
@@ -1417,6 +1425,40 @@ def get_str_type_of_node(
14171425
else:
14181426
return ""
14191427

1428+
def maybe_unwrap_unary_expr(self, expr: Expression) -> Expression:
1429+
"""Unwrap (possibly nested) unary expressions.
1430+
1431+
But, some unary expressions can change the type of expression.
1432+
While we want to preserve it. For example, `~True` is `int`.
1433+
So, we only allow a subset of unary expressions to be unwrapped.
1434+
"""
1435+
if not isinstance(expr, UnaryExpr):
1436+
return expr
1437+
1438+
# First, try to unwrap `[+-]+ (int|float|complex)` expr:
1439+
math_ops = ("+", "-")
1440+
if expr.op in math_ops:
1441+
while isinstance(expr, UnaryExpr):
1442+
if expr.op not in math_ops or not isinstance(
1443+
expr.expr, (IntExpr, FloatExpr, ComplexExpr, UnaryExpr)
1444+
):
1445+
break
1446+
expr = expr.expr
1447+
return expr
1448+
1449+
# Next, try `not bool` expr:
1450+
if expr.op == "not":
1451+
while isinstance(expr, UnaryExpr):
1452+
if expr.op != "not" or not isinstance(expr.expr, (NameExpr, UnaryExpr)):
1453+
break
1454+
if isinstance(expr.expr, NameExpr) and expr.expr.name not in ("True", "False"):
1455+
break
1456+
expr = expr.expr
1457+
return expr
1458+
1459+
# This is some other unary expr, we cannot do anything with it (yet?).
1460+
return expr
1461+
14201462
def print_annotation(self, t: Type) -> str:
14211463
printer = AnnotationPrinter(self)
14221464
return t.accept(printer)

test-data/unit/stubgen.test

+45-3
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,52 @@ class A:
127127

128128
def g() -> None: ...
129129

130-
[case testVariable]
131-
x = 1
130+
[case testVariables]
131+
i = 1
132+
s = 'a'
133+
f = 1.5
134+
c1 = 1j
135+
c2 = 0j + 1
136+
bl1 = True
137+
bl2 = False
138+
bts = b''
139+
[out]
140+
i: int
141+
s: str
142+
f: float
143+
c1: complex
144+
c2: complex
145+
bl1: bool
146+
bl2: bool
147+
bts: bytes
148+
149+
[case testVariablesWithUnary]
150+
i = +-1
151+
f = -1.5
152+
c1 = -1j
153+
c2 = -1j + 1
154+
bl1 = not True
155+
bl2 = not not False
156+
[out]
157+
i: int
158+
f: float
159+
c1: complex
160+
c2: complex
161+
bl1: bool
162+
bl2: bool
163+
164+
[case testVariablesWithUnaryWrong]
165+
i = not +1
166+
bl1 = -True
167+
bl2 = not -False
168+
bl3 = -(not False)
132169
[out]
133-
x: int
170+
from _typeshed import Incomplete
171+
172+
i: Incomplete
173+
bl1: Incomplete
174+
bl2: Incomplete
175+
bl3: Incomplete
134176

135177
[case testAnnotatedVariable]
136178
x: int = 1

0 commit comments

Comments
 (0)