summaryrefslogtreecommitdiff
path: root/astroid/scoped_nodes.py
diff options
context:
space:
mode:
authorClaudiu Popa <cpopa@cloudbasesolutions.com>2015-08-06 14:38:22 +0300
committerClaudiu Popa <cpopa@cloudbasesolutions.com>2015-08-06 14:38:22 +0300
commitd0e11108e62e34658ff648723f993e3de38cd7ad (patch)
tree8767bb2fd3a0d461d370d5f6e3c4d93146d299c4 /astroid/scoped_nodes.py
parentb38a9a937ecd312518617b1fb9a886864d66d827 (diff)
downloadastroid-git-d0e11108e62e34658ff648723f993e3de38cd7ad.tar.gz
Move the determination of extra_decorators directly into Function, as Function.extra_decorators.
Previously, the extra_decorators computation was done in the rebuilder, which isn't the proper way to do it, since the rebuilder should be responsible only from transforming vanilla ast trees to astroid trees. Now, Function exports a propery called `extra_decorators`, which computes at runtime the callables that are wrapping a function in a class scope.
Diffstat (limited to 'astroid/scoped_nodes.py')
-rw-r--r--astroid/scoped_nodes.py44
1 files changed, 42 insertions, 2 deletions
diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py
index 14cf79fc..c660e903 100644
--- a/astroid/scoped_nodes.py
+++ b/astroid/scoped_nodes.py
@@ -670,15 +670,55 @@ class Function(bases.Statement, Lambda):
self.body = []
self.name = name
self.doc = doc
- self.extra_decorators = []
self.instance_attrs = {}
@decorators_mod.cachedproperty
+ def extra_decorators(self):
+ """Get the extra decorators that this function can have
+
+ Additional decorators are considered when they are used as
+ assignments, as in `method = staticmethod(method)`.
+ The property will return all the callables that are used for
+ decoration.
+ """
+ frame = self.parent.frame()
+ if not isinstance(frame, Class):
+ return []
+
+ decorators = []
+ for assign in frame.nodes_of_class(node_classes.Assign):
+ if (isinstance(assign.value, node_classes.CallFunc)
+ and isinstance(assign.value.func, node_classes.Name)):
+ for assign_node in assign.targets:
+ if not isinstance(assign_node, node_classes.AssName):
+ # Support only `name = callable(name)`
+ continue
+
+ if assign_node.name != self.name:
+ # Interested only in the assignment nodes that
+ # decorates the current method.
+ continue
+ try:
+ meth = frame[self.name]
+ except KeyError:
+ continue
+ else:
+ if isinstance(meth, Function):
+ decorators.append(assign.value)
+ return decorators
+
+ @decorators_mod.cachedproperty
def type(self):
"""Get the function type for this node.
Possible values are: method, function, staticmethod, classmethod.
"""
+ builtin_descriptors = {'classmethod', 'staticmethod'}
+
+ for decorator in self.extra_decorators:
+ if decorator.func.name in builtin_descriptors:
+ return decorator.func.name
+
frame = self.parent.frame()
type_name = 'function'
if isinstance(frame, Class):
@@ -690,7 +730,7 @@ class Function(bases.Statement, Lambda):
if self.decorators:
for node in self.decorators.nodes:
if isinstance(node, node_classes.Name):
- if node.name in ('classmethod', 'staticmethod'):
+ if node.name in builtin_descriptors:
return node.name
if isinstance(node, node_classes.CallFunc):