Fixed false positive error that occurs when using an issubclass type guard to narrow a type to an abstract base class. The resulting type should be instantiable without receiving a "cannot instantiate ABC" error.

This commit is contained in:
Eric Traut 2022-06-05 22:04:19 -07:00
parent 1b09c1aea0
commit e586d6f1f1
3 changed files with 41 additions and 2 deletions

View File

@ -1060,7 +1060,14 @@ function narrowTypeForIsInstance(
]) as ClassType;
}
filteredTypes.push(isInstanceCheck ? ClassType.cloneAsInstance(newClassType) : newClassType);
const newClassInstanceType = ClassType.cloneAsInstance(newClassType);
// If this is a issubclass check, we do a double conversion from instantiable
// to instance back to instantiable to make sure that the includeSubclasses flag
// gets cleared.
filteredTypes.push(
isInstanceCheck ? newClassInstanceType : ClassType.cloneAsInstantiable(newClassInstanceType)
);
}
}
} else if (isTypeVar(filterType) && TypeBase.isInstantiable(filterType)) {
@ -1198,7 +1205,14 @@ function narrowTypeForIsInstance(
combineTypes(classTypeList.map((classType) => convertToInstance(classType)))
);
} else {
anyOrUnknownSubstitutions.push(combineTypes(classTypeList));
// We perform a double conversion from instance to instantiable
// here to make sure that the includeSubclasses flag is cleared
// if it's a class.
anyOrUnknownSubstitutions.push(
combineTypes(
classTypeList.map((classType) => convertToInstantiable(convertToInstance(classType)))
)
);
}
anyOrUnknown.push(subtype);

View File

@ -0,0 +1,19 @@
# This sample tests the case where an issubclass type guard narrows
# to an abstract base class. When attempting to instantiate the
# class, there should be no "cannot instantiate ABC" error.
# pyright: strict
from abc import ABC, abstractmethod
from typing import Any
class Base(ABC):
@abstractmethod
def f(self) -> None:
...
def func(cls: Any):
assert issubclass(cls, Base)
_ = cls()

View File

@ -374,6 +374,12 @@ test('TypeNarrowingIsinstance7', () => {
TestUtils.validateResults(analysisResults, 0);
});
test('TypeNarrowingIsinstance8', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeNarrowingIsinstance8.py']);
TestUtils.validateResults(analysisResults, 0);
});
test('TypeNarrowingTupleLength1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeNarrowingTupleLength1.py']);