Skip to content

Commit 0c76a13

Browse files
thomas-serre-sonarsourceclaude
authored andcommitted
SONARPY-5038 S5644: Allow item assignment on TypedDict instances
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> GitOrigin-RevId: 379673d528f95adc3ade63b4ecc4175ae30acf6a
1 parent e90f2bd commit 0c76a13

2 files changed

Lines changed: 25 additions & 0 deletions

File tree

‎python-checks/src/main/java/org/sonar/python/checks/ItemOperationsTypeCheck.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ public class ItemOperationsTypeCheck extends ItemOperationsType {
6262

6363
private static final TypeMatcher UNION_TYPE = TypeMatchers.isType("types.UnionType");
6464

65+
// TypedDict instances are dicts at runtime; typing._TypedDict omits the item assignment synthesized by type checkers
66+
private static final TypeMatcher TYPED_DICT_INSTANCE = TypeMatchers.isObjectInstanceOf("typing._TypedDict");
67+
6568
private static final Set<String> TYPING_LITERAL = Set.of("typing.Literal", "typing_extensions.Literal");
6669
private static final Set<String> TYPING_ANNOTATED = Set.of("typing.Annotated", "typing_extensions.Annotated");
6770

@@ -96,6 +99,10 @@ public boolean isValidSubscription(Expression subscriptionObject, String require
9699
}
97100
}
98101

102+
if (TYPED_DICT_INSTANCE.isTrueFor(subscriptionObject, context)) {
103+
return true;
104+
}
105+
99106
// Preserve the existing V1 check when V2 has no type information at all.
100107
if (!(subscriptionObject.typeV2() instanceof UnknownType)
101108
&& !KNOWN_DECORATOR_EFFECTS.isTrueFor(subscriptionObject, context)) {

‎python-checks/src/test/resources/checks/itemOperationsTypeCheck/itemOperations_setitem.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,21 @@ class FinalClass:
186186
pass
187187

188188
FinalClass[0] = 1 # Noncompliant
189+
190+
191+
def typed_dict_instances(customer, days_until_due):
192+
# TypedDict instances are dictionaries at runtime: typing._TypedDict omits the __setitem__ synthesized by type checkers
193+
import stripe
194+
from typing import TypedDict
195+
196+
list_params = stripe.params.InvoiceListParams(customer=customer, limit=1)
197+
list_params["total"] = 0
198+
invoice_params = stripe.params.InvoiceCreateParams(customer=customer)
199+
invoice_params["days_until_due"] = days_until_due
200+
del invoice_params["days_until_due"]
201+
202+
class LocalParams(TypedDict):
203+
total: int
204+
205+
local_params = LocalParams(total=1)
206+
local_params["total"] = 2

0 commit comments

Comments
 (0)