From 3097e6a89c3945e00eb6be021ae71dc4780491db Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Thu, 20 Oct 2016 01:31:01 +0300 Subject: [PATCH 1/3] Support @ operator and add tests --- mypy/fastparse.py | 2 -- mypy/lex.py | 4 ++-- mypy/nodes.py | 4 +++- mypy/parse.py | 4 ++-- test-data/unit/check-expressions.test | 16 ++++++++++++++++ test-data/unit/check-statements.test | 15 +++++++++++++++ 6 files changed, 38 insertions(+), 7 deletions(-) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 85d7ac856672..68925edb7682 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -157,8 +157,6 @@ def from_operator(self, op: ast35.operator) -> str: op_name = ASTConverter.op_map.get(type(op)) if op_name is None: raise RuntimeError('Unknown operator ' + str(type(op))) - elif op_name == '@': - raise RuntimeError('mypy does not support the MatMult operator') else: return op_name diff --git a/mypy/lex.py b/mypy/lex.py index f074de9c09b1..66800a6a9659 100644 --- a/mypy/lex.py +++ b/mypy/lex.py @@ -195,13 +195,13 @@ def lex(string: Union[str, bytes], first_line: int = 1, # List of regular expressions that match non-alphabetical operators operators = [re.compile('[-+*/<>.%&|^~]'), - re.compile('==|!=|<=|>=|\\*\\*|//|<<|>>|<>')] + re.compile('==|!=|<=|>=|\\*\\*|@|//|<<|>>|<>')] # List of regular expressions that match punctuator tokens punctuators = [re.compile('[=,()@`]|(->)'), re.compile('\\['), re.compile(']'), - re.compile('([-+*/%&|^]|\\*\\*|//|<<|>>)=')] + re.compile('([-+*/%@&|^]|\\*\\*|//|<<|>>)=')] # Map single-character string escape sequences to corresponding characters. diff --git a/mypy/nodes.py b/mypy/nodes.py index 933a9934bf6c..7cc2d44d401e 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -1328,6 +1328,7 @@ def accept(self, visitor: NodeVisitor[T]) -> T: '%': '__mod__', '//': '__floordiv__', '**': '__pow__', + '@': '__matmul__', '&': '__and__', '|': '__or__', '^': '__xor__', @@ -1349,7 +1350,7 @@ def accept(self, visitor: NodeVisitor[T]) -> T: ops_with_inplace_method = { - '+', '-', '*', '/', '%', '//', '**', '&', '|', '^', '<<', '>>'} + '+', '-', '*', '/', '%', '//', '**', '@', '&', '|', '^', '<<', '>>'} inplace_operator_methods = set( '__i' + op_methods[op][2:] for op in ops_with_inplace_method) @@ -1362,6 +1363,7 @@ def accept(self, visitor: NodeVisitor[T]) -> T: '__mod__': '__rmod__', '__floordiv__': '__rfloordiv__', '__pow__': '__rpow__', + '__matmul__': '__rmatmul__', '__and__': '__rand__', '__or__': '__ror__', '__xor__': '__rxor__', diff --git a/mypy/parse.py b/mypy/parse.py index 5739056e4b21..a32b120753f7 100644 --- a/mypy/parse.py +++ b/mypy/parse.py @@ -44,7 +44,7 @@ '**': 16, '-u': 15, '+u': 15, '~': 15, # unary operators (-, + and ~) '': 14, - '*': 13, '/': 13, '//': 13, '%': 13, + '*': 13, '/': 13, '//': 13, '%': 13, '@': 13, '+': 12, '-': 12, '>>': 11, '<<': 11, '&': 10, @@ -61,7 +61,7 @@ op_assign = set([ - '+=', '-=', '*=', '/=', '//=', '%=', '**=', '|=', '&=', '^=', '>>=', + '+=', '-=', '*=', '/=', '//=', '%=', '**=', '@=', '|=', '&=', '^=', '>>=', '<<=']) op_comp = set([ diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 6ea0a9fba912..8e627d78dcbf 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -182,6 +182,22 @@ main:3: error: Unsupported operand types for * ("A" and "C") main:4: error: Incompatible types in assignment (expression has type "C", variable has type "A") main:5: error: Unsupported left operand type for * ("B") +[case testMatMul] + +a, b, c = None, None, None # type: (A, B, C) +c = a @ c # E: Unsupported operand types for @ ("A" and "C") +a = a @ b # E: Incompatible types in assignment (expression has type "C", variable has type "A") +c = b @ a # E: Unsupported left operand type for @ ("B") +c = a @ b + +class A: + def __matmul__(self, x: 'B') -> 'C': + pass +class B: + pass +class C: + pass + [case testDiv] a, b, c = None, None, None # type: (A, B, C) diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 0bbe47e9e686..c443a7c1b062 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -228,6 +228,18 @@ class C: pass main:3: error: Unsupported operand types for * ("A" and "A") main:4: error: Unsupported left operand type for * ("C") +[case testMatMulAssign] + +a, c = None, None # type: (A, C) +a @= a # E: Unsupported operand types for @ ("A" and "A") +c @= a # E: Unsupported left operand type for @ ("C") +a @= c + +class A: + def __matmul__(self, x: 'C') -> 'A': pass + +class C: pass + [case testDivAssign] a, c = None, None # type: (A, C) @@ -295,11 +307,14 @@ import typing class A: def __iadd__(self, x: int) -> 'A': pass def __imul__(self, x: str) -> 'A': pass + def __imatmul__(self, x: str) -> 'A': pass a = A() a += 1 a *= '' +a @= '' a += '' # E: Argument 1 to "__iadd__" of "A" has incompatible type "str"; expected "int" a *= 1 # E: Argument 1 to "__imul__" of "A" has incompatible type "int"; expected "str" +a @= 1 # E: Argument 1 to "__imatmul__" of "A" has incompatible type "int"; expected "str" [case testInplaceSetitem] class A(object): From 23f1acbf7edbeadab0f81faf857c276a45409fb4 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Thu, 20 Oct 2016 02:02:30 +0300 Subject: [PATCH 2/3] kill blank lines --- test-data/unit/check-expressions.test | 1 - test-data/unit/check-statements.test | 1 - 2 files changed, 2 deletions(-) diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 8e627d78dcbf..f0576dd4a47a 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -183,7 +183,6 @@ main:4: error: Incompatible types in assignment (expression has type "C", variab main:5: error: Unsupported left operand type for * ("B") [case testMatMul] - a, b, c = None, None, None # type: (A, B, C) c = a @ c # E: Unsupported operand types for @ ("A" and "C") a = a @ b # E: Incompatible types in assignment (expression has type "C", variable has type "A") diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index c443a7c1b062..4de0d72ae59b 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -229,7 +229,6 @@ main:3: error: Unsupported operand types for * ("A" and "A") main:4: error: Unsupported left operand type for * ("C") [case testMatMulAssign] - a, c = None, None # type: (A, C) a @= a # E: Unsupported operand types for @ ("A" and "A") c @= a # E: Unsupported left operand type for @ ("C") From 2a86647c325f2ab102f76d1d3f048bf070803148 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Thu, 20 Oct 2016 02:32:26 +0300 Subject: [PATCH 3/3] test that fast parser recognises @ --- test-data/unit/check-fastparse.test | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test-data/unit/check-fastparse.test b/test-data/unit/check-fastparse.test index 1ebb5ffa0cf1..810efa939f09 100644 --- a/test-data/unit/check-fastparse.test +++ b/test-data/unit/check-fastparse.test @@ -179,3 +179,10 @@ def f(a): pass [out] main:3: error: invalid type comment + +[case testFastParseMatMul] +# flags: --fast-parser +from typing import Any +x = None # type: Any +x @ 1 +x @= 1