Skip to content

Commit 355647c

Browse files
authored
One more case in utbot-python-types subtype checker (#2688)
A little more accurate SupportsCall
1 parent 921b69a commit 355647c

File tree

7 files changed

+53
-5
lines changed

7 files changed

+53
-5
lines changed

utbot-python-types/src/main/kotlin/org/utbot/python/newtyping/PythonTypeComparison.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,18 @@ class PythonSubtypeChecker(
406406

407407
val rightCallAttribute = rightCallAttributeAbstract as? FunctionType ?: return false
408408

409+
// __call__(*args): in this case check only return type
410+
if (leftMeta.argumentKinds.contains(PythonCallableTypeDescription.ArgKind.ARG_STAR)) {
411+
return PythonSubtypeChecker(
412+
left = leftAsFunctionType.returnValue,
413+
right = rightCallAttribute.returnValue,
414+
pythonTypeStorage,
415+
typeParameterCorrespondence,
416+
nextAssumingSubtypePairs,
417+
recursionDepth + 1
418+
).rightIsSubtypeOfLeft()
419+
}
420+
409421
if (rightCallAttribute.arguments.size != leftAsFunctionType.arguments.size)
410422
return false
411423
val leftBounded = leftAsFunctionType.getBoundedParameters()

utbot-python-types/src/test/kotlin/org/utbot/python/newtyping/PythonSubtypeCheckerTest.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,22 @@ internal class PythonSubtypeCheckerTest {
127127

128128
assertTrue(checkIfRightIsSubtypeOfLeft(abstractSetOfAny, setOfAny, pythonTypeStorage))
129129
}
130+
131+
@Test
132+
fun testSupportsCall() {
133+
val hasF = storage.definitions["subtypes"]!!["HasF"]!!.getUtBotType()
134+
val classS = storage.definitions["subtypes"]!!["S"]!!.getUtBotType()
135+
136+
assertTrue(checkIfRightIsSubtypeOfLeft(hasF, classS, pythonTypeStorage))
137+
}
138+
139+
@Test
140+
fun testSupportsSpecificCall() {
141+
val hasF = storage.definitions["subtypes"]!!["HasSpecificF"]!!.getUtBotType()
142+
val classS = storage.definitions["subtypes"]!!["S"]!!.getUtBotType()
143+
val classR = storage.definitions["subtypes"]!!["RImpl"]!!.getUtBotType()
144+
145+
assertFalse(checkIfRightIsSubtypeOfLeft(hasF, classS, pythonTypeStorage))
146+
assertTrue(checkIfRightIsSubtypeOfLeft(hasF, classR, pythonTypeStorage))
147+
}
130148
}

utbot-python-types/src/test/resources/annotation_tests.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

utbot-python-types/src/test/resources/boruvka.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

utbot-python-types/src/test/resources/import_test.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

utbot-python-types/src/test/resources/samples/subtypes.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,22 @@ def func_abs(x: SupportsAbs[T]):
5252
return abs(x)
5353

5454

55-
b: int = 10
55+
b: int = 10
56+
57+
58+
class SupportsCall(Protocol):
59+
def __call__(self, *args, **kwargs):
60+
...
61+
62+
63+
class HasF(Protocol):
64+
f: SupportsCall
65+
66+
67+
class SupportsSpecificCall(Protocol):
68+
def __call__(self, *args, **kwargs) -> HasF:
69+
...
70+
71+
72+
class HasSpecificF(Protocol):
73+
f: SupportsSpecificCall

utbot-python-types/src/test/resources/subtypes.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)