diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 9db08313..a26fc733 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -166,7 +166,7 @@ class Evaluator(object): @memoize_default(evaluator_is_first_arg=True) def eval_element(self, element): - debug.dbg('eval_element %s', element) + debug.dbg('eval_element %s@%s', element, element.start_pos) if isinstance(element, (pr.Name, pr.Literal)) or pr.is_node(element, 'atom'): return self._eval_atom(element) elif isinstance(element, pr.Keyword): diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index bded1d58..2e30856d 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -160,8 +160,7 @@ class NameFinder(object): # current scope. if isinstance(stmt, (pr.Param, pr.Import)) \ or isinstance(name_list_scope, (pr.Lambda, pr.ListComprehension, er.Instance, InterpreterNamespace)) \ - or isinstance(scope, compiled.CompiledObject) \ - or isinstance(stmt, pr.ExprStmt) and stmt.is_global(): + or isinstance(scope, compiled.CompiledObject): # Always reachable. names.append(name) else: diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index a117ef82..f5e4288b 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -36,6 +36,7 @@ from itertools import chain from jedi._compatibility import use_metaclass, unicode, Python3Method from jedi.parser import representation as pr from jedi.parser.tokenize import Token +from jedi.parser.pytree import python_symbols from jedi import debug from jedi import common from jedi.cache import underscore_memoization @@ -152,7 +153,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)): if sub.name.value == '__init__': # ``__init__`` is special because the params need are injected # this way. Therefore an execution is necessary. - if not sub.decorators: + if not sub.get_decorators(): # __init__ decorators should generally just be ignored, # because to follow them and their self variables is too # complicated. @@ -464,9 +465,16 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)): # Only enter it, if has not already been processed. if not self.is_decorated: - for dec in reversed(self.base_func.decorators): + for dec in reversed(self.base_func.get_decorators()): debug.dbg('decorator: %s %s', dec, f) - dec_results = self._evaluator.eval_statement(dec) + dec.children + dec_results = self._evaluator.eval_element(dec.children[1]) + trailer = dec.children[2:-1] + if trailer: + # Create a trailer and evaluate it. + trailer = pr.Node(python_symbols.trailer, trailer) + dec_results = self._evaluator.eval_trailer(trailer) + if not len(dec_results): debug.warning('decorator not found: %s on %s', dec, self.base_func) return None @@ -474,6 +482,7 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)): if dec_results: debug.warning('multiple decorators found %s %s', self.base_func, dec_results) + # Create param array. old_func = Function(self._evaluator, f, is_decorated=True) @@ -526,7 +535,7 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)): def __repr__(self): dec_func = self._decorated_func() dec = '' - if not self.is_decorated and self.base_func.decorators: + if not self.is_decorated and self.base_func.get_decorators(): dec = " is " + repr(dec_func) return "" % (type(self).__name__, self.base_func, dec) diff --git a/jedi/parser/__init__.py b/jedi/parser/__init__.py index e99f785a..88670843 100644 --- a/jedi/parser/__init__.py +++ b/jedi/parser/__init__.py @@ -74,6 +74,7 @@ class Parser(object): 'while_stmt': pr.WhileStmt, 'try_stmt': pr.TryStmt, 'comp_for': pr.CompFor, + 'decorator': pr.Decorator, } self._ast_mapping = dict((getattr(pytree.python_symbols, k), v) diff --git a/jedi/parser/representation.py b/jedi/parser/representation.py index 4d9e6ebf..789c5966 100644 --- a/jedi/parser/representation.py +++ b/jedi/parser/representation.py @@ -561,7 +561,7 @@ class Scope(Simple, DocstringMixin): if include_imports: checks += self.imports if self.isinstance(Function): - checks += self.decorators + checks += self.get_decorators() checks += [r for r in self.returns if r is not None] if self.isinstance(Flow): checks += self.inputs @@ -699,6 +699,10 @@ class SubModule(Scope, Module): return False +class Decorator(Simple): + pass + + class ClassOrFunc(Scope): __slots__ = () @@ -706,6 +710,16 @@ class ClassOrFunc(Scope): def name(self): return self.children[1] + def get_decorators(self): + decorated = self.parent + if is_node(decorated, 'decorated'): + if is_node(decorated.children[0], 'decorators'): + return decorated.children[0].children + else: + return decorated.children[:1] + else: + return [] + class Class(ClassOrFunc): """ @@ -718,11 +732,9 @@ class Class(ClassOrFunc): :param start_pos: The start position (line, column) of the class. :type start_pos: tuple(int, int) """ - __slots__ = ('decorators') def __init__(self, children): super(Class, self).__init__(children) - self.decorators = [] def get_super_arglist(self): if len(self.children) == 4: # Has no parentheses @@ -762,11 +774,10 @@ class Function(ClassOrFunc): :param start_pos: The start position (line, column) the Function. :type start_pos: tuple(int, int) """ - __slots__ = ('decorators', 'listeners', 'params') + __slots__ = ('listeners', 'params') def __init__(self, children): super(Function, self).__init__(children) - self.decorators = [] self.listeners = set() # not used here, but in evaluation. self.params = self._params()