feat(lint): exempt TypedDict-annotated dict literals from LIT002 - #36813
feat(lint): exempt TypedDict-annotated dict literals from LIT002#36813mateo-berri wants to merge 6 commits into
Conversation
Greptile SummaryThe PR exempts annotated dict literals from LIT002 when their annotation resolves conservatively to a top-level, same-module, all-ReadOnly TypedDict.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains; the current implementation addresses the previously reported scope, rebinding, star-import, and nested-definition cases.
|
| Filename | Overview |
|---|---|
| scripts/check_type_discipline.py | Implements the conservative TypedDict-literal exemption and now prevents nested definitions from lending file-wide exemptions; no blocking failure remains. |
| tests/test_litellm/test_check_type_discipline.py | Adds focused regression coverage for valid exemptions and the previously reported scope, rebinding, and star-import failure modes. |
Reviews (6): Last reviewed commit: "fix(lint): limit the LIT002 exemption to..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
bugbot run |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit bbb5c94. Configure here.
|
Superceded by #36869 |
TLDR
Problem this solves:
x: MyTd = {...}but not theMyTd(...)spellingHow it solves it:
ReadOnlyUser Flow
Before: a contributor who builds a TypedDict payload as an annotated dict literal fails the lint gate and must respell or suppress
payload: Final[RequestKwargs] = {"model": m, "max_tokens": 128}withRequestKwargsa TypedDict defined in the same modulemake checkLIT002 mutable dict literaland the codebase count lands one over its ratcheted ceilingRequestKwargs(model=m, max_tokens=128)or add# mutable-ok: <reason>, though neither changes what the value is at runtimeAfter: the same literal passes the gate untouched
payload: Final[RequestKwargs] = {"model": m, "max_tokens": 128}linemake checkRequestKwargs(...)call, and the LIT002 count is unchangedRelevant issues
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Demo snippet
td_snip.py(a TypedDict-annotated dict literal, the pattern this PR legalizes):Before, at base commit 7fcca52:
After, at bbb5c94 the same command prints nothing and exits 0
Gates and tests at bbb5c94, on top of
litellm_internal_staging:False-negative audit at bbb5c94: running the base checker and this branch's checker over
litellm/ tests/ enterprise/ scripts/produces the identical set of 162,952 LIT002 lines, so no existing site gains the exemption today andmake lint-budget-updatemoves no ceilings ("Ratcheted LIT-rule limits down by 0"). Exempted sites are also frozen by construction: the annotation must name a module-top-level TypedDict bound exactly once whose every declared and inherited field isReadOnly, so a writable or# writable-ok'd TypedDict still countsType
🚄 Infrastructure
Caveats (if any)
from x import *anywhere in the file disqualifies every name in itx: mod.Td = {...}) never match, since they cannot name a local classx: "MyTd") are not recognizedFinal Attestation
Note
Low Risk
Lint-only change to an internal checker with conservative guards and broad negative tests; no runtime or auth/data paths touched.
Overview
LIT002 no longer flags dict literals on annotated assignments when they are the same one-shot shape as a
TypedDict(...)call. The type-discipline checker adds_typeddict_assigned_value_ids(plus helpers for annotation names and file-wide bindings) and unions that set into the existing LIT002 exemptions alongside annotation internals and freezing-wrapper arguments.The exemption is narrow: module-top-level TypedDict whose name is bound exactly once, every field (including inherited) is
ReadOnly[...], bare orFinal[...]annotation only—no dotted types, imported/nested TypedDicts, writable fields,from x import *, or rebound names. Nested mutables inside the literal still trip LIT002. Rule docs and the violation fix message now describe this path; tests cover happy paths and the conservative false-negative guards.Reviewed by Cursor Bugbot for commit bbb5c94. Bugbot is set up for automated code reviews on this repo. Configure here.