Skip to content

Commit d3cdb47

Browse files
committed
Avoid stack exhaustion in superclass_names()
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.
1 parent e830317 commit d3cdb47

File tree

2 files changed

+21
-16
lines changed

2 files changed

+21
-16
lines changed

src/pep8ext_naming.py

+17-16
Original file line numberDiff line numberDiff line change
@@ -296,23 +296,24 @@ class ClassNameCheck(BaseASTCheck):
296296
N801 = "class name '{name}' should use CapWords convention"
297297
N818 = "exception name '{name}' should be named with an Error suffix"
298298

299-
def get_class_def(self, name, parents):
299+
@classmethod
300+
def get_classdef(cls, name, parents):
300301
for parent in parents:
301-
for definition in parent.body:
302-
is_class_definition = isinstance(definition, ast.ClassDef)
303-
if is_class_definition and definition.name == name:
304-
return definition
305-
306-
def superclass_names(self, name, parents):
307-
class_ids = set()
308-
class_def = self.get_class_def(name, parents)
309-
if not class_def:
310-
return class_ids
311-
for base in class_def.bases:
312-
if hasattr(base, "id"):
313-
class_ids.add(base.id)
314-
class_ids.update(self.superclass_names(base.id, parents))
315-
return class_ids
302+
for node in parent.body:
303+
if isinstance(node, ast.ClassDef) and node.name == name:
304+
return node
305+
306+
@classmethod
307+
def superclass_names(cls, name, parents, _names=None):
308+
names = _names or set()
309+
classdef = cls.get_classdef(name, parents)
310+
if not classdef:
311+
return names
312+
for base in classdef.bases:
313+
if isinstance(base, ast.Name) and base.id not in names:
314+
names.add(base.id)
315+
names.update(cls.superclass_names(base.id, parents, names))
316+
return names
316317

317318
def visit_classdef(self, node, parents, ignore=None):
318319
name = node.name

testsuite/N818.py

+4
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,7 @@ class Mixin:
2828
pass
2929
class MixinActionClass(Mixin, MixinError):
3030
pass
31+
#: Okay
32+
from decimal import Decimal
33+
class Decimal(Decimal):
34+
pass

0 commit comments

Comments
 (0)