Skip to content
Snippets Groups Projects
Commit 52e67656 authored by Nick Drozd's avatar Nick Drozd Committed by Claudiu Popa
Browse files

Add type-specific get_children

get_children is elegant and flexible and slow.

    def get_children(self):
        for field in self._astroid_fields:
            attr = getattr(self, field)
            if attr is None:
                continue
            if isinstance(attr, (list, tuple)):
                for elt in attr:
                    yield elt
            else:
                yield attr

It iterates over a list, dynamically accesses attributes, does null
checks, and does type checking. This function gets called a lot, and
all that extra work is a real drag on performance.

In most cases there isn't any need to do any of these checks. Take an
Assign node for instance:

    def get_children(self):
        for elt in self.targets:
            yield elt

        yield self.value

It's known in advance that Assign nodes have a list of targets and a
value, so just yield those without checking anything.
parent 7ead5dd6
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -965,6 +965,9 @@ class _BaseContainer(mixins.ParentAssignTypeMixin,
:rtype: str
"""
 
def get_children(self):
yield from self.elts
 
class LookupMixIn(object):
"""Mixin to look up a name in the right scope."""
Loading
Loading
@@ -1170,6 +1173,9 @@ class AssignName(LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG):
 
super(AssignName, self).__init__(lineno, col_offset, parent)
 
def get_children(self):
yield from ()
 
class DelName(LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG):
"""Variation of :class:`ast.Delete` represention deletion of a name.
Loading
Loading
@@ -1207,6 +1213,9 @@ class DelName(LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG):
 
super(DelName, self).__init__(lineno, col_offset, parent)
 
def get_children(self):
yield from ()
 
class Name(LookupMixIn, NodeNG):
"""Class representing an :class:`ast.Name` node.
Loading
Loading
@@ -1247,6 +1256,9 @@ class Name(LookupMixIn, NodeNG):
 
super(Name, self).__init__(lineno, col_offset, parent)
 
def get_children(self):
yield from ()
 
class Arguments(mixins.AssignTypeMixin, NodeNG):
"""Class representing an :class:`ast.arguments` node.
Loading
Loading
@@ -1489,16 +1501,28 @@ class Arguments(mixins.AssignTypeMixin, NodeNG):
return None, None
 
def get_children(self):
"""Get the child nodes below this node.
yield from self.args or ()
 
This skips over `None` elements in :attr:`kw_defaults`.
yield from self.defaults
yield from self.kwonlyargs
 
:returns: The children.
:rtype: iterable(NodeNG)
"""
for child in super(Arguments, self).get_children():
if child is not None:
yield child
if self.varargannotation is not None:
yield self.varargannotation
if self.kwargannotation is not None:
yield self.kwargannotation
for elt in self.kw_defaults:
if elt is not None:
yield elt
for elt in self.annotations:
if elt is not None:
yield elt
for elt in self.kwonlyargs_annotations:
if elt is not None:
yield elt
 
 
def _find_arg(argname, args, rec=False):
Loading
Loading
@@ -1587,6 +1611,9 @@ class AssignAttr(mixins.ParentAssignTypeMixin, NodeNG):
"""
self.expr = expr
 
def get_children(self):
yield self.expr
 
class Assert(Statement):
"""Class representing an :class:`ast.Assert` node.
Loading
Loading
@@ -1621,6 +1648,12 @@ class Assert(Statement):
self.fail = fail
self.test = test
 
def get_children(self):
yield self.test
if self.fail is not None:
yield self.fail
 
class Assign(mixins.AssignTypeMixin, Statement):
"""Class representing an :class:`ast.Assign` node.
Loading
Loading
@@ -1656,6 +1689,11 @@ class Assign(mixins.AssignTypeMixin, Statement):
self.targets = targets
self.value = value
 
def get_children(self):
yield from self.targets
yield self.value
 
class AnnAssign(mixins.AssignTypeMixin, Statement):
"""Class representing an :class:`ast.AnnAssign` node.
Loading
Loading
@@ -1711,6 +1749,13 @@ class AnnAssign(mixins.AssignTypeMixin, Statement):
self.value = value
self.simple = simple
 
def get_children(self):
yield self.target
yield self.annotation
if self.value is not None:
yield self.value
 
class AugAssign(mixins.AssignTypeMixin, Statement):
"""Class representing an :class:`ast.AugAssign` node.
Loading
Loading
@@ -1792,6 +1837,10 @@ class AugAssign(mixins.AssignTypeMixin, Statement):
except exceptions.InferenceError:
return []
 
def get_children(self):
yield self.target
yield self.value
 
class Repr(NodeNG):
"""Class representing an :class:`ast.Repr` node.
Loading
Loading
@@ -1896,6 +1945,10 @@ class BinOp(NodeNG):
except exceptions.InferenceError:
return []
 
def get_children(self):
yield self.left
yield self.right
 
class BoolOp(NodeNG):
"""Class representing an :class:`ast.BoolOp` node.
Loading
Loading
@@ -1945,6 +1998,9 @@ class BoolOp(NodeNG):
"""
self.values = values
 
def get_children(self):
yield from self.values
 
class Break(Statement):
"""Class representing an :class:`ast.Break` node.
Loading
Loading
@@ -1954,6 +2010,9 @@ class Break(Statement):
<Break l.1 at 0x7f23b2e9e5c0>
"""
 
def get_children(self):
yield from ()
 
class Call(NodeNG):
"""Class representing an :class:`ast.Call` node.
Loading
Loading
@@ -2015,6 +2074,13 @@ class Call(NodeNG):
keywords = self.keywords or []
return [keyword for keyword in keywords if keyword.arg is None]
 
def get_children(self):
yield self.func
yield from self.args
yield from self.keywords or ()
 
class Compare(NodeNG):
"""Class representing an :class:`ast.Compare` node.
Loading
Loading
@@ -2183,6 +2249,12 @@ class Comprehension(NodeNG):
 
return stmts, False
 
def get_children(self):
yield self.target
yield self.iter
yield from self.ifs
 
class Const(NodeNG, bases.Instance):
"""Class representing any constant including num, str, bool, None, bytes.
Loading
Loading
@@ -2295,6 +2367,9 @@ class Const(NodeNG, bases.Instance):
"""
return bool(self.value)
 
def get_children(self):
yield from ()
 
class Continue(Statement):
"""Class representing an :class:`ast.Continue` node.
Loading
Loading
@@ -2304,6 +2379,9 @@ class Continue(Statement):
<Continue l.1 at 0x7f23b2e35588>
"""
 
def get_children(self):
yield from ()
 
class Decorators(NodeNG):
"""A node representing a list of decorators.
Loading
Loading
@@ -2345,6 +2423,9 @@ class Decorators(NodeNG):
# skip the function node to go directly to the upper level scope
return self.parent.parent.scope()
 
def get_children(self):
yield from self.nodes
 
class DelAttr(mixins.ParentAssignTypeMixin, NodeNG):
"""Variation of :class:`ast.Delete` representing deletion of an attribute.
Loading
Loading
@@ -2394,6 +2475,9 @@ class DelAttr(mixins.ParentAssignTypeMixin, NodeNG):
"""
self.expr = expr
 
def get_children(self):
yield self.expr
 
class Delete(mixins.AssignTypeMixin, Statement):
"""Class representing an :class:`ast.Delete` node.
Loading
Loading
@@ -2419,6 +2503,9 @@ class Delete(mixins.AssignTypeMixin, Statement):
"""
self.targets = targets
 
def get_children(self):
yield from self.targets
 
class Dict(NodeNG, bases.Instance):
"""Class representing an :class:`ast.Dict` node.
Loading
Loading
@@ -2579,6 +2666,9 @@ class Expr(Statement):
"""
self.value = value
 
def get_children(self):
yield self.value
 
class Ellipsis(NodeNG): # pylint: disable=redefined-builtin
"""Class representing an :class:`ast.Ellipsis` node.
Loading
Loading
@@ -2599,12 +2689,18 @@ class Ellipsis(NodeNG): # pylint: disable=redefined-builtin
"""
return True
 
def get_children(self):
yield from ()
 
class EmptyNode(NodeNG):
"""Holds an arbitrary object in the :attr:`LocalsDictNodeNG.locals`."""
 
object = None
 
def get_children(self):
yield from ()
 
class ExceptHandler(mixins.AssignTypeMixin, Statement):
"""Class representing an :class:`ast.ExceptHandler`. node.
Loading
Loading
@@ -2639,6 +2735,15 @@ class ExceptHandler(mixins.AssignTypeMixin, Statement):
:type: list(NodeNG) or None
"""
 
def get_children(self):
if self.type is not None:
yield self.type
if self.name is not None:
yield self.name
yield from self.body
# pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
def postinit(self, type=None, name=None, body=None):
"""Do some setup after initialisation.
Loading
Loading
@@ -2820,6 +2925,13 @@ class For(mixins.BlockRangeMixIn, mixins.AssignTypeMixin, Statement):
"""
return self.iter.tolineno
 
def get_children(self):
yield self.target
yield self.iter
yield from self.body
yield from self.orelse
 
class AsyncFor(For):
"""Class representing an :class:`ast.AsyncFor` node.
Loading
Loading
@@ -2871,6 +2983,9 @@ class Await(NodeNG):
"""
self.value = value
 
def get_children(self):
yield self.value
 
class ImportFrom(mixins.ImportFromMixin, Statement):
"""Class representing an :class:`ast.ImportFrom` node.
Loading
Loading
@@ -2931,6 +3046,9 @@ class ImportFrom(mixins.ImportFromMixin, Statement):
 
super(ImportFrom, self).__init__(lineno, col_offset, parent)
 
def get_children(self):
yield from ()
 
class Attribute(NodeNG):
"""Class representing an :class:`ast.Attribute` node."""
Loading
Loading
@@ -2973,6 +3091,9 @@ class Attribute(NodeNG):
"""
self.expr = expr
 
def get_children(self):
yield self.expr
 
class Global(Statement):
"""Class representing an :class:`ast.Global` node.
Loading
Loading
@@ -3009,6 +3130,9 @@ class Global(Statement):
def _infer_name(self, frame, name):
return name
 
def get_children(self):
yield from ()
 
class If(mixins.BlockRangeMixIn, Statement):
"""Class representing an :class:`ast.If` node.
Loading
Loading
@@ -3075,6 +3199,12 @@ class If(mixins.BlockRangeMixIn, Statement):
return self._elsed_block_range(lineno, self.orelse,
self.body[0].fromlineno - 1)
 
def get_children(self):
yield self.test
yield from self.body
yield from self.orelse
 
class IfExp(NodeNG):
"""Class representing an :class:`ast.IfExp` node.
Loading
Loading
@@ -3116,6 +3246,12 @@ class IfExp(NodeNG):
self.body = body
self.orelse = orelse
 
def get_children(self):
yield self.test
yield self.body
yield self.orelse
class Import(mixins.ImportFromMixin, Statement):
"""Class representing an :class:`ast.Import` node.
 
Loading
Loading
@@ -3151,6 +3287,9 @@ class Import(mixins.ImportFromMixin, Statement):
 
super(Import, self).__init__(lineno, col_offset, parent)
 
def get_children(self):
yield from ()
 
class Index(NodeNG):
"""Class representing an :class:`ast.Index` node.
Loading
Loading
@@ -3178,6 +3317,9 @@ class Index(NodeNG):
"""
self.value = value
 
def get_children(self):
yield self.value
 
class Keyword(NodeNG):
"""Class representing an :class:`ast.keyword` node.
Loading
Loading
@@ -3227,6 +3369,9 @@ class Keyword(NodeNG):
"""
self.value = value
 
def get_children(self):
yield self.value
 
class List(_BaseContainer):
"""Class representing an :class:`ast.List` node.
Loading
Loading
@@ -3318,6 +3463,9 @@ class Nonlocal(Statement):
def _infer_name(self, frame, name):
return name
 
def get_children(self):
yield from ()
 
class Pass(Statement):
"""Class representing an :class:`ast.Pass` node.
Loading
Loading
@@ -3327,6 +3475,9 @@ class Pass(Statement):
<Pass l.1 at 0x7f23b2e9e748>
"""
 
def get_children(self):
yield from ()
 
class Print(Statement):
"""Class representing an :class:`ast.Print` node.
Loading
Loading
@@ -3428,6 +3579,13 @@ class Raise(Statement):
return True
return False
 
def get_children(self):
if self.exc is not None:
yield self.exc
if self.cause is not None:
yield self.cause
 
class Return(Statement):
"""Class representing an :class:`ast.Return` node.
Loading
Loading
@@ -3451,6 +3609,10 @@ class Return(Statement):
"""
self.value = value
 
def get_children(self):
if self.value is not None:
yield self.value
 
class Set(_BaseContainer):
"""Class representing an :class:`ast.Set` node.
Loading
Loading
@@ -3554,6 +3716,16 @@ class Slice(NodeNG):
def getattr(self, attrname, context=None):
return self._proxied.getattr(attrname, context)
 
def get_children(self):
if self.lower is not None:
yield self.lower
if self.step is not None:
yield self.step
if self.upper is not None:
yield self.upper
 
class Starred(mixins.ParentAssignTypeMixin, NodeNG):
"""Class representing an :class:`ast.Starred` node.
Loading
Loading
@@ -3602,6 +3774,9 @@ class Starred(mixins.ParentAssignTypeMixin, NodeNG):
"""
self.value = value
 
def get_children(self):
yield self.value
 
class Subscript(NodeNG):
"""Class representing an :class:`ast.Subscript` node.
Loading
Loading
@@ -3660,6 +3835,10 @@ class Subscript(NodeNG):
self.value = value
self.slice = slice
 
def get_children(self):
yield self.value
yield self.slice
 
class TryExcept(mixins.BlockRangeMixIn, Statement):
"""Class representing an :class:`ast.TryExcept` node.
Loading
Loading
@@ -3729,6 +3908,12 @@ class TryExcept(mixins.BlockRangeMixIn, Statement):
last = exhandler.body[0].fromlineno - 1
return self._elsed_block_range(lineno, self.orelse, last)
 
def get_children(self):
yield from self.body
yield from self.handlers or ()
yield from self.orelse or ()
 
class TryFinally(mixins.BlockRangeMixIn, Statement):
"""Class representing an :class:`ast.TryFinally` node.
Loading
Loading
@@ -3785,6 +3970,10 @@ class TryFinally(mixins.BlockRangeMixIn, Statement):
return child.block_range(lineno)
return self._elsed_block_range(lineno, self.finalbody)
 
def get_children(self):
yield from self.body
yield from self.finalbody
 
class Tuple(_BaseContainer):
"""Class representing an :class:`ast.Tuple` node.
Loading
Loading
@@ -3903,6 +4092,9 @@ class UnaryOp(NodeNG):
except exceptions.InferenceError:
return []
 
def get_children(self):
yield self.operand
 
class While(mixins.BlockRangeMixIn, Statement):
"""Class representing an :class:`ast.While` node.
Loading
Loading
@@ -3967,6 +4159,12 @@ class While(mixins.BlockRangeMixIn, Statement):
"""
return self. _elsed_block_range(lineno, self.orelse)
 
def get_children(self):
yield self.test
yield from self.body
yield from self.orelse
 
class With(mixins.BlockRangeMixIn, mixins.AssignTypeMixin, Statement):
"""Class representing an :class:`ast.With` node.
Loading
Loading
@@ -4051,6 +4249,10 @@ class Yield(NodeNG):
"""
self.value = value
 
def get_children(self):
if self.value is not None:
yield self.value
 
class YieldFrom(Yield):
"""Class representing an :class:`ast.YieldFrom` node."""
Loading
Loading
@@ -4059,6 +4261,9 @@ class YieldFrom(Yield):
class DictUnpack(NodeNG):
"""Represents the unpacking of dicts into dicts using :pep:`448`."""
 
def get_children(self):
yield from ()
 
class FormattedValue(NodeNG):
"""Class representing an :class:`ast.FormattedValue` node.
Loading
Loading
@@ -4110,6 +4315,12 @@ class FormattedValue(NodeNG):
self.conversion = conversion
self.format_spec = format_spec
 
def get_children(self):
yield self.value
if self.format_spec is not None:
yield self.format_spec
 
class JoinedStr(NodeNG):
"""Represents a list of string expressions to be joined.
Loading
Loading
@@ -4134,6 +4345,9 @@ class JoinedStr(NodeNG):
"""
self.values = values
 
def get_children(self):
yield from self.values
 
class Unknown(mixins.AssignTypeMixin, NodeNG):
"""This node represents a node in a constructed AST where
Loading
Loading
Loading
Loading
@@ -696,6 +696,10 @@ class Module(LocalsDictNodeNG):
"""
return True
 
def get_children(self):
for elt in self.body:
yield elt
 
class ComprehensionScope(LocalsDictNodeNG):
"""Scoping for different types of comprehensions."""
Loading
Loading
@@ -777,6 +781,11 @@ class GeneratorExp(ComprehensionScope):
"""
return True
 
def get_children(self):
yield self.elt
yield from self.generators
 
class DictComp(ComprehensionScope):
"""Class representing an :class:`ast.DictComp` node.
Loading
Loading
@@ -851,6 +860,12 @@ class DictComp(ComprehensionScope):
"""
return util.Uninferable
 
def get_children(self):
yield self.key
yield self.value
yield from self.generators
 
class SetComp(ComprehensionScope):
"""Class representing an :class:`ast.SetComp` node.
Loading
Loading
@@ -916,6 +931,11 @@ class SetComp(ComprehensionScope):
"""
return util.Uninferable
 
def get_children(self):
yield self.elt
yield from self.generators
 
class _ListComp(node_classes.NodeNG):
"""Class representing an :class:`ast.ListComp` node.
Loading
Loading
@@ -957,6 +977,11 @@ class _ListComp(node_classes.NodeNG):
"""
return util.Uninferable
 
def get_children(self):
yield self.elt
yield from self.generators
 
class ListComp(_ListComp, ComprehensionScope):
"""Class representing an :class:`ast.ListComp` node.
Loading
Loading
@@ -1174,6 +1199,10 @@ class Lambda(mixins.FilterStmtsMixin, LocalsDictNodeNG):
"""
return True
 
def get_children(self):
yield self.args
yield self.body
 
class FunctionDef(node_classes.Statement, Lambda):
"""Class representing an :class:`ast.FunctionDef`.
Loading
Loading
@@ -1554,6 +1583,18 @@ class FunctionDef(node_classes.Statement, Lambda):
"""
return True
 
def get_children(self):
if self.decorators is not None:
yield self.decorators
yield self.args
if self.returns is not None:
yield self.returns
for elt in self.body:
yield elt
 
class AsyncFunctionDef(FunctionDef):
"""Class representing an :class:`ast.FunctionDef` node.
Loading
Loading
@@ -2651,6 +2692,16 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG,
"""
return True
 
def get_children(self):
for elt in self.body:
yield elt
for elt in self.bases:
yield elt
if self.decorators is not None:
yield self.decorators
 
# Backwards-compatibility aliases
Class = util.proxy_alias('Class', ClassDef)
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment