From 56e28256276ad54c076f58990b29f28679e3250c Mon Sep 17 00:00:00 2001 From: detachhead Date: Sun, 1 Dec 2024 20:02:00 +1000 Subject: [PATCH] fix `Callable` narrowing hack incorrectly being used on `TypeIs` --- packages/pyright-internal/src/analyzer/typeGuards.ts | 7 ++++++- .../src/tests/samples/typeNarrowingUsingBounds.py | 10 ++++++++-- .../tests/samples/typeNarrowingUsingBoundsDisabled.py | 10 ++++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/pyright-internal/src/analyzer/typeGuards.ts b/packages/pyright-internal/src/analyzer/typeGuards.ts index 02ca177f2..6a4e489f5 100644 --- a/packages/pyright-internal/src/analyzer/typeGuards.ts +++ b/packages/pyright-internal/src/analyzer/typeGuards.ts @@ -1818,7 +1818,12 @@ function narrowTypeForInstance( if ( isClass(subtype) || - (getFileInfo(errorNode).diagnosticRuleSet.strictGenericNarrowing && isFunction(subtype)) + // when strictGenericNarrowing is enabled, we need to convert the Callable type to a Callable + // protocol to make sure it keeps the generics when narrowing, but this is only needed for + // isinstance checks because you can't specify generics to it + (!isTypeIsCheck && + getFileInfo(errorNode).diagnosticRuleSet.strictGenericNarrowing && + isFunction(subtype)) ) { if (isFunction(subtype)) { subtype = synthesizeCallableProtocolFromFunctionType(evaluator, subtype, errorNode); diff --git a/packages/pyright-internal/src/tests/samples/typeNarrowingUsingBounds.py b/packages/pyright-internal/src/tests/samples/typeNarrowingUsingBounds.py index b6df0e847..c5abf9b6a 100644 --- a/packages/pyright-internal/src/tests/samples/typeNarrowingUsingBounds.py +++ b/packages/pyright-internal/src/tests/samples/typeNarrowingUsingBounds.py @@ -1,4 +1,4 @@ -from typing import Any, Never, assert_type, runtime_checkable, Protocol, Iterable, Iterator, MutableMapping, Reversible, Callable +from typing import Any, Never, assert_type, runtime_checkable, Protocol, Iterable, Iterator, MutableMapping, Reversible, Callable, TypeIs from types import FunctionType class Covariant[T]: @@ -134,4 +134,10 @@ def _(f: CallableProtocol[[], None]): def _(f: Callable[[int], str]): if isinstance(f, FunctionType): - assert_type(f, FunctionType) \ No newline at end of file + assert_type(f, FunctionType) + +def takes_arg(value: object) -> TypeIs[Callable[[int], None]]: ... + +def _(value: Callable[[], None] | Callable[[int], None]): + if takes_arg(value): + assert_type(value, Callable[[int], None]) \ No newline at end of file diff --git a/packages/pyright-internal/src/tests/samples/typeNarrowingUsingBoundsDisabled.py b/packages/pyright-internal/src/tests/samples/typeNarrowingUsingBoundsDisabled.py index 4ffd3bb05..9860d4358 100644 --- a/packages/pyright-internal/src/tests/samples/typeNarrowingUsingBoundsDisabled.py +++ b/packages/pyright-internal/src/tests/samples/typeNarrowingUsingBoundsDisabled.py @@ -1,4 +1,4 @@ -from typing import Any, assert_type, runtime_checkable, Protocol, Iterable, Iterator, MutableMapping, Reversible, Callable +from typing import Any, assert_type, runtime_checkable, Protocol, Iterable, Iterator, MutableMapping, Reversible, Callable, TypeIs from types import FunctionType @@ -132,4 +132,10 @@ def _(f: CallableProtocol[[], None]): def _(f: Callable[[int], str]): if isinstance(f, FunctionType): - assert_type(f, FunctionType) \ No newline at end of file + assert_type(f, FunctionType) + +def takes_arg(value: object) -> TypeIs[Callable[[int], None]]: ... + +def _(value: Callable[[], None] | Callable[[int], None]): + if takes_arg(value): + assert_type(value, Callable[[int], None]) \ No newline at end of file