From e662fddbf4815674799197cd67cb846e8211f105 Mon Sep 17 00:00:00 2001 From: YuanchaoZhu Date: Mon, 22 Sep 2014 01:48:16 -0400 Subject: [PATCH 1/9] Implement type checking raise from --- mypy/checker.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 4d6e0999f68f..885da982a058 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1338,8 +1338,12 @@ def visit_raise_stmt(self, s: RaiseStmt) -> Type: # Good! return None # Else fall back to the check below (which will fail). + super_type = self.named_type('builtins.BaseException') + # Type checking "raise from" + if s.from_expr: + super_type = self.accept(s.from_expr) self.check_subtype(typ, - self.named_type('builtins.BaseException'), s, + super_type, s, messages.INVALID_EXCEPTION) def visit_try_stmt(self, s: TryStmt) -> Type: From be8cc1c2f406cd6709f6dab436c3e93ec5b9c1a7 Mon Sep 17 00:00:00 2001 From: YuanchaoZhu Date: Mon, 22 Sep 2014 03:42:56 -0400 Subject: [PATCH 2/9] New test cases for raise from type checking --- mypy/test/data/check-statements.test | 8 ++++++++ mypy/test/data/fixtures/exception.py | 2 ++ 2 files changed, 10 insertions(+) diff --git a/mypy/test/data/check-statements.test b/mypy/test/data/check-statements.test index 185769a1c872..6cc3d4b6fb63 100644 --- a/mypy/test/data/check-statements.test +++ b/mypy/test/data/check-statements.test @@ -313,6 +313,14 @@ raise object # E: Exception must be derived from BaseException raise f # E: Exception must be derived from BaseException [builtins fixtures/exception.py] +[case testRaiseFromStatement] +from typing import Undefined +e = Undefined # type: Exception +a = Undefined # type: BaseException +raise e from a +raise e from 1 # E: Exception must be derived from BaseException +[builtins fixtures/exception.py] + [case testTryFinallyStatement] import typing try: diff --git a/mypy/test/data/fixtures/exception.py b/mypy/test/data/fixtures/exception.py index 8177156ccc4d..928272f64d2d 100644 --- a/mypy/test/data/fixtures/exception.py +++ b/mypy/test/data/fixtures/exception.py @@ -7,3 +7,5 @@ class tuple: pass class function: pass class BaseException: pass +class Exception: pass +class int: pass From ef13fc5d71f35fb3bec5063d551c58d2e2d05e4e Mon Sep 17 00:00:00 2001 From: YuanchaoZhu Date: Mon, 22 Sep 2014 10:37:15 -0400 Subject: [PATCH 3/9] Cast super_type from Instance down to Type --- mypy/checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 885da982a058..a2aa9f33e118 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1338,7 +1338,7 @@ def visit_raise_stmt(self, s: RaiseStmt) -> Type: # Good! return None # Else fall back to the check below (which will fail). - super_type = self.named_type('builtins.BaseException') + super_type = cast(Type, self.named_type('builtins.BaseException')) # Type checking "raise from" if s.from_expr: super_type = self.accept(s.from_expr) From fb77d28d06dcc309554ec5edee636cc070acbdbb Mon Sep 17 00:00:00 2001 From: YuanchaoZhu Date: Mon, 29 Sep 2014 03:47:59 -0400 Subject: [PATCH 4/9] Add more raise from statement test cases --- mypy/test/data/check-statements.test | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/mypy/test/data/check-statements.test b/mypy/test/data/check-statements.test index 6cc3d4b6fb63..f99786c4057b 100644 --- a/mypy/test/data/check-statements.test +++ b/mypy/test/data/check-statements.test @@ -315,10 +315,26 @@ raise f # E: Exception must be derived from BaseException [case testRaiseFromStatement] from typing import Undefined -e = Undefined # type: Exception -a = Undefined # type: BaseException -raise e from a -raise e from 1 # E: Exception must be derived from BaseException +e = Undefined # type: BaseException +f = Undefined # type: MyError +a = Undefined # type: A +raise e from a # E: Exception must be derived from BaseException +raise e from e +raise e from f +class A: pass +class MyError(BaseException): pass +[builtins fixtures/exception.py] + +[case testRaiseFromClassobject] +import typing +class A: pass +class MyError(BaseException): pass +def f(): pass +raise BaseException from BaseException +raise BaseException from MyError +raise BaseException from A # E: Exception must be derived from BaseException +raise BaseException from object # E: Exception must be derived from BaseException +raise BaseException from f # E: Exception must be derived from BaseException [builtins fixtures/exception.py] [case testTryFinallyStatement] From 99561a04fc3f30f8424b71da4cab3cf7729a402f Mon Sep 17 00:00:00 2001 From: YuanchaoZhu Date: Mon, 29 Sep 2014 03:48:54 -0400 Subject: [PATCH 5/9] Remove unused class in fixtures/exception.py --- mypy/test/data/fixtures/exception.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mypy/test/data/fixtures/exception.py b/mypy/test/data/fixtures/exception.py index 928272f64d2d..8177156ccc4d 100644 --- a/mypy/test/data/fixtures/exception.py +++ b/mypy/test/data/fixtures/exception.py @@ -7,5 +7,3 @@ class tuple: pass class function: pass class BaseException: pass -class Exception: pass -class int: pass From 6de93540517a86494e5b9ed1c8694db1dc31c8cf Mon Sep 17 00:00:00 2001 From: YuanchaoZhu Date: Mon, 29 Sep 2014 03:51:32 -0400 Subject: [PATCH 6/9] Semantic analyze from_expr of RaiseStmt --- mypy/semanal.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index c354cfc823cf..ca80caeefcec 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -993,6 +993,8 @@ def visit_return_stmt(self, s: ReturnStmt) -> None: def visit_raise_stmt(self, s: RaiseStmt) -> None: if s.expr: s.expr.accept(self) + if s.from_expr: + s.from_expr.accept(self) def visit_yield_stmt(self, s: YieldStmt) -> None: if not self.is_func_scope(): From 5ec84c969ab25ceaa2a25de3e0388a506751d76c Mon Sep 17 00:00:00 2001 From: YuanchaoZhu Date: Mon, 29 Sep 2014 03:52:29 -0400 Subject: [PATCH 7/9] Add raise from type checking --- mypy/checker.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index a2aa9f33e118..88c4232839b9 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1328,22 +1328,39 @@ def visit_raise_stmt(self, s: RaiseStmt) -> Type: """Type check a raise statement.""" self.breaking_out = True if s.expr: + skip_check_subtype = False typ = self.accept(s.expr) if isinstance(typ, FunctionLike): if typ.is_type_obj(): # Cases like "raise ExceptionClass". typeinfo = typ.type_object() base = self.lookup_typeinfo('builtins.BaseException') + if base in typeinfo.mro: + # Good! + if s.from_expr: + skip_check_subtype = True + else: + return None + # Else fall back to the check below (which will fail). + if not skip_check_subtype: + self.check_subtype(typ, + self.named_type('builtins.BaseException'), s, + messages.INVALID_EXCEPTION) + + # Type checking from + if s.from_expr: + typ = self.accept(s.from_expr) + if isinstance(typ, FunctionLike): + if typ.is_type_obj(): + # Cases like "from ExceptionClass". + typeinfo = typ.type_object() + base = self.lookup_typeinfo('builtins.BaseException') if base in typeinfo.mro: # Good! return None # Else fall back to the check below (which will fail). - super_type = cast(Type, self.named_type('builtins.BaseException')) - # Type checking "raise from" - if s.from_expr: - super_type = self.accept(s.from_expr) self.check_subtype(typ, - super_type, s, + self.named_type('builtins.BaseException'), s, messages.INVALID_EXCEPTION) def visit_try_stmt(self, s: TryStmt) -> Type: From b5d2bacf53868d9cd508a4ed93addddfb317d502 Mon Sep 17 00:00:00 2001 From: YuanchaoZhu Date: Sun, 5 Oct 2014 22:17:26 -0400 Subject: [PATCH 8/9] Add type_check_raise() function and remove duplicate code --- mypy/checker.py | 50 +++++++++++++++++-------------------------------- 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 88c4232839b9..ccacd8d8065f 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1328,40 +1328,24 @@ def visit_raise_stmt(self, s: RaiseStmt) -> Type: """Type check a raise statement.""" self.breaking_out = True if s.expr: - skip_check_subtype = False - typ = self.accept(s.expr) - if isinstance(typ, FunctionLike): - if typ.is_type_obj(): - # Cases like "raise ExceptionClass". - typeinfo = typ.type_object() - base = self.lookup_typeinfo('builtins.BaseException') - if base in typeinfo.mro: - # Good! - if s.from_expr: - skip_check_subtype = True - else: - return None - # Else fall back to the check below (which will fail). - if not skip_check_subtype: - self.check_subtype(typ, - self.named_type('builtins.BaseException'), s, - messages.INVALID_EXCEPTION) - - # Type checking from + self.type_check_raise(s.expr, s) if s.from_expr: - typ = self.accept(s.from_expr) - if isinstance(typ, FunctionLike): - if typ.is_type_obj(): - # Cases like "from ExceptionClass". - typeinfo = typ.type_object() - base = self.lookup_typeinfo('builtins.BaseException') - if base in typeinfo.mro: - # Good! - return None - # Else fall back to the check below (which will fail). - self.check_subtype(typ, - self.named_type('builtins.BaseException'), s, - messages.INVALID_EXCEPTION) + self.type_check_raise(s.from_expr, s) + + def type_check_raise(self, e: NameExpr, s: RaiseStmt) -> None: + typ = self.accept(e) + if isinstance(typ, FunctionLike): + if typ.is_type_obj(): + # Cases like "raise/from ExceptionClass". + typeinfo = typ.type_object() + base = self.lookup_typeinfo('builtins.BaseException') + if base in typeinfo.mro: + # Good! + return None + # Else fall back to the check below (which will fail). + self.check_subtype(typ, + self.named_type('builtins.BaseException'), s, + messages.INVALID_EXCEPTION) def visit_try_stmt(self, s: TryStmt) -> Type: """Type check a try statement.""" From 134ae8c612ec2baa018dfdaa8e1be0600828a531 Mon Sep 17 00:00:00 2001 From: YuanchaoZhu Date: Sun, 5 Oct 2014 22:47:56 -0400 Subject: [PATCH 9/9] Change type to Node --- mypy/checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index ccacd8d8065f..19cf07536b86 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1332,7 +1332,7 @@ def visit_raise_stmt(self, s: RaiseStmt) -> Type: if s.from_expr: self.type_check_raise(s.from_expr, s) - def type_check_raise(self, e: NameExpr, s: RaiseStmt) -> None: + def type_check_raise(self, e: Node, s: RaiseStmt) -> None: typ = self.accept(e) if isinstance(typ, FunctionLike): if typ.is_type_obj():