Skip to content

Commit f9e8e0b

Browse files
Allow unary + in Literal (#16729)
Implements python/typing#1550 Fixes #16727 (a trivial bug I found while implementing this feature)
1 parent d0d5876 commit f9e8e0b

File tree

3 files changed

+21
-10
lines changed

3 files changed

+21
-10
lines changed

mypy/fastparse.py

+11-5
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@
126126
import ast as ast3
127127

128128
# TODO: Index, ExtSlice are deprecated in 3.9.
129-
from ast import AST, Attribute, Call, FunctionType, Index, Name, Starred, UnaryOp, USub
129+
from ast import AST, Attribute, Call, FunctionType, Index, Name, Starred, UAdd, UnaryOp, USub
130130

131131

132132
def ast3_parse(
@@ -1940,13 +1940,19 @@ def visit_Constant(self, n: Constant) -> Type:
19401940

19411941
# UnaryOp(op, operand)
19421942
def visit_UnaryOp(self, n: UnaryOp) -> Type:
1943-
# We support specifically Literal[-4] and nothing else.
1944-
# For example, Literal[+4] or Literal[~6] is not supported.
1943+
# We support specifically Literal[-4], Literal[+4], and nothing else.
1944+
# For example, Literal[~6] or Literal[not False] is not supported.
19451945
typ = self.visit(n.operand)
1946-
if isinstance(typ, RawExpressionType) and isinstance(n.op, USub):
1947-
if isinstance(typ.literal_value, int):
1946+
if (
1947+
isinstance(typ, RawExpressionType)
1948+
# Use type() because we do not want to allow bools.
1949+
and type(typ.literal_value) is int # noqa: E721
1950+
):
1951+
if isinstance(n.op, USub):
19481952
typ.literal_value *= -1
19491953
return typ
1954+
if isinstance(n.op, UAdd):
1955+
return typ
19501956
return self.invalid_type(n)
19511957

19521958
def numeric_type(self, value: object, n: AST) -> Type:

test-data/unit/check-literal.test

+8-3
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,6 @@ from typing_extensions import Literal
591591
def dummy() -> int: return 3
592592
a: Literal[3 + 4] # E: Invalid type: Literal[...] cannot contain arbitrary expressions
593593
b: Literal[" foo ".trim()] # E: Invalid type: Literal[...] cannot contain arbitrary expressions
594-
c: Literal[+42] # E: Invalid type: Literal[...] cannot contain arbitrary expressions
595594
d: Literal[~12] # E: Invalid type: Literal[...] cannot contain arbitrary expressions
596595
e: Literal[dummy()] # E: Invalid type: Literal[...] cannot contain arbitrary expressions
597596
[builtins fixtures/tuple.pyi]
@@ -2739,16 +2738,22 @@ def test() -> None:
27392738
...
27402739
[builtins fixtures/bool.pyi]
27412740

2742-
[case testNegativeIntLiteral]
2741+
[case testUnaryOpLiteral]
27432742
from typing_extensions import Literal
27442743

27452744
a: Literal[-2] = -2
27462745
b: Literal[-1] = -1
27472746
c: Literal[0] = 0
27482747
d: Literal[1] = 1
27492748
e: Literal[2] = 2
2749+
f: Literal[+1] = 1
2750+
g: Literal[+2] = 2
2751+
2752+
x: Literal[+True] = True # E: Invalid type: Literal[...] cannot contain arbitrary expressions
2753+
y: Literal[-True] = -1 # E: Invalid type: Literal[...] cannot contain arbitrary expressions
2754+
z: Literal[~0] = 0 # E: Invalid type: Literal[...] cannot contain arbitrary expressions
27502755
[out]
2751-
[builtins fixtures/float.pyi]
2756+
[builtins fixtures/ops.pyi]
27522757

27532758
[case testNegativeIntLiteralWithFinal]
27542759
from typing_extensions import Literal, Final

test-data/unit/fixtures/ops.pyi

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ class tuple(Sequence[Tco]):
2424

2525
class function: pass
2626

27-
class bool: pass
28-
2927
class str:
3028
def __init__(self, x: 'int') -> None: pass
3129
def __add__(self, x: 'str') -> 'str': pass
@@ -54,6 +52,8 @@ class int:
5452
def __gt__(self, x: 'int') -> bool: pass
5553
def __ge__(self, x: 'int') -> bool: pass
5654

55+
class bool(int): pass
56+
5757
class float:
5858
def __add__(self, x: 'float') -> 'float': pass
5959
def __radd__(self, x: 'float') -> 'float': pass

0 commit comments

Comments
 (0)