8000 Teach `stubgen` to work with `complex` and unary expressions by sobolevn · Pull Request #15661 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Teach stubgen to work with complex and unary expressions #15661

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

Merged
merged 3 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

8000
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Only allow some unary expressions
  • Loading branch information
sobolevn committed Jul 13, 2023
commit ec71ebe662a0ce87a8b29933e6949f79a83d1080
42 changes: 35 additions & 7 deletions mypy/stubgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -1397,7 +1397,7 @@ def is_private_member(self, fullname: str) -> bool:
def get_str_type_of_node(
self, rvalue: Expression, can_infer_optional: bool = False, can_be_any: bool = True
) -> str:
rvalue = self.unwrap_unary_expr(rvalue)
rvalue = self.maybe_unwrap_unary_expr(rvalue)

if isinstance(rvalue, IntExpr):
return "int"
Expand All @@ -1410,8 +1410,8 @@ def get_str_type_of_node(
if isinstance(rvalue, ComplexExpr): # 1j
return "complex"
if isinstance(rvalue, OpExpr) and rvalue.op in ("-", "+"): # -1j + 1
if isinstance(self.unwrap_unary_expr(rvalue.left), ComplexExpr) or isinstance(
self.unwrap_unary_expr(rvalue.right), ComplexExpr
if isinstance(self.maybe_unwrap_unary_expr(rvalue.left), ComplexExpr) or isinstance(
self.maybe_unwrap_unary_expr(rvalue.right), ComplexExpr
):
return "complex"
if isinstance(rvalue, NameExpr) and rvalue.name in ("True", "False"):
Expand All @@ -1425,10 +1425,38 @@ def get_str_type_of_node(
else:
return ""

def unwrap_unary_expr(self, expr: Expression) -> Expression:
# Unwrap (possibly nested) unary expressions:
while isinstance(expr, UnaryExpr):
expr = expr.expr
def maybe_unwrap_unary_expr(self, expr: Expression) -> Expression:
"""Unwrap (possibly nested) unary expressions.

But, some unary expressions can change the type of expression.
While we want to preserve it. For example, `~True` is `int`.
So, we only allow a subset of unary expressions to be unwrapped.
"""
if not isinstance(expr, UnaryExpr):
return expr

# First, try to unwrap `[+-]+ (int|float|complex)` expr:
math_ops = ("+", "-")
if expr.op in math_ops:
while isinstance(expr, UnaryExpr):
if expr.op not in math_ops or not isinstance(
expr.expr, (IntExpr, FloatExpr, ComplexExpr, UnaryExpr)
):
break
expr = expr.expr
return expr

# Next, try `not bool` expr:
if expr.op == "not":
while isinstance(expr, UnaryExpr):
if expr.op != "not" or not isinstance(expr.expr, (NameExpr, UnaryExpr)):
break
if isinstance(expr.expr, NameExpr) and expr.expr.name not in ("True", "False"):
break
expr = expr.expr
return expr

# This is some other unary expr, we cannot do anything with it (yet?).
return expr

def print_annotation(self, t: Type) -> str:
Expand Down
17 changes: 15 additions & 2 deletions test-data/unit/stubgen.test
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,12 @@ bl2: bool
bts: bytes

[case testVariablesWithUnary]
i = ++1
i = +-1
f = -1.5
c1 = -1j
c2 = -1j + 1
bl1 = not True
bl2 = not False
bl2 = not not False
[out]
i: int
f: float
Expand All @@ -161,6 +161,19 @@ c2: complex
bl1: bool
bl2: bool

[case testVariablesWithUnaryWrong]
i = not +1
bl1 = -True
bl2 = not -False
bl3 = -(not False)
[out]
from _typeshed import Incomplete

i: Incomplete
bl1: Incomplete
bl2: Incomplete
bl3: Incomplete

[case testAnnotatedVariable]
x: int = 1
[out]
Expand Down
0