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
commit6234932673dedeeb220db45e181c311211544e31 (patch)
tree7e2d5a0296b588c4c4cd20f71d0a9a3c4c3989e3 /astroid/scoped_nodes.py
parentf8f8b0ca248fe80ffde3c2be9b5b48096b39c62b (diff)
downloadastroid-6234932673dedeeb220db45e181c311211544e31.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 14cf79f..c660e90 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):