Skip to content

B902: don't false-positive on metaclasses with a dotted base#559

Merged
cooperlees merged 1 commit into
PyCQA:mainfrom
StressTestor:fix/b902-dotted-metaclass-base
Jun 18, 2026
Merged

B902: don't false-positive on metaclasses with a dotted base#559
cooperlees merged 1 commit into
PyCQA:mainfrom
StressTestor:fix/b902-dotted-metaclass-base

Conversation

@StressTestor

Copy link
Copy Markdown
Contributor

what

B902 raises a false positive on a metaclass whose base is written with a dotted name, e.g.

import abc

class CustomMeta(abc.ABCMeta):
    def __new__(mcs, name, bases, ns): ...  # flagged, but mcs is correct here

This is issue #411.

why

The B902 metaclass check builds the set of base names from bare ast.Name nodes only:

bases = {b.id for b in cls.bases if isinstance(b, ast.Name)}

A dotted base like abc.ABCMeta is an ast.Attribute, so it gets dropped. The class is then treated as a plain class and its cls/metacls/mcs first arguments are flagged. #415 added detection for the bare type/ABCMeta/EnumMeta names back in 2023; the dotted form was the remaining gap, and #411 is still open.

fix

Also take the attribute name from dotted ast.Attribute bases:

bases = {
    b.id if isinstance(b, ast.Name) else b.attr
    for b in cls.bases
    if isinstance(b, (ast.Name, ast.Attribute))
}

Scoped to direct bases (transitive base chains stay out of scope). This is the same name-based heuristic the check already used, so dotted bases now behave like the bare ones.

tests

tests/eval_files/b902.py gains dotted abc.ABCMeta and enum.EnumMeta metaclasses (clean), plus a precision case where a wrong first arg on a dotted metaclass is still reported as a metaclass instance method. Before the fix those clean fixtures emit spurious B902s and the precision error is lost; after, they are correct.

Two classes in tests/eval_files/b024.py (multi_super_1, non_keyword_abcmeta_2) directly subclass abc.ABCMeta and used self, which was only valid while the bug hid their metaclass-ness. They now use cls, consistent with the bare non_keyword_abcmeta_1 sibling in the same file.

Also updated the B902 README entry, which only mentioned type, to cover ABCMeta/EnumMeta and dotted bases. Full suite green.

Fixes #411.

The B902 metaclass check only collected bare `ast.Name` bases, so a
metaclass written with a dotted base (`abc.ABCMeta`, `enum.EnumMeta`)
was treated as a plain class and its `cls`/`metacls`/`mcs` first
arguments were wrongly flagged. Collect the attribute name from dotted
`ast.Attribute` bases too, matching the existing bare-name handling.

The b024 fixtures `multi_super_1` and `non_keyword_abcmeta_2` used
`self` on what are actually metaclasses (only valid while the bug hid
them); they now use `cls`, consistent with the bare `non_keyword_abcmeta_1`
sibling in the same file.

Fixes PyCQA#411.

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.

Pull request overview

Fixes a B902 false-positive where custom metaclasses inheriting from dotted standard-library metaclass names (e.g. abc.ABCMeta, enum.EnumMeta) were not recognized as metaclasses, causing valid first-argument names like mcs/metacls/cls to be incorrectly flagged.

Changes:

  • Extend B902 metaclass-base detection to include ast.Attribute bases (dotted bases), not just bare ast.Name.
  • Add/adjust eval fixtures to ensure dotted metaclass bases don’t trigger B902, while still reporting genuinely-wrong first arguments.
  • Update README B902 documentation and changelog entry to reflect the dotted-base behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
bugbear.py Updates B902 base-name extraction to include dotted bases (ast.Attribute) so metaclasses are correctly detected.
tests/eval_files/b902.py Adds fixtures covering dotted abc.ABCMeta / enum.EnumMeta metaclasses (no warnings) and a precision case (still warns).
tests/eval_files/b024.py Adjusts two fixtures to use cls so they remain clean under the corrected B902 behavior.
README.rst Updates B902 description and UNRELEASED changelog to document the dotted-base fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@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.

Many Thanks! Nice.

@cooperlees cooperlees merged commit 9694953 into PyCQA:main Jun 18, 2026
6 checks passed
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.

False-positive B902 on custom metaclass extending ABCMeta

3 participants