From 796f5274b2967abb94db1bf52e5ab9326759d3e8 Mon Sep 17 00:00:00 2001 From: TH3CHARLie Date: Tue, 12 Nov 2019 09:44:16 +0800 Subject: [PATCH 1/7] add testcase --- test-data/unit/check-classes.test | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 78121bc70dad..c8fbd59cf26e 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -6512,3 +6512,28 @@ def access_after_declaration(self) -> None: reveal_type(x) # N: Revealed type is 'builtins.int' x = x + 1 + +[case testIsSubClassNarrowDownTypesOfTypeVariables] +from typing import TypeVar, Generic + +TypeT = TypeVar("TypeT", bound=type) + +class IntBase: + field: int = 42 + +class IntFoo(Generic[TypeT]): + def method(self, other: TypeT) -> int: + if issubclass(other, IntBase): + return other.field + return 0 + +class StrBase: + field: str = "hello" + +class StrFoo(Generic[TypeT]): + def method(self, other: TypeT) -> str: + if issubclass(other, StrBase): + return other.field + return "hi" + +[builtins fixtures/isinstancelist.pyi] From 238bfa8d2c2f804013e6849135e2a69c0b836def Mon Sep 17 00:00:00 2001 From: TH3CHARLie Date: Tue, 12 Nov 2019 09:59:12 +0800 Subject: [PATCH 2/7] modify find_isinstance_check in checker.py to fix bug --- mypy/checker.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 557ceb8a71c0..7259cef15da5 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3745,6 +3745,8 @@ def find_isinstance_check(self, node: Expression elif (isinstance(vartype, Instance) and vartype.type.fullname() == 'builtins.type'): vartype = self.named_type('builtins.object') + elif (isinstance(vartype, TypeVarType)): + vartype = vartype.upper_bound else: # Any other object whose type we don't know precisely # for example, Any or a custom metaclass. From 5b35e9d8b20087652ce23f6aab54403c831711d1 Mon Sep 17 00:00:00 2001 From: TH3CHARLie Date: Wed, 13 Nov 2019 09:51:33 +0800 Subject: [PATCH 3/7] adjust branch statement in checker.py, modify testcase --- mypy/checker.py | 5 +++-- test-data/unit/check-classes.test | 21 ++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 7259cef15da5..7c6e1583db6a 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3730,6 +3730,9 @@ def find_isinstance_check(self, node: Expression if literal(expr) == LITERAL_TYPE: vartype = get_proper_type(type_map[expr]) type = get_isinstance_type(node.args[1], type_map) + if (isinstance(vartype, TypeVarType)): + vartype = vartype.upper_bound + vartype = get_proper_type(vartype) if isinstance(vartype, UnionType): union_list = [] for t in get_proper_types(vartype.items): @@ -3745,8 +3748,6 @@ def find_isinstance_check(self, node: Expression elif (isinstance(vartype, Instance) and vartype.type.fullname() == 'builtins.type'): vartype = self.named_type('builtins.object') - elif (isinstance(vartype, TypeVarType)): - vartype = vartype.upper_bound else: # Any other object whose type we don't know precisely # for example, Any or a custom metaclass. diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index c8fbd59cf26e..467f2d436cda 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -6518,22 +6518,21 @@ from typing import TypeVar, Generic TypeT = TypeVar("TypeT", bound=type) -class IntBase: +class Base: field: int = 42 -class IntFoo(Generic[TypeT]): - def method(self, other: TypeT) -> int: - if issubclass(other, IntBase): +class C1: + def method(self, other: type) -> int: + if issubclass(other, Base): + reveal_type(other) # N: Revealed type is 'Type[__main__.Base]' return other.field return 0 -class StrBase: - field: str = "hello" - -class StrFoo(Generic[TypeT]): - def method(self, other: TypeT) -> str: - if issubclass(other, StrBase): +class C2(Generic[TypeT]): + def method(self, other: TypeT) -> int: + if issubclass(other, Base): + reveal_type(other) # N: Revealed type is 'Type[__main__.Base]' return other.field - return "hi" + return 0 [builtins fixtures/isinstancelist.pyi] From df62cbc10529256d4aea1912236fab2509196cd0 Mon Sep 17 00:00:00 2001 From: TH3CHARLie Date: Wed, 13 Nov 2019 10:37:02 +0800 Subject: [PATCH 4/7] refactor issubclass check into helper function --- mypy/checker.py | 64 +++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 7c6e1583db6a..c401818dfb60 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3726,35 +3726,7 @@ def find_isinstance_check(self, node: Expression elif refers_to_fullname(node.callee, 'builtins.issubclass'): if len(node.args) != 2: # the error will be reported elsewhere return {}, {} - expr = node.args[0] - if literal(expr) == LITERAL_TYPE: - vartype = get_proper_type(type_map[expr]) - type = get_isinstance_type(node.args[1], type_map) - if (isinstance(vartype, TypeVarType)): - vartype = vartype.upper_bound - vartype = get_proper_type(vartype) - if isinstance(vartype, UnionType): - union_list = [] - for t in get_proper_types(vartype.items): - if isinstance(t, TypeType): - union_list.append(t.item) - else: - # This is an error that should be reported earlier - # if we reach here, we refuse to do any type inference. - return {}, {} - vartype = UnionType(union_list) - elif isinstance(vartype, TypeType): - vartype = vartype.item - elif (isinstance(vartype, Instance) and - vartype.type.fullname() == 'builtins.type'): - vartype = self.named_type('builtins.object') - else: - # Any other object whose type we don't know precisely - # for example, Any or a custom metaclass. - return {}, {} # unknown type - yes_map, no_map = conditional_type_map(expr, vartype, type) - yes_map, no_map = map(convert_to_typetype, (yes_map, no_map)) - return yes_map, no_map + return self.infer_issubclass_maps(node, type_map) elif refers_to_fullname(node.callee, 'builtins.callable'): if len(node.args) != 1: # the error will be reported elsewhere return {}, {} @@ -4197,6 +4169,40 @@ def push_type_map(self, type_map: 'TypeMap') -> None: for expr, type in type_map.items(): self.binder.put(expr, type) + def infer_issubclass_maps(self, node: CallExpr, + type_map: Dict[Expression, Type] + ) -> Tuple[TypeMap, TypeMap]: + expr = node.args[0] + if literal(expr) == LITERAL_TYPE: + vartype = type_map[expr] + type = get_isinstance_type(node.args[1], type_map) + if (isinstance(vartype, TypeVarType)): + vartype = vartype.upper_bound + vartype = get_proper_type(vartype) + if isinstance(vartype, UnionType): + union_list = [] + for t in get_proper_types(vartype.items): + if isinstance(t, TypeType): + union_list.append(t.item) + else: + # This is an error that should be reported earlier + # if we reach here, we refuse to do any type inference. + return {}, {} + vartype = UnionType(union_list) + elif isinstance(vartype, TypeType): + vartype = vartype.item + elif (isinstance(vartype, Instance) and + vartype.type.fullname() == 'builtins.type'): + vartype = self.named_type('builtins.object') + else: + # Any other object whose type we don't know precisely + # for example, Any or a custom metaclass. + return {}, {} # unknown type + yes_map, no_map = conditional_type_map(expr, vartype, type) + yes_map, no_map = map(convert_to_typetype, (yes_map, no_map)) + return yes_map, no_map + return {}, {} + def conditional_type_map(expr: Expression, current_type: Optional[Type], From 316cca73b582dd4543b6688f1d4bb45381ff8fbb Mon Sep 17 00:00:00 2001 From: TH3CHARLie Date: Wed, 13 Nov 2019 22:20:57 +0800 Subject: [PATCH 5/7] modify test by adding new class with different upper bound, add docstring for helper --- mypy/checker.py | 69 ++++++++++++++++--------------- test-data/unit/check-classes.test | 14 +++++++ 2 files changed, 49 insertions(+), 34 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index c401818dfb60..730d17bed183 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3726,7 +3726,9 @@ def find_isinstance_check(self, node: Expression elif refers_to_fullname(node.callee, 'builtins.issubclass'): if len(node.args) != 2: # the error will be reported elsewhere return {}, {} - return self.infer_issubclass_maps(node, type_map) + expr = node.args[0] + if literal(expr) == LITERAL_TYPE: + return self.infer_issubclass_maps_literal_type(node, expr, type_map) elif refers_to_fullname(node.callee, 'builtins.callable'): if len(node.args) != 1: # the error will be reported elsewhere return {}, {} @@ -4169,39 +4171,38 @@ def push_type_map(self, type_map: 'TypeMap') -> None: for expr, type in type_map.items(): self.binder.put(expr, type) - def infer_issubclass_maps(self, node: CallExpr, - type_map: Dict[Expression, Type] - ) -> Tuple[TypeMap, TypeMap]: - expr = node.args[0] - if literal(expr) == LITERAL_TYPE: - vartype = type_map[expr] - type = get_isinstance_type(node.args[1], type_map) - if (isinstance(vartype, TypeVarType)): - vartype = vartype.upper_bound - vartype = get_proper_type(vartype) - if isinstance(vartype, UnionType): - union_list = [] - for t in get_proper_types(vartype.items): - if isinstance(t, TypeType): - union_list.append(t.item) - else: - # This is an error that should be reported earlier - # if we reach here, we refuse to do any type inference. - return {}, {} - vartype = UnionType(union_list) - elif isinstance(vartype, TypeType): - vartype = vartype.item - elif (isinstance(vartype, Instance) and - vartype.type.fullname() == 'builtins.type'): - vartype = self.named_type('builtins.object') - else: - # Any other object whose type we don't know precisely - # for example, Any or a custom metaclass. - return {}, {} # unknown type - yes_map, no_map = conditional_type_map(expr, vartype, type) - yes_map, no_map = map(convert_to_typetype, (yes_map, no_map)) - return yes_map, no_map - return {}, {} + def infer_issubclass_maps_literal_type(self, node: CallExpr, + expr: Expression, + type_map: Dict[Expression, Type] + ) -> Tuple[TypeMap, TypeMap]: + """Infer type maps for issubclass calls on literal type.""" + vartype = type_map[expr] + type = get_isinstance_type(node.args[1], type_map) + if (isinstance(vartype, TypeVarType)): + vartype = vartype.upper_bound + vartype = get_proper_type(vartype) + if isinstance(vartype, UnionType): + union_list = [] + for t in get_proper_types(vartype.items): + if isinstance(t, TypeType): + union_list.append(t.item) + else: + # This is an error that should be reported earlier + # if we reach here, we refuse to do any type inference. + return {}, {} + vartype = UnionType(union_list) + elif isinstance(vartype, TypeType): + vartype = vartype.item + elif (isinstance(vartype, Instance) and + vartype.type.fullname() == 'builtins.type'): + vartype = self.named_type('builtins.object') + else: + # Any other object whose type we don't know precisely + # for example, Any or a custom metaclass. + return {}, {} # unknown type + yes_map, no_map = conditional_type_map(expr, vartype, type) + yes_map, no_map = map(convert_to_typetype, (yes_map, no_map)) + return yes_map, no_map def conditional_type_map(expr: Expression, diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 467f2d436cda..43b354ab0cc2 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -6516,11 +6516,18 @@ def access_after_declaration(self) -> None: [case testIsSubClassNarrowDownTypesOfTypeVariables] from typing import TypeVar, Generic +class Foo: + int_val: int = 42 + str_val: str = "hello" + TypeT = TypeVar("TypeT", bound=type) +TypeT1 = TypeVar("TypeT1", bound=Foo) + class Base: field: int = 42 + class C1: def method(self, other: type) -> int: if issubclass(other, Base): @@ -6535,4 +6542,11 @@ class C2(Generic[TypeT]): return other.field return 0 +class C3(Generic[TypeT1]): + def method(self, other: TypeT) -> int: + if issubclass(other, Base): + reveal_type(other) # N: Revealed type is 'Type[__main__.Base]' + return other.field + return 0 + [builtins fixtures/isinstancelist.pyi] From e987e420e6f3c17c06be289f2fedb1ae9cf27384 Mon Sep 17 00:00:00 2001 From: TH3CHARLie Date: Wed, 13 Nov 2019 23:50:10 +0800 Subject: [PATCH 6/7] modify testcase, modify helper function name --- mypy/checker.py | 12 ++++++------ test-data/unit/check-classes.test | 17 ++++++----------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 730d17bed183..e1963b34e1e6 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3728,7 +3728,7 @@ def find_isinstance_check(self, node: Expression return {}, {} expr = node.args[0] if literal(expr) == LITERAL_TYPE: - return self.infer_issubclass_maps_literal_type(node, expr, type_map) + return self.infer_issubclass_maps(node, expr, type_map) elif refers_to_fullname(node.callee, 'builtins.callable'): if len(node.args) != 1: # the error will be reported elsewhere return {}, {} @@ -4171,11 +4171,11 @@ def push_type_map(self, type_map: 'TypeMap') -> None: for expr, type in type_map.items(): self.binder.put(expr, type) - def infer_issubclass_maps_literal_type(self, node: CallExpr, - expr: Expression, - type_map: Dict[Expression, Type] - ) -> Tuple[TypeMap, TypeMap]: - """Infer type maps for issubclass calls on literal type.""" + def infer_issubclass_maps(self, node: CallExpr, + expr: Expression, + type_map: Dict[Expression, Type] + ) -> Tuple[TypeMap, TypeMap]: + """Infer type restrictions for an expression in issubclass call.""" vartype = type_map[expr] type = get_isinstance_type(node.args[1], type_map) if (isinstance(vartype, TypeVarType)): diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 43b354ab0cc2..9e134d95cb97 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -6514,19 +6514,14 @@ def access_after_declaration(self) -> None: x = x + 1 [case testIsSubClassNarrowDownTypesOfTypeVariables] -from typing import TypeVar, Generic - -class Foo: - int_val: int = 42 - str_val: str = "hello" - -TypeT = TypeVar("TypeT", bound=type) - -TypeT1 = TypeVar("TypeT1", bound=Foo) +from typing import Type, TypeVar, Generic class Base: field: int = 42 +TypeT = TypeVar("TypeT", bound=type) + +TypeT1 = TypeVar("TypeT1", bound=Type[Base]) class C1: def method(self, other: type) -> int: @@ -6543,9 +6538,9 @@ class C2(Generic[TypeT]): return 0 class C3(Generic[TypeT1]): - def method(self, other: TypeT) -> int: + def method(self, other: TypeT1) -> int: if issubclass(other, Base): - reveal_type(other) # N: Revealed type is 'Type[__main__.Base]' + reveal_type(other) # N: Revealed type is 'TypeT1`1' return other.field return 0 From 8ab0d96b8d2d53557c65e1b60d76a8396c9dd5e7 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 14 Nov 2019 14:48:08 +0000 Subject: [PATCH 7/7] Remove redundant parens --- mypy/checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index e1963b34e1e6..4bb5d031be83 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4178,7 +4178,7 @@ def infer_issubclass_maps(self, node: CallExpr, """Infer type restrictions for an expression in issubclass call.""" vartype = type_map[expr] type = get_isinstance_type(node.args[1], type_map) - if (isinstance(vartype, TypeVarType)): + if isinstance(vartype, TypeVarType): vartype = vartype.upper_bound vartype = get_proper_type(vartype) if isinstance(vartype, UnionType): pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy