From 33e5a3280addce586d95b3620c9959db4512e2f2 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 12 Aug 2014 01:19:19 +0200 Subject: [PATCH] Remove IsScope in favor of an is_scope function. This function was partially implemented anway. Now we've also added a function called 'get_parent_scope', to make it easy to get a scope of a Call, Statement, whatever. --- jedi/api/classes.py | 2 +- jedi/api/usages.py | 2 +- jedi/evaluate/__init__.py | 5 ++-- jedi/evaluate/compiled/__init__.py | 7 ++++-- jedi/evaluate/finder.py | 2 +- jedi/evaluate/iterable.py | 2 +- jedi/evaluate/representation.py | 20 ++++++++++++--- jedi/parser/representation.py | 40 ++++++++++++++++++------------ 8 files changed, 53 insertions(+), 27 deletions(-) diff --git a/jedi/api/classes.py b/jedi/api/classes.py index ab1134b2..21a3a930 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -360,7 +360,7 @@ class BaseDefinition(object): if isinstance(self._definition, compiled.CompiledObject): non_flow = self._definition.parent else: - scope = self._definition.get_parent_until(pr.IsScope, include_current=False) + scope = self._definition.get_parent_scope(include_current=False) non_flow = scope.get_parent_until(pr.Flow, reverse=True) return Definition(self._evaluator, non_flow) diff --git a/jedi/api/usages.py b/jedi/api/usages.py index 21ef7ece..e18dc955 100644 --- a/jedi/api/usages.py +++ b/jedi/api/usages.py @@ -19,7 +19,7 @@ def usages(evaluator, definitions, search_name, mods): def check_call_for_usage(call): stmt = call.parent - while not isinstance(stmt.parent, pr.IsScope): + while not stmt.parent.is_scope(): stmt = stmt.parent # New definition, call cannot be a part of stmt if len(call.name) == 1 and call.execution is None \ diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index eb9da089..4bbd0567 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -220,9 +220,10 @@ class Evaluator(object): """Follow a call is following a function, variable, string, etc.""" path = call.generate_call_path() + # TODO use scope_parent # find the statement of the Scope s = call - while not s.parent.isinstance(pr.IsScope): + while not s.parent.is_scope(): s = s.parent par = s.parent return self.eval_call_path(path, par, s.start_pos) @@ -335,7 +336,7 @@ class Evaluator(object): return types def goto(self, stmt, call_path): - scope = stmt.get_parent_until(pr.IsScope) + scope = stmt.get_parent_scope() pos = stmt.start_pos call_path, search_name_part = call_path[:-1], call_path[-1] diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index 3d665fcd..ee18d5d4 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -10,7 +10,7 @@ from jedi._compatibility import builtins as _builtins, unicode from jedi import debug from jedi.cache import underscore_memoization, memoize from jedi.evaluate.sys_path import get_sys_path -from jedi.parser.representation import Param, SubModule, Base, IsScope, Operator +from jedi.parser.representation import Param, SubModule, Base, Operator from jedi.evaluate.helpers import FakeName from . import fake @@ -374,12 +374,15 @@ def _parse_function_doc(doc): return param_str, ret -class Builtin(CompiledObject, IsScope): +class Builtin(CompiledObject, Base): @memoize def get_by_name(self, name): item = [n for n in self.get_defined_names() if n.get_code() == name][0] return item.parent + def is_scope(self): + return True + def _a_generator(foo): """Used to have an object to return for generators.""" diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 506968b8..6916fc6d 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -380,7 +380,7 @@ def check_flow_information(evaluator, flow, search_name_part, pos): return None result = [] - if isinstance(flow, pr.IsScope): + if flow.is_scope(): for ass in reversed(flow.asserts): if pos is None or ass.start_pos > pos: continue diff --git a/jedi/evaluate/iterable.py b/jedi/evaluate/iterable.py index 26efb83d..66ae1e17 100644 --- a/jedi/evaluate/iterable.py +++ b/jedi/evaluate/iterable.py @@ -337,7 +337,7 @@ def _check_array_additions(evaluator, compare_array, module, is_list): backtrack_path = iter(call_path[:separate_index]) position = c.start_pos - scope = c.get_parent_until(pr.IsScope) + scope = c.get_parent_scope() found = evaluator.eval_call_path(backtrack_path, scope, position) if not compare_array in found: diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index aaf62f2e..c1b2df1f 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -41,7 +41,7 @@ def wrap(evaluator, element): return element -class Executed(pr.IsScope): +class Executed(pr.Base): """ An instance is also an executable - because __init__ is called :param var_args: The param input array, consist of `pr.Array` or list. @@ -51,6 +51,9 @@ class Executed(pr.IsScope): self.base = base self.var_args = var_args + def is_scope(self): + return True + def get_parent_until(self, *args, **kwargs): return self.base.get_parent_until(*args, **kwargs) @@ -278,6 +281,12 @@ class InstanceElement(use_metaclass(CachedMetaClass, pr.Base)): def isinstance(self, *cls): return isinstance(self.var, cls) + def is_scope(self): + """ + Since we inherit from Base, it would overwrite the action we want here. + """ + return self.var.is_scope() + def py__call__(self, evaluator, params): return Function.py__call__(self, evaluator, params) @@ -285,7 +294,12 @@ class InstanceElement(use_metaclass(CachedMetaClass, pr.Base)): return "<%s of %s>" % (type(self).__name__, self.var) -class Class(use_metaclass(CachedMetaClass, pr.IsScope)): +class Wrapper(pr.Base): + def is_scope(self): + return True + + +class Class(use_metaclass(CachedMetaClass, Wrapper)): """ This class is not only important to extend `pr.Class`, it is also a important for descriptors (if the descriptor methods are evaluated or not). @@ -376,7 +390,7 @@ class Class(use_metaclass(CachedMetaClass, pr.IsScope)): return "" % (type(self).__name__, self.base) -class Function(use_metaclass(CachedMetaClass, pr.IsScope)): +class Function(use_metaclass(CachedMetaClass, Wrapper)): """ Needed because of decorators. Decorators are evaluated here. """ diff --git a/jedi/parser/representation.py b/jedi/parser/representation.py index 6b25533e..56262090 100644 --- a/jedi/parser/representation.py +++ b/jedi/parser/representation.py @@ -134,11 +134,23 @@ class Base(object): classes = (classes,) scope = self if include_current else self.parent while scope.parent is not None: + # TODO why if classes? if classes and reverse != scope.isinstance(*classes): break scope = scope.parent return scope + def get_parent_scope(self, include_current=True): + """ + Returns the underlying scope. + """ + scope = self if include_current else self.parent + while scope.parent is not None: + if scope.is_scope(): + break + scope = scope.parent + return scope + def space(self, from_pos, to_pos): """Return the space between two tokens""" linecount = to_pos[0] - from_pos[0] @@ -150,6 +162,10 @@ class Base(object): self.whitespace * to_pos[1], ) + def is_scope(self): + # Default is not being a scope. Just inherit from Scope. + return False + class Simple(Base): """ @@ -205,18 +221,8 @@ class Simple(Base): return "<%s: %s@%s,%s>" % \ (type(self).__name__, code, self.start_pos[0], self.start_pos[1]) - def is_scope(self): - return False - -class IsScope(Base): - __slots__ = () - - def is_scope(self): - return True - - -class Scope(IsScope, Simple, DocstringMixin): +class Scope(Simple, DocstringMixin): """ Super class for the parser tree, which represents the state of a python text file. @@ -242,6 +248,9 @@ class Scope(IsScope, Simple, DocstringMixin): self.returns = [] self.is_generator = False + def is_scope(self): + return True + def add_scope(self, sub, decorators): sub.parent = self.use_as_parent sub.decorators = decorators @@ -384,10 +393,12 @@ class Scope(IsScope, Simple, DocstringMixin): r = r.next -class Module(IsScope): +class Module(Base): """ For isinstance checks. fast_parser.Module also inherits from this. """ + def is_scope(self): + return True class SubModule(Scope, Module): @@ -846,9 +857,6 @@ class KeywordStatement(Base): if stmt is not None: stmt.parent = self - def is_scope(self): - return False - def __repr__(self): return "<%s(%s): %s>" % (type(self).__name__, self.name, self.stmt) @@ -1099,7 +1107,7 @@ class Statement(Simple, DocstringMixin): return None, tok # Since Lambda is a Function scope, it needs Scope parents. - parent = self.get_parent_until(IsScope) + parent = self.get_parent_scope() lambd = Lambda(self._sub_module, params, start_pos, parent) ret, tok = parse_stmt(token_iterator)