Skip to content

B008: resolve imported immutable calls - #574

Open
1678092075 wants to merge 1 commit into
PyCQA:mainfrom
1678092075:fix/b008-imported-immutable-calls
Open

B008: resolve imported immutable calls#574
1678092075 wants to merge 1 commit into
PyCQA:mainfrom
1678092075:fix/b008-imported-immutable-calls

Conversation

@1678092075

Copy link
Copy Markdown

Fixes #252.

Summary

  • resolve direct absolute module-level imports and aliases before matching B008's extend-immutable-calls
  • preserve existing source-path matches while invalidating imported bindings after a module-level redefinition
  • add focused eval coverage for imports, aliases, negative matches, and shadowing, plus an unreleased changelog entry

Validation

  • tox -e py313 — 81 passed, 1 skipped; 97% coverage
  • pre-commit run --all-files — isort, Black, flake8, and rstcheck passed
  • git diff --check

Scope

This intentionally resolves only direct absolute module-level imports. Nested/local, relative, and star-import resolution remain out of scope, and B039 behavior is unchanged.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Module-scope tracking mishandles annotation-only assignments and misses some walrus rebindings.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds import-aware resolution for B008 immutable-call matching and shadowing detection.

Changes:

  • Resolves absolute module imports and aliases.
  • Tracks module-level rebinding.
  • Adds evaluation tests and changelog entry.
File summaries
File Description
bugbear.py Implements import resolution and shadow tracking.
tests/eval_files/b008_extended.py Tests imports, aliases, and rebinding.
tests/eval_files/b008_extended_shadowing.py Tests unqualified configuration shadowing.
README.rst Adds the unreleased changelog entry.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread bugbear.py Outdated
Comment on lines +643 to +644
if self._b008_in_module_scope() and isinstance(node.ctx, (ast.Store, ast.Del)):
self._b008_shadow_imports((node.id,))
Comment thread bugbear.py Outdated
Comment on lines +643 to +644
if self._b008_in_module_scope() and isinstance(node.ctx, (ast.Store, ast.Del)):
self._b008_shadow_imports((node.id,))

@cooperlees cooperlees left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for this. This seems mostly there, but maybe we can add a test case + handle the walrus operator too? (if I'm understanding correctly copilots finding)

@1678092075
1678092075 force-pushed the fix/b008-imported-immutable-calls branch from a362dec to 4588df1 Compare September 8, 2026 04:25
@cooperlees
cooperlees requested a balanced review from Copilot September 8, 2026 15:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Import resolution misses class methods and several valid module-scope rebinding forms.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

bugbear.py:804

  • This recognizes a walrus only when it is inside exactly one comprehension context. In a nested module-level comprehension, the assignment-expression target still binds at module scope, but contexts contains the module plus both comprehensions, so the imported name remains trusted and later calls are incorrectly exempted. Check that the first context is the module and every remaining context is a comprehension instead of requiring len == 2.
            or (
                len(self.contexts) == 2
                and isinstance(self.contexts[0].node, ast.Module)
                and isinstance(self.contexts[1].node, COMPREHENSION_NODES)
            )

bugbear.py:2504

  • Skipping the entire lambda also skips its default expressions, even though those expressions execute immediately when the lambda is created. For a decorator such as @decorate(lambda value=(Depends := Other): value), the module binding is changed before the decorated function's defaults are evaluated, but the stale import mapping still exempts Depends(). Visit the lambda defaults while continuing to skip its deferred body.
class B008NamedExprFinder(NamedExprFinder):
    def visit_Lambda(self, node: ast.Lambda) -> None:  # noqa: B906
        pass
  • Files reviewed: 4/4 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread bugbear.py
Comment on lines 732 to +734
self.check_for_b903(node)
if self._b008_in_direct_module_child():
self._b008_shadow_named_expr_targets(node.decorator_list)
Comment thread bugbear.py
Comment on lines +910 to +914
if (
self.b008_b039_extend_immutable_calls
and self._b008_in_direct_module_child()
):
imported_names = self._b008_imports
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

B008: extend-immutable-calls does not work with imported function

3 participants