Skip to content

Commit

Permalink
Improved type narrowing for issubclass in the negative ("else") cas…
Browse files Browse the repository at this point in the history
…e when the subject type is `type` or `Any`. This addresses #9656. (#9704)
  • Loading branch information
erictraut authored Jan 14, 2025
1 parent 1593a7c commit 575c639
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 10 deletions.
26 changes: 16 additions & 10 deletions packages/pyright-internal/src/analyzer/typeGuards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1249,20 +1249,15 @@ function narrowTypeForInstanceOrSubclassInternal(
if (!isInstanceCheck) {
const isTypeInstance = isClassInstance(subtype) && ClassType.isBuiltIn(subtype, 'type');

// Handle metaclass instances specially.
if (isMetaclassInstance(subtype) && !isTypeInstance) {
// Handle metaclass instances specially.
adjFilterTypes = filterTypes.map((filterType) => convertToInstantiable(filterType));
} else {
const convSubtype = convertToInstance(subtype);
adjSubtype = convertToInstance(subtype);

// Handle type[Any] specially for this case.
if (isClassInstance(subtype) && ClassType.isBuiltIn(subtype, 'type') && isAnyOrUnknown(convSubtype)) {
adjSubtype = convertToInstance(evaluator.getObjectType());
} else {
adjSubtype = convSubtype;
if (!isAnyOrUnknown(subtype) || isPositiveTest) {
resultRequiresAdj = true;
}

resultRequiresAdj = true;
}
}

Expand All @@ -1276,7 +1271,18 @@ function narrowTypeForInstanceOrSubclassInternal(
errorNode
);

return resultRequiresAdj ? convertToInstantiable(narrowedResult) : narrowedResult;
if (!resultRequiresAdj) {
return narrowedResult;
}

if (isAnyOrUnknown(narrowedResult)) {
const typeClass = evaluator.getTypeClassType();
if (typeClass) {
return ClassType.specialize(ClassType.cloneAsInstance(typeClass), [narrowedResult]);
}
}

return convertToInstantiable(narrowedResult);
});

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,20 @@ def func6_2(ty: type[int] | int):
reveal_type(ty, expected_text="int")


def func6_3(ty: type):
if issubclass(ty, str):
reveal_type(ty, expected_text="type[str]")
else:
reveal_type(ty, expected_text="type[Unknown]")


def func6_4(ty: Any):
if issubclass(ty, str):
reveal_type(ty, expected_text="type[str]")
else:
reveal_type(ty, expected_text="Any")


# Test the handling of protocol classes that support runtime checking.
def func7(a: Union[list[int], int]):
if isinstance(a, Sized):
Expand Down

0 comments on commit 575c639

Please sign in to comment.