Fixed regression that caused false positive error when protocol match involved a property that returned a type that was generic.

This commit is contained in:
Eric Traut 2022-03-25 11:34:53 -06:00
parent 15f6164788
commit 177ea4d79d
3 changed files with 51 additions and 11 deletions

View File

@ -32,7 +32,13 @@ import {
Type,
UnknownType,
} from './types';
import { CanAssignFlags, computeMroLinearization, getTypeVarScopeId, isProperty } from './typeUtils';
import {
CanAssignFlags,
computeMroLinearization,
getTypeVarScopeId,
isProperty,
partiallySpecializeType,
} from './typeUtils';
import { TypeVarMap } from './typeVarMap';
export function validatePropertyMethod(evaluator: TypeEvaluator, method: FunctionType, errorNode: ParseNode) {
@ -89,6 +95,9 @@ export function createProperty(
const propertyObject = ClassType.cloneAsInstance(propertyClass);
propertyClass.isAsymmetricDescriptor = false;
// Update the __set__ and __delete__ methods if present.
updateGetSetDelMethodForClonedProperty(propertyObject, evaluator);
// Fill in the fget method.
const fgetSymbol = Symbol.createWithType(
SymbolFlags.ClassMember,
@ -100,9 +109,6 @@ export function createProperty(
propertyClass.details.flags |= ClassTypeFlags.ClassProperty;
}
// Update the __set__ and __set__ methods if present.
updateGetSetDelMethodForClonedProperty(propertyObject, evaluator);
// Fill in the __get__ method with an overload.
addGetMethodToPropertySymbolTable(propertyObject, fget);
@ -187,6 +193,9 @@ export function clonePropertyWithSetter(
}
});
// Update the __get__ and __delete__ methods if present.
updateGetSetDelMethodForClonedProperty(propertyObject, evaluator);
// Fill in the fset method.
const fsetSymbol = Symbol.createWithType(
SymbolFlags.ClassMember,
@ -194,9 +203,6 @@ export function clonePropertyWithSetter(
);
fields.set('fset', fsetSymbol);
// Update the __get__ and __delete__ methods if present.
updateGetSetDelMethodForClonedProperty(propertyObject, evaluator);
// Fill in the __set__ method.
addSetMethodToPropertySymbolTable(propertyObject, fset, evaluator);
@ -243,6 +249,9 @@ export function clonePropertyWithDeleter(
}
});
// Update the __get__ and __set__ methods if present.
updateGetSetDelMethodForClonedProperty(propertyObject, evaluator);
// Fill in the fdel method.
const fdelSymbol = Symbol.createWithType(
SymbolFlags.ClassMember,
@ -250,9 +259,6 @@ export function clonePropertyWithDeleter(
);
fields.set('fdel', fdelSymbol);
// Update the __get__ and __set__ methods if present.
updateGetSetDelMethodForClonedProperty(propertyObject, evaluator);
// Fill in the __delete__ method.
addDelMethodToPropertySymbolTable(propertyObject, fdel, evaluator);
@ -481,7 +487,7 @@ export function canAssignProperty(
if (destAccessType && isFunction(destAccessType)) {
const srcAccessSymbol = srcPropertyType.details.fields.get(accessorInfo.name);
const srcAccessType = srcAccessSymbol ? evaluator.getDeclaredTypeOfSymbol(srcAccessSymbol) : undefined;
let srcAccessType = srcAccessSymbol ? evaluator.getDeclaredTypeOfSymbol(srcAccessSymbol) : undefined;
if (!srcAccessType || !isFunction(srcAccessType)) {
if (diag) {
@ -491,6 +497,8 @@ export function canAssignProperty(
return;
}
srcAccessType = partiallySpecializeType(srcAccessType, srcClass) as FunctionType;
const boundDestAccessType = evaluator.bindFunctionToClassOrObject(
objectToBind,
destAccessType,

View File

@ -0,0 +1,26 @@
# This sample tests the special-case handling of properties that return
# generics within a protocol.
from functools import partial
from typing_extensions import Protocol, Self
from typing import Any, Callable, Type, TypeVar
_T = TypeVar("_T")
class Partial(Protocol[_T]):
@property
def func(self) -> Callable[..., _T]:
...
def __new__(
cls: Type[Self], __func: Callable[..., _T], *args: Any, **kwargs: Any
) -> Self:
...
def f(x: Partial[int]):
...
f(partial(int))

View File

@ -1054,6 +1054,12 @@ test('Protocol28', () => {
TestUtils.validateResults(analysisResults, 0);
});
test('Protocol29', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['protocol29.py']);
TestUtils.validateResults(analysisResults, 0);
});
test('TypedDict1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typedDict1.py']);