diff --git a/mypy/checker.py b/mypy/checker.py index 2f0d37d626dc..7c7913fbeeb9 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3733,30 +3733,7 @@ def find_isinstance_check_helper(self, node: Expression) -> Tuple[TypeMap, TypeM 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, 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, expr, type_map) elif refers_to_fullname(node.callee, 'builtins.callable'): if len(node.args) != 1: # the error will be reported elsewhere return {}, {} @@ -4367,6 +4344,39 @@ 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, + 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): + 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, current_type: Optional[Type], diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 78121bc70dad..9e134d95cb97 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -6512,3 +6512,36 @@ def access_after_declaration(self) -> None: reveal_type(x) # N: Revealed type is 'builtins.int' x = x + 1 + +[case testIsSubClassNarrowDownTypesOfTypeVariables] +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: + if issubclass(other, Base): + reveal_type(other) # N: Revealed type is 'Type[__main__.Base]' + return other.field + return 0 + +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 0 + +class C3(Generic[TypeT1]): + def method(self, other: TypeT1) -> int: + if issubclass(other, Base): + reveal_type(other) # N: Revealed type is 'TypeT1`1' + return other.field + return 0 + +[builtins fixtures/isinstancelist.pyi] 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