Skip to content

Conversation

@danparizher
Copy link
Contributor

Summary

Fixes the PLE1141 (dict-iter-missing-items) rule to allow fixes for empty dictionaries unless they have type annotations indicating 2-tuple keys. Previously, the fix was incorrectly suppressed for all empty dicts due to vacuous truth in the all() function.

Fixes #21289

Problem Analysis

The is_dict_key_tuple_with_two_elements function was designed to suppress the fix when a dictionary's keys are all 2-tuples, as unpacking tuple keys directly would change runtime behavior.

However, for empty dictionaries, iter_keys() returns an empty iterator, and all() on an empty iterator returns true (vacuous truth). This caused the function to incorrectly suppress fixes for empty dicts, even when there was no indication that future keys would be 2-tuples.

Approach

  1. Detect empty dictionaries: Added a check to identify when a dict literal has no keys.

  2. Handle annotated empty dicts: For empty dicts with type annotations:

    • Parse the annotation to check if it's dict[tuple[T1, T2], ...] where the tuple has exactly 2 elements
    • Support both PEP 484 (typing.Dict, typing.Tuple) and PEP 585 (dict, tuple) syntax
    • If tuple keys are detected, suppress the fix (correct behavior)
    • Otherwise, allow the fix
  3. Handle unannotated empty dicts: For empty dicts without annotations, allow the fix since there's no indication that keys will be 2-tuples.

  4. Preserve existing behavior: For non-empty dicts, the original logic is unchanged - check if all existing keys are 2-tuples.

The implementation includes helper functions:

  • is_annotation_dict_with_tuple_keys(): Checks if a type annotation specifies dict with tuple keys
  • is_tuple_type_with_two_elements(): Checks if a type expression represents a 2-tuple

Test cases were added to verify:

  • Empty dict without annotation triggers the error
  • Empty dict with dict[tuple[int, str], bool] suppresses the error
  • Empty dict with dict[str, int] triggers the error
  • Existing tests remain unchanged

@github-actions
Copy link
Contributor

github-actions bot commented Nov 6, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@ntBre ntBre added rule Implementing or modifying a lint rule preview Related to preview mode features labels Nov 6, 2025
@danparizher danparizher requested a review from ntBre November 11, 2025 22:20
Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is looking great! I just had one question about the expected result of the new test case.

if is_empty {
// For empty dicts, check type annotation
return annotation
.is_some_and(|annotation| is_annotation_dict_with_tuple_keys(annotation, semantic));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may have gotten this backwards, sorry. Should it be is_none_or instead of is_some_and? I'm wondering because I was expecting a diagnostic at line 46 of the new tests, but we're not getting one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that. The logic was correct (is_some_and is right), but I made it explicit for clarity.

For empty dicts:

  • With annotation indicating tuple keys → suppress fix (return true)
  • Without annotation → allow fix (return false)

The is_some_and chain was correct, but I refactored it to an explicit if let Some pattern to make the behavior clearer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, the two test cases were interfering because they both referred to the same annotation above. I think the is_some_and is clear then, I was mostly just confused about the end result. I'll put it back. Thanks for fixing the test!

@danparizher danparizher requested a review from ntBre November 20, 2025 00:56
Copy link
Contributor

@ntBre ntBre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@ntBre ntBre enabled auto-merge (squash) November 21, 2025 22:03
@ntBre ntBre merged commit ddc1417 into astral-sh:main Nov 21, 2025
36 checks passed
@danparizher danparizher deleted the fix-21289 branch November 21, 2025 23:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Related to preview mode features rule Implementing or modifying a lint rule

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PLE1141 fix is suppressed for empty dict

2 participants