Fixed bug in type evaluator where it not properly handling properties within protocol classes.

This commit is contained in:
Eric Traut 2020-02-15 00:59:00 -07:00
parent 094bd03019
commit b623cb0389
3 changed files with 73 additions and 0 deletions

View File

@ -9033,6 +9033,32 @@ export function createTypeEvaluator(importLookup: ImportLookup): TypeEvaluator {
return typesAreConsistent;
}
// Handle property classes. They are special because each property
// class has a different source ID, so they wouldn't otherwise match.
// We need to see if the return types of the properties match.
if (ClassType.isPropertyClass(destType) && ClassType.isPropertyClass(srcType)) {
let typesAreConsistent = true;
const fgetDest = destType.details.fields.get('fget');
const fgetSrc = srcType.details.fields.get('fget');
if (fgetDest && fgetSrc) {
const fgetDestType = getDeclaredTypeOfSymbol(fgetDest);
const fgetSrcType = getDeclaredTypeOfSymbol(fgetSrc);
if (fgetDestType && fgetSrcType &&
fgetDestType.category === TypeCategory.Function &&
fgetSrcType.category === TypeCategory.Function) {
const fgetDestReturnType = getFunctionEffectiveReturnType(fgetDestType);
const fgetSrcReturnType = getFunctionEffectiveReturnType(fgetSrcType);
if (!canAssignType(fgetDestReturnType, fgetSrcReturnType, diag)) {
typesAreConsistent = false;
}
}
}
return typesAreConsistent;
}
// Special-case conversion for the "numeric tower".
if (ClassType.isBuiltIn(destType, 'float')) {
if (ClassType.isBuiltIn(srcType, 'int')) {

View File

@ -882,6 +882,12 @@ test('Protocol2', () => {
validateResults(analysisResults, 0);
});
test('Protocol3', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['protocol3.py']);
validateResults(analysisResults, 1);
});
test('TypedDict1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typedDict1.py']);

View File

@ -0,0 +1,41 @@
# This sample tests the assignment of protocols that
# include property declarations.
from typing import Protocol
class Foo1(Protocol):
@property
def batch_shape(self) -> int:
return 0
class MockFoo1:
def __init__(self, batch_shape: int):
self._batch_shape = batch_shape
@property
def batch_shape(self) -> int:
return self._batch_shape
# This should not generate an error.
d: Foo1 = MockFoo1(batch_shape=1)
class Foo2(Protocol):
@property
def batch_shape(self) -> int:
return 0
class MockFoo2:
def __init__(self, batch_shape: int):
self._batch_shape = batch_shape
@property
def batch_shape(self) -> float:
return self._batch_shape
# This should generate an error because the
# type of the batch_shape property is not compatible.
e: Foo2 = MockFoo2(batch_shape=1)