Skip to content

Commit

Permalink
Avoid stack exhaustion in superclass_names()
Browse files Browse the repository at this point in the history
Because this function is recursive, it can exhaust the stack if the
class hierarchy contains duplicate names. Avoid that using simple
memoization.

Also, these two supporting functions can be classmethods because they
don't use any instance-level state.
  • Loading branch information
jparise committed Aug 5, 2021
1 parent e830317 commit d3cdb47
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 16 deletions.
33 changes: 17 additions & 16 deletions src/pep8ext_naming.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,23 +296,24 @@ class ClassNameCheck(BaseASTCheck):
N801 = "class name '{name}' should use CapWords convention"
N818 = "exception name '{name}' should be named with an Error suffix"

def get_class_def(self, name, parents):
@classmethod
def get_classdef(cls, name, parents):
for parent in parents:
for definition in parent.body:
is_class_definition = isinstance(definition, ast.ClassDef)
if is_class_definition and definition.name == name:
return definition

def superclass_names(self, name, parents):
class_ids = set()
class_def = self.get_class_def(name, parents)
if not class_def:
return class_ids
for base in class_def.bases:
if hasattr(base, "id"):
class_ids.add(base.id)
class_ids.update(self.superclass_names(base.id, parents))
return class_ids
for node in parent.body:
if isinstance(node, ast.ClassDef) and node.name == name:
return node

@classmethod
def superclass_names(cls, name, parents, _names=None):
names = _names or set()
classdef = cls.get_classdef(name, parents)
if not classdef:
return names
for base in classdef.bases:
if isinstance(base, ast.Name) and base.id not in names:
names.add(base.id)
names.update(cls.superclass_names(base.id, parents, names))
return names

def visit_classdef(self, node, parents, ignore=None):
name = node.name
Expand Down
4 changes: 4 additions & 0 deletions testsuite/N818.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ class Mixin:
pass
class MixinActionClass(Mixin, MixinError):
pass
#: Okay
from decimal import Decimal
class Decimal(Decimal):
pass

0 comments on commit d3cdb47

Please sign in to comment.