diff --git a/packages/pyright-internal/src/analyzer/checker.ts b/packages/pyright-internal/src/analyzer/checker.ts index 7e1b83fda91b..21c1e5170dbb 100644 --- a/packages/pyright-internal/src/analyzer/checker.ts +++ b/packages/pyright-internal/src/analyzer/checker.ts @@ -3797,7 +3797,9 @@ export class Checker extends ParseTreeWalker { break; case TypeCategory.Function: - isSupported = TypeBase.isInstantiable(subtype); + if (!TypeBase.isInstantiable(subtype) || subtype.isCallableWithTypeArgs) { + isSupported = false; + } break; case TypeCategory.Union: diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index 8e10fae3f3f0..e20431c8f04f 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -14255,6 +14255,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions functionType.details.typeVarScopeId = ParseTreeUtils.getScopeIdForNode(errorNode); if (typeArgs && typeArgs.length > 0) { + functionType.isCallableWithTypeArgs = true; + if (typeArgs[0].typeList) { const typeList = typeArgs[0].typeList; let sawUnpacked = false; diff --git a/packages/pyright-internal/src/analyzer/types.ts b/packages/pyright-internal/src/analyzer/types.ts index bfc606d51112..233ff8ad4b3a 100644 --- a/packages/pyright-internal/src/analyzer/types.ts +++ b/packages/pyright-internal/src/analyzer/types.ts @@ -1454,6 +1454,11 @@ export interface FunctionType extends TypeBase { // for that call and any other signatures that were passed as // arguments to it. trackedSignatures?: SignatureWithOffsets[]; + + // If this function is created with a "Callable" annotation with + // type arguments? This allows us to detect and report an error + // when this is used in an isinstance call. + isCallableWithTypeArgs?: boolean; } export namespace FunctionType { diff --git a/packages/pyright-internal/src/tests/samples/isinstance3.py b/packages/pyright-internal/src/tests/samples/isinstance3.py index 8e40b5657cc3..05c1240edded 100644 --- a/packages/pyright-internal/src/tests/samples/isinstance3.py +++ b/packages/pyright-internal/src/tests/samples/isinstance3.py @@ -4,7 +4,7 @@ from abc import abstractmethod -from typing import Any, Generic, Sequence, Tuple, Type, TypeVar, Union +from typing import Any, Callable, Generic, Sequence, Tuple, Type, TypeVar, Union _T = TypeVar("_T", int, str) @@ -55,3 +55,15 @@ def execute(self, var: Union[_T, Tuple[_T]]) -> None: def func1(exceptions: Sequence[type[BaseException]], exception: Exception): return isinstance(exception, tuple(exceptions)) + + +if isinstance(a, Callable): + ... + +# This should generate an error because a subscripted Callable +# will result in a runtime exception. +if isinstance(a, Callable[[], Any]): + ... + +if isinstance(a, type(len)): + ... diff --git a/packages/pyright-internal/src/tests/typeEvaluator2.test.ts b/packages/pyright-internal/src/tests/typeEvaluator2.test.ts index 9745e6dcaf4d..db701bcc5425 100644 --- a/packages/pyright-internal/src/tests/typeEvaluator2.test.ts +++ b/packages/pyright-internal/src/tests/typeEvaluator2.test.ts @@ -285,11 +285,11 @@ test('isInstance3', () => { configOptions.defaultPythonVersion = PythonVersion.V3_9; const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['isinstance3.py'], configOptions); - TestUtils.validateResults(analysisResults1, 1); + TestUtils.validateResults(analysisResults1, 2); configOptions.defaultPythonVersion = PythonVersion.V3_10; const analysisResults2 = TestUtils.typeAnalyzeSampleFiles(['isinstance3.py'], configOptions); - TestUtils.validateResults(analysisResults2, 1); + TestUtils.validateResults(analysisResults2, 2); }); test('isInstance4', () => {