PY-76900 Conformance test failure: typeddicts_readonly_consistency.py

GitOrigin-RevId: abb3b75e32241d3e31c1e965f71c7881b07986e3
This commit is contained in:
Petr
2024-11-29 20:35:12 +00:00
committed by intellij-monorepo-bot
parent 87ccf48ad0
commit a6c9ab63ba
2 changed files with 62 additions and 9 deletions
@@ -136,7 +136,10 @@ class PyTypedDictType @JvmOverloads constructor(private val name: String,
*/
data class TypedDictFieldQualifiers(val isRequired: Boolean? = true, val isReadOnly: Boolean = false)
data class FieldTypeAndTotality(val value: PyExpression?, val type: PyType?, val qualifiers: TypedDictFieldQualifiers = TypedDictFieldQualifiers())
data class FieldTypeAndTotality(val value: PyExpression?, val type: PyType?, val qualifiers: TypedDictFieldQualifiers = TypedDictFieldQualifiers()) {
val isRequired: Boolean get() = qualifiers.isRequired ?: true
val isReadOnly: Boolean get() = qualifiers.isReadOnly
}
companion object {
@@ -302,16 +305,34 @@ class PyTypedDictType @JvmOverloads constructor(private val name: String,
return null
}
expected.fields.forEach {
val expectedTypeAndTotality = it.value
val actualTypeAndTotality = actual.fields[it.key]
if (actualTypeAndTotality == null
|| !strictUnionMatch(expectedTypeAndTotality.type, actualTypeAndTotality.type, context)
|| !strictUnionMatch(actualTypeAndTotality.type, expectedTypeAndTotality.type, context)
|| expectedTypeAndTotality.qualifiers.isRequired != actualTypeAndTotality.qualifiers.isRequired) {
for ((expectedKey, expectedField) in expected.fields) {
if (expectedField.isReadOnly && !expectedField.isRequired && expectedField.type?.name == PyNames.OBJECT) {
continue
}
val actualField = actual.fields[expectedKey]
if (actualField == null) {
return TypeCheckingResult(false)
}
if (!strictUnionMatch(expectedField.type, actualField.type, context)) {
return TypeCheckingResult(false)
}
if (!expectedField.isReadOnly) {
if (!(strictUnionMatch(actualField.type, expectedField.type, context) && !actualField.isReadOnly)) {
return TypeCheckingResult(false)
}
}
if (expectedField.isRequired) {
if (!actualField.isRequired) {
return TypeCheckingResult(false)
}
}
else {
if (!expectedField.isReadOnly) {
if (actualField.isRequired) {
return TypeCheckingResult(false)
}
}
}
}
return TypeCheckingResult(true)
}
@@ -1230,6 +1230,38 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase {
);
}
public void testTypedDictsReadonlyConsistency() {
doTestByText("""
from typing import TypedDict, Required, NotRequired, ReadOnly
class A1(TypedDict):
x: NotRequired[str]
class B1(TypedDict):
x: NotRequired[ReadOnly[str]]
class B2(TypedDict):
x: ReadOnly[NotRequired[str]]
class C(TypedDict):
x: Required[str]
def func1(b1: B1, b2: B2, c: C):
v1: A1 = <warning descr="Expected type 'A1', got 'B1' instead">b1</warning>
v2: A1 = <warning descr="Expected type 'A1', got 'B2' instead">b2</warning>
v3: B1 = c
class A2(TypedDict):
x: ReadOnly[NotRequired[object]]
class B3(TypedDict):
pass
def func2(b: B3):
a: A2 = b
""");
}
// PY-53611
public void testTypingRequiredTypeSpecificationsMultiFile() {
doMultiFileTest();