[pyupgrade] Fix false positive on relative imports from local .builtins module (UP029)
#21309
+19
−1
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
Fixes false positive where
UP029incorrectly flagged relative imports from local modules namedbuiltins(e.g.,from .builtins import next) as unnecessary builtin imports. The rule should only flag imports from Python's standardbuiltinsmodule, not from local modules.Fixes #21307
Problem Analysis
The
UP029rule checks for unnecessary imports from Python'sbuiltinsmodule. However, it was incorrectly flagging relative imports likefrom .builtins import nextas unnecessary, even though these are importing from a local module namedbuiltins, not Python's builtins module.The bug occurred because the rule only checked if the module name matched
"builtins"without distinguishing between:from builtins import next(should be flagged)from .builtins import next(should NOT be flagged)This caused issues in projects like
aioitertoolsthat reimplement builtins as async-compatible variants in a local.builtinsmodule.Approach
The fix adds a check for relative imports by:
level: u32parameter to theunnecessary_builtin_importfunction to detect import levellevel > 0(indicating a relative import) before checking the module namelevelparameter from the AST nodeThis ensures that relative imports are skipped entirely, as they're importing from local modules, not Python's builtins. Absolute imports continue to work correctly and are still flagged as expected.
The fix is minimal and surgical - it only adds the relative import check without modifying any existing logic for absolute imports.