summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEevee (Alex Munroe) <amunroe@yelp.com>2014-03-24 18:00:17 -0700
committerEevee (Alex Munroe) <amunroe@yelp.com>2014-07-01 17:33:00 -0700
commit53013fa01f45114c1b8ee87527eab4fbeef8a113 (patch)
treed4ea5d9e01a4e746ea48037443a62bdb40d250da
parent2e13d989dbf9a097c679e4e1ca71e59fc9e5524f (diff)
downloadastroid-git-53013fa01f45114c1b8ee87527eab4fbeef8a113.tar.gz
Speed up rebuilder considerably by computing line numbers lazily.
Fetching the last child of each of hundreds of thousands of nodes is relatively expensive, and most of the time this information is never used, so don't actually figure it out until it's asked for. Saves the overhead of quite a few function calls, too.
-rw-r--r--bases.py28
-rw-r--r--mixins.py10
-rw-r--r--node_classes.py33
-rw-r--r--rebuilder.py74
-rw-r--r--scoped_nodes.py32
5 files changed, 71 insertions, 106 deletions
diff --git a/bases.py b/bases.py
index 60fd30d2..dd1ef14c 100644
--- a/bases.py
+++ b/bases.py
@@ -24,6 +24,8 @@ __docformat__ = "restructuredtext en"
import sys
from contextlib import contextmanager
+from logilab.common.decorators import cachedproperty
+
from astroid.exceptions import (InferenceError, AstroidError, NotFoundError,
UnresolvableName, UseInferenceDefault)
@@ -441,7 +443,7 @@ class NodeNG(object):
attr = getattr(self, field)
if not attr: # None or empty listy / tuple
continue
- if isinstance(attr, (list, tuple)):
+ if attr.__class__ in (list, tuple):
return attr[-1]
else:
return attr
@@ -532,16 +534,28 @@ class NodeNG(object):
# FIXME: raise an exception if nearest is None ?
return nearest[0]
- def set_line_info(self, lastchild):
+ # these are lazy because they're relatively expensive to compute for every
+ # single node, and they rarely get looked at
+
+ @cachedproperty
+ def fromlineno(self):
if self.lineno is None:
- self.fromlineno = self._fixed_source_line()
+ return self._fixed_source_line()
else:
- self.fromlineno = self.lineno
+ return self.lineno
+
+ @cachedproperty
+ def tolineno(self):
+ if not self._astroid_fields:
+ # can't have children
+ lastchild = None
+ else:
+ lastchild = self.last_child()
if lastchild is None:
- self.tolineno = self.fromlineno
+ return self.fromlineno
else:
- self.tolineno = lastchild.tolineno
- return
+ return lastchild.tolineno
+
# TODO / FIXME:
assert self.fromlineno is not None, self
assert self.tolineno is not None, self
diff --git a/mixins.py b/mixins.py
index 5e7b7878..757c2af9 100644
--- a/mixins.py
+++ b/mixins.py
@@ -18,16 +18,18 @@
"""This module contains some mixins for the different nodes.
"""
+from logilab.common.decorators import cachedproperty
+
from astroid.exceptions import (AstroidBuildingException, InferenceError,
NotFoundError)
class BlockRangeMixIn(object):
"""override block range """
- def set_line_info(self, lastchild):
- self.fromlineno = self.lineno
- self.tolineno = lastchild.tolineno
- self.blockstart_tolineno = self._blockstart_toline()
+
+ @cachedproperty
+ def blockstart_tolineno(self):
+ return self.lineno
def _elsed_block_range(self, lineno, orelse, last=None):
"""handle block line numbers range for try/finally, for, if and while
diff --git a/node_classes.py b/node_classes.py
index e247392e..45a00ba9 100644
--- a/node_classes.py
+++ b/node_classes.py
@@ -20,6 +20,8 @@
import sys
+from logilab.common.decorators import cachedproperty
+
from astroid.exceptions import NoDefault
from astroid.bases import (NodeNG, Statement, Instance, InferenceContext,
_infer_stmts, YES, BUILTINS)
@@ -267,6 +269,11 @@ class Arguments(NodeNG, AssignTypeMixin):
return name
return None
+ @cachedproperty
+ def fromlineno(self):
+ lineno = super(Arguments, self).fromlineno
+ return max(lineno, self.parent.fromlineno)
+
def format_args(self):
"""return arguments formatted as string"""
result = []
@@ -560,7 +567,8 @@ class ExceptHandler(Statement, AssignTypeMixin):
name = None
body = None
- def _blockstart_toline(self):
+ @cachedproperty
+ def blockstart_tolineno(self):
if self.name:
return self.name.tolineno
elif self.type:
@@ -568,11 +576,6 @@ class ExceptHandler(Statement, AssignTypeMixin):
else:
return self.lineno
- def set_line_info(self, lastchild):
- self.fromlineno = self.lineno
- self.tolineno = lastchild.tolineno
- self.blockstart_tolineno = self._blockstart_toline()
-
def catch(self, exceptions):
if self.type is None or exceptions is None:
return True
@@ -603,7 +606,8 @@ class For(BlockRangeMixIn, AssignTypeMixin, Statement):
orelse = None
optional_assign = True
- def _blockstart_toline(self):
+ @cachedproperty
+ def blockstart_tolineno(self):
return self.iter.tolineno
@@ -638,7 +642,8 @@ class If(BlockRangeMixIn, Statement):
body = None
orelse = None
- def _blockstart_toline(self):
+ @cachedproperty
+ def blockstart_tolineno(self):
return self.test.tolineno
def block_range(self, lineno):
@@ -789,9 +794,6 @@ class TryExcept(BlockRangeMixIn, Statement):
def _infer_name(self, frame, name):
return name
- def _blockstart_toline(self):
- return self.lineno
-
def block_range(self, lineno):
"""handle block line numbers range for try/except statements"""
last = None
@@ -811,9 +813,6 @@ class TryFinally(BlockRangeMixIn, Statement):
body = None
finalbody = None
- def _blockstart_toline(self):
- return self.lineno
-
def block_range(self, lineno):
"""handle block line numbers range for try/finally statements"""
child = self.body[0]
@@ -857,7 +856,8 @@ class While(BlockRangeMixIn, Statement):
body = None
orelse = None
- def _blockstart_toline(self):
+ @cachedproperty
+ def blockstart_tolineno(self):
return self.test.tolineno
def block_range(self, lineno):
@@ -871,7 +871,8 @@ class With(BlockRangeMixIn, AssignTypeMixin, Statement):
items = None
body = None
- def _blockstart_toline(self):
+ @cachedproperty
+ def blockstart_tolineno(self):
return self.items[-1][0].tolineno
def get_children(self):
diff --git a/rebuilder.py b/rebuilder.py
index 47eff50e..cef00f8c 100644
--- a/rebuilder.py
+++ b/rebuilder.py
@@ -103,10 +103,8 @@ def _init_set_doc(node, newnode):
def _lineno_parent(oldnode, newnode, parent):
newnode.parent = parent
- if hasattr(oldnode, 'lineno'):
- newnode.lineno = oldnode.lineno
- if hasattr(oldnode, 'col_offset'):
- newnode.col_offset = oldnode.col_offset
+ newnode.lineno = oldnode.lineno
+ newnode.col_offset = oldnode.col_offset
def _set_infos(oldnode, newnode, parent):
newnode.parent = parent
@@ -114,14 +112,12 @@ def _set_infos(oldnode, newnode, parent):
newnode.lineno = oldnode.lineno
if hasattr(oldnode, 'col_offset'):
newnode.col_offset = oldnode.col_offset
- newnode.set_line_info(newnode.last_child()) # set_line_info accepts None
def _create_yield_node(node, parent, rebuilder, factory):
newnode = factory()
_lineno_parent(node, newnode, parent)
if node.value is not None:
newnode.value = rebuilder.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
@@ -141,10 +137,9 @@ class TreeRebuilder(object):
"""visit a Module node by returning a fresh instance of it"""
newnode = new.Module(modname, None)
newnode.package = package
- _lineno_parent(node, newnode, parent=None)
+ newnode.parent = None
_init_set_doc(node, newnode)
newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.set_line_info(newnode.last_child())
return self._transform(newnode)
def visit(self, node, parent):
@@ -169,7 +164,7 @@ class TreeRebuilder(object):
def visit_arguments(self, node, parent):
"""visit a Arguments node by returning a fresh instance of it"""
newnode = new.Arguments()
- _lineno_parent(node, newnode, parent)
+ newnode.parent = parent
self.asscontext = "Ass"
newnode.args = [self.visit(child, newnode) for child in node.args]
self.asscontext = None
@@ -190,7 +185,6 @@ class TreeRebuilder(object):
newnode.parent.set_local(vararg, newnode)
if kwarg:
newnode.parent.set_local(kwarg, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_assattr(self, node, parent):
@@ -201,7 +195,6 @@ class TreeRebuilder(object):
newnode.expr = self.visit(node.expr, newnode)
self.asscontext = assc
self._delayed_assattr.append(newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_assert(self, node, parent):
@@ -211,7 +204,6 @@ class TreeRebuilder(object):
newnode.test = self.visit(node.test, newnode)
if node.msg is not None:
newnode.fail = self.visit(node.msg, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_assign(self, node, parent):
@@ -239,7 +231,6 @@ class TreeRebuilder(object):
meth.extra_decorators.append(newnode.value)
except (AttributeError, KeyError):
continue
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_assname(self, node, parent, node_name=None):
@@ -259,7 +250,6 @@ class TreeRebuilder(object):
newnode.target = self.visit(node.target, newnode)
self.asscontext = None
newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_backquote(self, node, parent):
@@ -267,7 +257,6 @@ class TreeRebuilder(object):
newnode = new.Backquote()
_lineno_parent(node, newnode, parent)
newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_binop(self, node, parent):
@@ -277,7 +266,6 @@ class TreeRebuilder(object):
newnode.left = self.visit(node.left, newnode)
newnode.right = self.visit(node.right, newnode)
newnode.op = _BIN_OP_CLASSES[node.op.__class__]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_boolop(self, node, parent):
@@ -286,7 +274,6 @@ class TreeRebuilder(object):
_lineno_parent(node, newnode, parent)
newnode.values = [self.visit(child, newnode) for child in node.values]
newnode.op = _BOOL_OP_CLASSES[node.op.__class__]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_break(self, node, parent):
@@ -306,7 +293,6 @@ class TreeRebuilder(object):
if node.kwargs is not None:
newnode.kwargs = self.visit(node.kwargs, newnode)
newnode.args.extend(self.visit(child, newnode) for child in node.keywords)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_class(self, node, parent):
@@ -318,7 +304,6 @@ class TreeRebuilder(object):
newnode.body = [self.visit(child, newnode) for child in node.body]
if 'decorator_list' in node._fields and node.decorator_list:# py >= 2.6
newnode.decorators = self.visit_decorators(node, newnode)
- newnode.set_line_info(newnode.last_child())
newnode.parent.frame().set_local(newnode.name, newnode)
return newnode
@@ -341,19 +326,17 @@ class TreeRebuilder(object):
newnode.left = self.visit(node.left, newnode)
newnode.ops = [(_CMP_OP_CLASSES[op.__class__], self.visit(expr, newnode))
for (op, expr) in zip(node.ops, node.comparators)]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_comprehension(self, node, parent):
"""visit a Comprehension node by returning a fresh instance of it"""
newnode = new.Comprehension()
- _lineno_parent(node, newnode, parent)
+ newnode.parent = parent
self.asscontext = "Ass"
newnode.target = self.visit(node.target, newnode)
self.asscontext = None
newnode.iter = self.visit(node.iter, newnode)
newnode.ifs = [self.visit(child, newnode) for child in node.ifs]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_decorators(self, node, parent):
@@ -367,7 +350,6 @@ class TreeRebuilder(object):
else:
decorators= node.decorator_list
newnode.nodes = [self.visit(child, newnode) for child in decorators]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_delete(self, node, parent):
@@ -377,7 +359,6 @@ class TreeRebuilder(object):
self.asscontext = "Del"
newnode.targets = [self.visit(child, newnode) for child in node.targets]
self.asscontext = None
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_dict(self, node, parent):
@@ -386,7 +367,6 @@ class TreeRebuilder(object):
_lineno_parent(node, newnode, parent)
newnode.items = [(self.visit(key, newnode), self.visit(value, newnode))
for key, value in zip(node.keys, node.values)]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_dictcomp(self, node, parent):
@@ -397,7 +377,6 @@ class TreeRebuilder(object):
newnode.value = self.visit(node.value, newnode)
newnode.generators = [self.visit(child, newnode)
for child in node.generators]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_discard(self, node, parent):
@@ -405,7 +384,6 @@ class TreeRebuilder(object):
newnode = new.Discard()
_lineno_parent(node, newnode, parent)
newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_ellipsis(self, node, parent):
@@ -432,7 +410,6 @@ class TreeRebuilder(object):
newnode.name = self.visit(node.name, newnode)
self.asscontext = None
newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_exec(self, node, parent):
@@ -444,15 +421,13 @@ class TreeRebuilder(object):
newnode.globals = self.visit(node.globals, newnode)
if node.locals is not None:
newnode.locals = self.visit(node.locals, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_extslice(self, node, parent):
"""visit an ExtSlice node by returning a fresh instance of it"""
newnode = new.ExtSlice()
- _lineno_parent(node, newnode, parent)
+ newnode.parent = parent
newnode.dims = [self.visit(dim, newnode) for dim in node.dims]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_for(self, node, parent):
@@ -465,7 +440,6 @@ class TreeRebuilder(object):
newnode.iter = self.visit(node.iter, newnode)
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_from(self, node, parent):
@@ -492,7 +466,6 @@ class TreeRebuilder(object):
decorators = getattr(node, attr)
if decorators:
newnode.decorators = self.visit_decorators(node, newnode)
- newnode.set_line_info(newnode.last_child())
self._global_names.pop()
frame = newnode.parent.frame()
if isinstance(frame, new.Class):
@@ -516,7 +489,6 @@ class TreeRebuilder(object):
_lineno_parent(node, newnode, parent)
newnode.elt = self.visit(node.elt, newnode)
newnode.generators = [self.visit(child, newnode) for child in node.generators]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_getattr(self, node, parent):
@@ -536,7 +508,6 @@ class TreeRebuilder(object):
newnode.expr = self.visit(node.value, newnode)
self.asscontext = asscontext
newnode.attrname = node.attr
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_global(self, node, parent):
@@ -555,7 +526,6 @@ class TreeRebuilder(object):
newnode.test = self.visit(node.test, newnode)
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_ifexp(self, node, parent):
@@ -565,7 +535,6 @@ class TreeRebuilder(object):
newnode.test = self.visit(node.test, newnode)
newnode.body = self.visit(node.body, newnode)
newnode.orelse = self.visit(node.orelse, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_import(self, node, parent):
@@ -582,18 +551,16 @@ class TreeRebuilder(object):
def visit_index(self, node, parent):
"""visit a Index node by returning a fresh instance of it"""
newnode = new.Index()
- _lineno_parent(node, newnode, parent)
+ newnode.parent = parent
newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_keyword(self, node, parent):
"""visit a Keyword node by returning a fresh instance of it"""
newnode = new.Keyword()
- _lineno_parent(node, newnode, parent)
+ newnode.parent = parent
newnode.arg = node.arg
newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_lambda(self, node, parent):
@@ -602,7 +569,6 @@ class TreeRebuilder(object):
_lineno_parent(node, newnode, parent)
newnode.args = self.visit(node.args, newnode)
newnode.body = self.visit(node.body, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_list(self, node, parent):
@@ -610,7 +576,6 @@ class TreeRebuilder(object):
newnode = new.List()
_lineno_parent(node, newnode, parent)
newnode.elts = [self.visit(child, newnode) for child in node.elts]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_listcomp(self, node, parent):
@@ -620,7 +585,6 @@ class TreeRebuilder(object):
newnode.elt = self.visit(node.elt, newnode)
newnode.generators = [self.visit(child, newnode)
for child in node.generators]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_name(self, node, parent):
@@ -643,7 +607,6 @@ class TreeRebuilder(object):
# XXX REMOVE me :
if self.asscontext in ('Del', 'Ass'): # 'Aug' ??
self._save_assignment(newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_bytes(self, node, parent):
@@ -678,7 +641,6 @@ class TreeRebuilder(object):
if node.dest is not None:
newnode.dest = self.visit(node.dest, newnode)
newnode.values = [self.visit(child, newnode) for child in node.values]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_raise(self, node, parent):
@@ -691,7 +653,6 @@ class TreeRebuilder(object):
newnode.inst = self.visit(node.inst, newnode)
if node.tback is not None:
newnode.tback = self.visit(node.tback, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_return(self, node, parent):
@@ -700,7 +661,6 @@ class TreeRebuilder(object):
_lineno_parent(node, newnode, parent)
if node.value is not None:
newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_set(self, node, parent):
@@ -708,7 +668,6 @@ class TreeRebuilder(object):
newnode = new.Set()
_lineno_parent(node, newnode, parent)
newnode.elts = [self.visit(child, newnode) for child in node.elts]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_setcomp(self, node, parent):
@@ -718,20 +677,18 @@ class TreeRebuilder(object):
newnode.elt = self.visit(node.elt, newnode)
newnode.generators = [self.visit(child, newnode)
for child in node.generators]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_slice(self, node, parent):
"""visit a Slice node by returning a fresh instance of it"""
newnode = new.Slice()
- _lineno_parent(node, newnode, parent)
+ newnode.parent = parent
if node.lower is not None:
newnode.lower = self.visit(node.lower, newnode)
if node.upper is not None:
newnode.upper = self.visit(node.upper, newnode)
if node.step is not None:
newnode.step = self.visit(node.step, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_subscript(self, node, parent):
@@ -742,7 +699,6 @@ class TreeRebuilder(object):
newnode.value = self.visit(node.value, newnode)
newnode.slice = self.visit(node.slice, newnode)
self.asscontext = subcontext
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_tryexcept(self, node, parent):
@@ -752,7 +708,6 @@ class TreeRebuilder(object):
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.handlers = [self.visit(child, newnode) for child in node.handlers]
newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_tryfinally(self, node, parent):
@@ -761,7 +716,6 @@ class TreeRebuilder(object):
_lineno_parent(node, newnode, parent)
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_tuple(self, node, parent):
@@ -769,7 +723,6 @@ class TreeRebuilder(object):
newnode = new.Tuple()
_lineno_parent(node, newnode, parent)
newnode.elts = [self.visit(child, newnode) for child in node.elts]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_unaryop(self, node, parent):
@@ -778,7 +731,6 @@ class TreeRebuilder(object):
_lineno_parent(node, newnode, parent)
newnode.operand = self.visit(node.operand, newnode)
newnode.op = _UNARY_OP_CLASSES[node.op.__class__]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_while(self, node, parent):
@@ -788,7 +740,6 @@ class TreeRebuilder(object):
newnode.test = self.visit(node.test, newnode)
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_with(self, node, parent):
@@ -803,7 +754,6 @@ class TreeRebuilder(object):
self.asscontext = None
newnode.items = [(expr, vars)]
newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_yield(self, node, parent):
@@ -842,7 +792,6 @@ class TreeRebuilder3k(TreeRebuilder):
if node.name is not None:
newnode.name = self.visit_assname(node, newnode, node.name)
newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_nonlocal(self, node, parent):
@@ -860,7 +809,6 @@ class TreeRebuilder3k(TreeRebuilder):
newnode.exc = self.visit(node.exc, newnode)
if node.cause is not None:
newnode.cause = self.visit(node.cause, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_starred(self, node, parent):
@@ -868,7 +816,6 @@ class TreeRebuilder3k(TreeRebuilder):
newnode = new.Starred()
_lineno_parent(node, newnode, parent)
newnode.value = self.visit(node.value, newnode)
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_try(self, node, parent):
@@ -883,7 +830,6 @@ class TreeRebuilder3k(TreeRebuilder):
excnode.body = [self.visit(child, excnode) for child in node.body]
excnode.handlers = [self.visit(child, excnode) for child in node.handlers]
excnode.orelse = [self.visit(child, excnode) for child in node.orelse]
- excnode.set_line_info(excnode.last_child())
newnode.body = [excnode]
else:
newnode.body = [self.visit(child, newnode) for child in node.body]
@@ -893,7 +839,6 @@ class TreeRebuilder3k(TreeRebuilder):
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.handlers = [self.visit(child, newnode) for child in node.handlers]
newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_with(self, node, parent):
@@ -915,7 +860,6 @@ class TreeRebuilder3k(TreeRebuilder):
newnode.items = [visit_child(child)
for child in node.items]
newnode.body = [self.visit(child, newnode) for child in node.body]
- newnode.set_line_info(newnode.last_child())
return newnode
def visit_yieldfrom(self, node, parent):
diff --git a/scoped_nodes.py b/scoped_nodes.py
index 389ebe71..f5d1c428 100644
--- a/scoped_nodes.py
+++ b/scoped_nodes.py
@@ -580,16 +580,20 @@ class Function(Statement, Lambda):
self.extra_decorators = []
self.instance_attrs = {}
- def set_line_info(self, lastchild):
- self.fromlineno = self.lineno
- # lineno is the line number of the first decorator, we want the def statement lineno
+ @cachedproperty
+ def fromlineno(self):
+ # lineno is the line number of the first decorator, we want the def
+ # statement lineno
+ lineno = self.lineno
if self.decorators is not None:
- self.fromlineno += sum(node.tolineno - node.lineno + 1
+ lineno += sum(node.tolineno - node.lineno + 1
for node in self.decorators.nodes)
- if self.args.fromlineno < self.fromlineno:
- self.args.fromlineno = self.fromlineno
- self.tolineno = lastchild.tolineno
- self.blockstart_tolineno = self.args.tolineno
+
+ return lineno
+
+ @cachedproperty
+ def blockstart_tolineno(self):
+ return self.args.tolineno
def block_range(self, lineno):
"""return block line numbers.
@@ -819,12 +823,12 @@ class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin):
doc="boolean indicating if it's a new style class"
"or not")
- def set_line_info(self, lastchild):
- self.fromlineno = self.lineno
- self.blockstart_tolineno = self.bases and self.bases[-1].tolineno or self.fromlineno
- if lastchild is not None:
- self.tolineno = lastchild.tolineno
- # else this is a class with only a docstring, then tolineno is (should be) already ok
+ @cachedproperty
+ def blockstart_tolineno(self):
+ if self.bases:
+ return self.bases[-1].tolineno
+ else:
+ return self.fromlineno
def block_range(self, lineno):
"""return block line numbers.