Skip to content

Commit

Permalink
Improve known metaclass base class detection
Browse files Browse the repository at this point in the history
We previously only looked for known metaclass base classes that were
inheriting using the simple `ast.Name` syntax:

    class Meta(type): pass
    class Meta(ABCMeta): pass

We weren't accounting for the `ast.Attribute` syntax:

    class Meta(abc.ABCMeta): pass

This second syntax will now also match `ABCMeta` and be recognized as a
metaclass base class.

We could alternatively add `abc.ABCMeta` to METACLASS_BASES and
construct the fully-qualified name of each base class that uses the
`ast.Attribute` syntax. The simpler approach used here is currently
unambiguous, so we'll start with this.
  • Loading branch information
jparise committed Feb 25, 2020
1 parent 002ad93 commit e5105c1
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 5 deletions.
15 changes: 10 additions & 5 deletions src/pep8ext_naming.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from collections import deque
from fnmatch import fnmatch
from functools import partial
from itertools import chain

from flake8_polyfill import options

Expand Down Expand Up @@ -212,11 +213,15 @@ def tag_class_functions(self, cls_node):
if isinstance(meth, ast.Name):
late_decoration[meth.id] = self.decorator_to_type[func_name]

cls_bases = [b for b in cls_node.bases if isinstance(b, ast.Name)]
# If this class inherits from `type`, it's a metaclass, and we'll
# consider all of it's methods to be classmethods.
ismetaclass = any(
name for name in cls_bases if name.id in METACLASS_BASES)
# If this class inherits from a known metaclass base class, it is
# itself a metaclass, and we'll consider all of it's methods to be
# classmethods.
bases = chain(
(b.id for b in cls_node.bases if isinstance(b, ast.Name)),
(b.attr for b in cls_node.bases if isinstance(b, ast.Attribute)),
)
ismetaclass = any(name for name in bases if name in METACLASS_BASES)

self.set_function_nodes_types(
iter_child_nodes(cls_node), ismetaclass, late_decoration)

Expand Down
7 changes: 7 additions & 0 deletions testsuite/N805.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import abc
from abc import ABCMeta

#: Okay
Expand Down Expand Up @@ -62,6 +63,12 @@ def __new__(cls, name, bases, attrs):
pass
def test(cls):
pass
#: Okay
class Meta(abc.ABCMeta):
def __new__(cls, name, bases, attrs):
pass
def test(cls):
pass
#: Okay(--classmethod-decorators=clazzy,cool)
class NewClassmethodDecorators(object):
@clazzy
Expand Down

0 comments on commit e5105c1

Please sign in to comment.