Skip to content

Commit

Permalink
Fixed bug in type evaluator where it not properly handling properties…
Browse files Browse the repository at this point in the history
… within protocol classes.
  • Loading branch information
msfterictraut committed Feb 15, 2020
1 parent 094bd03 commit b623cb0
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 0 deletions.
26 changes: 26 additions & 0 deletions server/src/analyzer/typeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')) {
Expand Down
6 changes: 6 additions & 0 deletions server/src/tests/checker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']);

Expand Down
41 changes: 41 additions & 0 deletions server/src/tests/samples/protocol3.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit b623cb0

Please sign in to comment.