summaryrefslogtreecommitdiff
path: root/scoped_nodes.py
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2014-07-24 17:01:10 +0200
committerClaudiu Popa <pcmanticore@gmail.com>2014-07-24 17:01:10 +0200
commitf827b42355c1971b7344d407901d1d7a513cf9cc (patch)
tree56955199e3c1dcc65f292744c62d1fec845e7a51 /scoped_nodes.py
parentf23249089f36c2a9beff3cdf45fb564f025617ed (diff)
downloadastroid-git-f827b42355c1971b7344d407901d1d7a513cf9cc.tar.gz
Function nodes can detect decorator call chain and see if they are decorated with builtin descriptors (`classmethod` and `staticmethod`).
Diffstat (limited to 'scoped_nodes.py')
-rw-r--r--scoped_nodes.py51
1 files changed, 50 insertions, 1 deletions
diff --git a/scoped_nodes.py b/scoped_nodes.py
index 6b09b9de..502ebd5d 100644
--- a/scoped_nodes.py
+++ b/scoped_nodes.py
@@ -37,7 +37,7 @@ from astroid.exceptions import NotFoundError, \
AstroidBuildingException, InferenceError
from astroid.node_classes import Const, DelName, DelAttr, \
Dict, From, List, Pass, Raise, Return, Tuple, Yield, YieldFrom, \
- LookupMixIn, const_factory as cf, unpack_infer, Name
+ LookupMixIn, const_factory as cf, unpack_infer, Name, CallFunc
from astroid.bases import NodeNG, InferenceContext, Instance,\
YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, copy_context, \
BUILTINS
@@ -480,6 +480,49 @@ else:
# Function ###################################################################
+def _infer_decorator_callchain(node):
+ """ Detect decorator call chaining and see if the
+ end result is a static or a classmethod.
+ """
+ current = node
+ ignored = set((None, YES))
+ while True:
+ if isinstance(current, CallFunc):
+ try:
+ current = current.func.infer().next()
+ except InferenceError:
+ return
+ elif isinstance(current, Function):
+ if not current.parent:
+ return
+ try:
+ # TODO: We don't handle multiple inference results right now,
+ # because there's no flow to reason when the return
+ # is what we are looking for, a static or a class method.
+ result = current.infer_call_result(current.parent).next()
+ except InferenceError:
+ return
+ if isinstance(result, (Function, CallFunc)):
+ current = result
+ else:
+ if isinstance(result, Instance):
+ result = result._proxied
+ if isinstance(result, Class):
+ if (result.name == 'classmethod' and
+ result.root().name == BUILTINS):
+ return 'classmethod'
+ elif (result.name == 'staticmethod' and
+ result.root().name == BUILTINS):
+ return 'staticmethod'
+ else:
+ return
+ else:
+ # We aren't interested in anything else returned,
+ # so go back to the function type inference.
+ return
+ else:
+ return
+
def _function_type(self):
"""
Function type, possible values are:
@@ -490,6 +533,12 @@ def _function_type(self):
# so do it here.
if self.decorators:
for node in self.decorators.nodes:
+ if isinstance(node, CallFunc):
+ _type = _infer_decorator_callchain(node)
+ if _type is None:
+ continue
+ else:
+ return _type
if not isinstance(node, Name):
continue
try: