From 58fdff54a88f3456efc272eb599ac015de4e5e16 Mon Sep 17 00:00:00 2001 From: Evgeniy Slobodkin Date: Sat, 20 Apr 2024 21:24:38 +0300 Subject: [PATCH 1/5] fix: TypeGuard becomes bool instead of Any when passed as TypeVar --- mypy/constraints.py | 8 ++++++-- test-data/unit/check-typeguard.test | 9 +++++++++ test-data/unit/check-typeis.test | 9 +++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 49a2aea8fa05..ffac916482df 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -1026,13 +1026,17 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: param_spec = template.param_spec() template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type + bool_type = UnionType( + [LiteralType(True, cactual_ret_type), LiteralType(False, cactual_ret_type)] # type: ignore[arg-type] + ) + if template.type_guard is not None and cactual.type_guard is not None: template_ret_type = template.type_guard cactual_ret_type = cactual.type_guard elif template.type_guard is not None: template_ret_type = AnyType(TypeOfAny.special_form) elif cactual.type_guard is not None: - cactual_ret_type = AnyType(TypeOfAny.special_form) + cactual_ret_type = bool_type if template.type_is is not None and cactual.type_is is not None: template_ret_type = template.type_is @@ -1040,7 +1044,7 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: elif template.type_is is not None: template_ret_type = AnyType(TypeOfAny.special_form) elif cactual.type_is is not None: - cactual_ret_type = AnyType(TypeOfAny.special_form) + cactual_ret_type = bool_type res.extend(infer_constraints(template_ret_type, cactual_ret_type, self.direction)) diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index 27b88553fb43..e7a8eac4f043 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -87,6 +87,15 @@ def main(a: Tuple[T, ...]): reveal_type(a) # N: Revealed type is "Tuple[T`-1, T`-1]" [builtins fixtures/tuple.pyi] +[case testTypeGuardPassedAsTypeVarIsBool] +from typing import Callable, TypeVar +from typing_extensions import TypeGuard +T = TypeVar('T') +def is_str(x: object) -> TypeGuard[str]: ... +def main(f: Callable[[object], T]) -> T: ... +reveal_type(main(is_str)) # N: Revealed type is "builtins.bool" +[builtins fixtures/tuple.pyi] + [case testTypeGuardNonOverlapping] from typing import List from typing_extensions import TypeGuard diff --git a/test-data/unit/check-typeis.test b/test-data/unit/check-typeis.test index 6b96845504ab..2372f990fda1 100644 --- a/test-data/unit/check-typeis.test +++ b/test-data/unit/check-typeis.test @@ -104,6 +104,15 @@ def main(x: object, type_check_func: Callable[[object], TypeIs[T]]) -> T: reveal_type(main("a", is_str)) # N: Revealed type is "builtins.str" [builtins fixtures/exception.pyi] +[case testTypeIsPassedAsTypeVarIsBool] +from typing import Callable, TypeVar +from typing_extensions import TypeIs +T = TypeVar('T') +def is_str(x: object) -> TypeIs[str]: pass +def main(f: Callable[[object], T]) -> T: pass +reveal_type(main(is_str)) # N: Revealed type is "builtins.bool" +[builtins fixtures/tuple.pyi] + [case testTypeIsUnionIn] from typing import Union from typing_extensions import TypeIs From c86a616aa98fdc8749dedb265ab31c614f4282fe Mon Sep 17 00:00:00 2001 From: Evgeniy Slobodkin Date: Fri, 26 Apr 2024 02:33:28 +0300 Subject: [PATCH 2/5] fix actual_ret_type for TypeGuard --- mypy/constraints.py | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index ffac916482df..36ce230d2503 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -17,6 +17,9 @@ CONTRAVARIANT, COVARIANT, ArgKind, + Block, + ClassDef, + SymbolTable, TypeInfo, ) from mypy.types import ( @@ -1026,25 +1029,18 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: param_spec = template.param_spec() template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type - bool_type = UnionType( - [LiteralType(True, cactual_ret_type), LiteralType(False, cactual_ret_type)] # type: ignore[arg-type] - ) if template.type_guard is not None and cactual.type_guard is not None: template_ret_type = template.type_guard cactual_ret_type = cactual.type_guard elif template.type_guard is not None: template_ret_type = AnyType(TypeOfAny.special_form) - elif cactual.type_guard is not None: - cactual_ret_type = bool_type if template.type_is is not None and cactual.type_is is not None: template_ret_type = template.type_is cactual_ret_type = cactual.type_is elif template.type_is is not None: template_ret_type = AnyType(TypeOfAny.special_form) - elif cactual.type_is is not None: - cactual_ret_type = bool_type res.extend(infer_constraints(template_ret_type, cactual_ret_type, self.direction)) @@ -1352,6 +1348,40 @@ def visit_type_type(self, template: TypeType) -> list[Constraint]: else: return [] + def _make_type_info( + self, + name: str, + module_name: str | None = None, + mro: list[TypeInfo] | None = None, + bases: list[Instance] | None = None, + ) -> TypeInfo: + """Make a TypeInfo suitable for use in unit tests.""" + + class_def = ClassDef(name, Block([]), None, []) + class_def.fullname = name + + if module_name is None: + if "." in name: + module_name = name.rsplit(".", 1)[0] + else: + module_name = "__main__" + + info = TypeInfo(SymbolTable(), class_def, module_name) + if mro is None: + mro = [] + if name != "builtins.object": + mro.append(self.oi) + info.mro = [info] + mro + if bases is None: + if mro: + # By default, assume that there is a single non-generic base. + bases = [Instance(mro[0], [])] + else: + bases = [] + info.bases = bases + + return info + def neg_op(op: int) -> int: """Map SubtypeOf to SupertypeOf and vice versa.""" From e10038f8cac4253df6c1c58ca8d9c9ec956cdff8 Mon Sep 17 00:00:00 2001 From: Evgeniy Slobodkin Date: Fri, 26 Apr 2024 02:38:15 +0300 Subject: [PATCH 3/5] remove useless code --- mypy/constraints.py | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 36ce230d2503..d8bfd060ee87 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -17,9 +17,6 @@ CONTRAVARIANT, COVARIANT, ArgKind, - Block, - ClassDef, - SymbolTable, TypeInfo, ) from mypy.types import ( @@ -1348,40 +1345,6 @@ def visit_type_type(self, template: TypeType) -> list[Constraint]: else: return [] - def _make_type_info( - self, - name: str, - module_name: str | None = None, - mro: list[TypeInfo] | None = None, - bases: list[Instance] | None = None, - ) -> TypeInfo: - """Make a TypeInfo suitable for use in unit tests.""" - - class_def = ClassDef(name, Block([]), None, []) - class_def.fullname = name - - if module_name is None: - if "." in name: - module_name = name.rsplit(".", 1)[0] - else: - module_name = "__main__" - - info = TypeInfo(SymbolTable(), class_def, module_name) - if mro is None: - mro = [] - if name != "builtins.object": - mro.append(self.oi) - info.mro = [info] + mro - if bases is None: - if mro: - # By default, assume that there is a single non-generic base. - bases = [Instance(mro[0], [])] - else: - bases = [] - info.bases = bases - - return info - def neg_op(op: int) -> int: """Map SubtypeOf to SupertypeOf and vice versa.""" From c4f187764b80e126042a254047272433e884538c Mon Sep 17 00:00:00 2001 From: Evgeniy Slobodkin Date: Fri, 26 Apr 2024 02:39:17 +0300 Subject: [PATCH 4/5] remove empty line --- mypy/constraints.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index d8bfd060ee87..3d4b2fd0a11d 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -1026,7 +1026,6 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: param_spec = template.param_spec() template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type - if template.type_guard is not None and cactual.type_guard is not None: template_ret_type = template.type_guard cactual_ret_type = cactual.type_guard From b16702269753682f52a1912def535f2e2eba77c6 Mon Sep 17 00:00:00 2001 From: Evgeniy Slobodkin Date: Fri, 18 Oct 2024 01:29:06 +0300 Subject: [PATCH 5/5] remove redundant check for typeguard and typeis --- mypy/constraints.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mypy/constraints.py b/mypy/constraints.py index 3d4b2fd0a11d..5c815bf2af65 100644 --- a/mypy/constraints.py +++ b/mypy/constraints.py @@ -1029,14 +1029,10 @@ def visit_callable_type(self, template: CallableType) -> list[Constraint]: if template.type_guard is not None and cactual.type_guard is not None: template_ret_type = template.type_guard cactual_ret_type = cactual.type_guard - elif template.type_guard is not None: - template_ret_type = AnyType(TypeOfAny.special_form) if template.type_is is not None and cactual.type_is is not None: template_ret_type = template.type_is cactual_ret_type = cactual.type_is - elif template.type_is is not None: - template_ret_type = AnyType(TypeOfAny.special_form) res.extend(infer_constraints(template_ret_type, cactual_ret_type, self.direction))