Skip to content

Commit

Permalink
Fixed recent regression that resulted in a false positive error when …
Browse files Browse the repository at this point in the history
…using bidirectional type inference for an assignment to a class-scoped variable where the annotated type is a descriptor. This addresses #5455.
  • Loading branch information
msfterictraut committed Jul 17, 2023
1 parent 7fcdc92 commit c8ea027
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 1 deletion.
5 changes: 4 additions & 1 deletion packages/pyright-internal/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2170,6 +2170,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
let classOrObjectBase: ClassType | undefined;
let memberAccessClass: Type | undefined;
let bindFunction = true;
let useDescriptorSetterType = false;

switch (expression.nodeType) {
case ParseNodeType.Name: {
Expand Down Expand Up @@ -2228,6 +2229,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (classMemberInfo?.isInstanceMember) {
bindFunction = false;
}

useDescriptorSetterType = true;
} else if (isInstantiableClass(baseType)) {
classMemberInfo = lookUpClassMember(
baseType,
Expand Down Expand Up @@ -2291,7 +2294,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
let declaredType = getDeclaredTypeOfSymbol(symbol)?.type;
if (declaredType) {
// If it's a descriptor, we need to get the setter type.
if (isClassInstance(declaredType)) {
if (useDescriptorSetterType && isClassInstance(declaredType)) {
const setterInfo = lookUpClassMember(declaredType, '__set__');
const setter = setterInfo ? getTypeOfMember(setterInfo) : undefined;
if (setterInfo && setter && isFunction(setter) && setter.details.parameters.length >= 3) {
Expand Down
25 changes: 25 additions & 0 deletions packages/pyright-internal/src/tests/samples/descriptor3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This sample tests that bidirectional type inference works when
# assigning to a class-scoped variable that is annotated with a
# descriptor. The setter type should not be used in this case.


from typing import Callable, Generic, TypeVar


T = TypeVar("T")


class Desc1(Generic[T]):
def __get__(self, instance: object | None, owner: type | None = None) -> list[T]:
...

def __set__(self, instance: object, value: list[T]) -> None:
...


def func1(factory: Callable[[], list[T]]) -> Desc1[T]:
...


class ClassA:
not_working: Desc1[int] = func1(list)
6 changes: 6 additions & 0 deletions packages/pyright-internal/src/tests/typeEvaluator1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1482,6 +1482,12 @@ test('Descriptor2', () => {
TestUtils.validateResults(analysisResults, 0);
});

test('Descriptor3', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['descriptor3.py']);

TestUtils.validateResults(analysisResults, 0);
});

test('Partial1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['partial1.py']);

Expand Down

0 comments on commit c8ea027

Please sign in to comment.