Skip to content

Commit

Permalink
Updated heuristics for constructor evaluation to accommodate a wider …
Browse files Browse the repository at this point in the history
…range of types returned by the `__new__` method. Previously, if the `__new__` return type was anything other than a class instance, the heuristics assumed it wasn't intended and assumed that `__new__` returned an instance of its class, as is usually the case. This addresses #6303. (#6305)
  • Loading branch information
erictraut authored Nov 2, 2023
1 parent 5adbd8d commit 6f9445f
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 7 deletions.
10 changes: 5 additions & 5 deletions packages/pyright-internal/src/analyzer/constructors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,9 @@ function validateNewAndInitMethods(
// (cls, *args, **kwargs) -> Self, allow the __init__ method to
// determine the specialized type of the class.
newMethodReturnType = ClassType.cloneAsInstance(type);
} else if (!isNever(newMethodReturnType) && !isClassInstance(newMethodReturnType)) {
// If the __new__ method returns something other than an object or
// NoReturn, we'll ignore its return type and assume that it
// returns Self.
} else if (isAnyOrUnknown(newMethodReturnType)) {
// If the __new__ method returns Any or Unknown, we'll ignore its return
// type and assume that it returns Self.
newMethodReturnType = applySolvedTypeVars(
ClassType.cloneAsInstance(type),
new TypeVarContext(getTypeVarScopeId(type)),
Expand All @@ -251,7 +250,8 @@ function validateNewAndInitMethods(
if (
!argumentErrors &&
!isNever(newMethodReturnType) &&
!shouldSkipInitEvaluation(evaluator, type, newMethodReturnType)
!shouldSkipInitEvaluation(evaluator, type, newMethodReturnType) &&
isClassInstance(newMethodReturnType)
) {
// If the __new__ method returned the same type as the class it's constructing
// but didn't supply solved type arguments, we'll ignore its specialized return
Expand Down
24 changes: 22 additions & 2 deletions packages/pyright-internal/src/tests/samples/constructor7.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,30 @@
# a type that differs from the class that contains it.


class HelloWorld:
from typing import Callable, ParamSpec, TypeVar


class ClassA:
def __new__(cls) -> str:
return "Hello World"


v1 = HelloWorld()
v1 = ClassA()
reveal_type(v1, expected_text="str")


_P = ParamSpec("_P")
_R = TypeVar("_R")


def func1(a: int) -> int:
return a + 1


class ClassB:
def __new__(cls, func: Callable[_P, _R]) -> Callable[_P, _R]:
return func


v2 = ClassB(func1)
reveal_type(v2, expected_text="(a: int) -> int")

0 comments on commit 6f9445f

Please sign in to comment.