mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-76900 Conformance test failure: typeddicts_readonly_consistency.py
GitOrigin-RevId: abb3b75e32241d3e31c1e965f71c7881b07986e3
This commit is contained in:
committed by
intellij-monorepo-bot
parent
87ccf48ad0
commit
a6c9ab63ba
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user