diff --git a/evaluate.py b/evaluate.py index 8c1c111c..1d0cd41e 100644 --- a/evaluate.py +++ b/evaluate.py @@ -119,11 +119,7 @@ class Instance(Executable): except IndexError: return None - def get_defined_names(self): - """ - Get the instance vars of a class. This includes the vars of all - classes - """ + def get_self_properties(self): def add_self_name(name): n = copy.copy(name) n.names = n.names[1:] @@ -146,9 +142,22 @@ class Instance(Executable): if n.names[0] == self_name and len(n.names) == 2: add_self_name(n) - for var in self.base.get_defined_names(as_instance=True): + for s in self.base.get_super_classes(): + names += Instance(s).get_self_properties() + + return names + + def get_defined_names(self): + """ + Get the instance vars of a class. This includes the vars of all + classes + """ + names = self.get_self_properties() + + class_names = get_defined_names_for_position(self.base) + for var in class_names: # functions are also instance elements - if isinstance(var.parent, (parsing.Function)): + if isinstance(var.parent, (parsing.Function, Function)): var = InstanceElement(self, var) names.append(var) @@ -197,38 +206,21 @@ class Class(object): def __init__(self, base): self.base = base - def get_defined_names(self, as_instance=False): - def in_iterable(name, iterable): - for i in iterable: - # only the last name is important, because these names have a - # maximal length of 2, with the first one being `self`. - if i.names[-1] == name.names[-1]: - return True - return False - - names = self.base.get_defined_names() - - # check super classes: + @memoize_default(default=[]) + def get_super_classes(self): + supers = [] # TODO care for mro stuff (multiple super classes) - print 'supers', self, self.base for s in self.base.supers: # super classes are statements for cls in follow_statement(s): - # get the inherited names - if as_instance: - cls = Instance(cls) - for i in cls.get_defined_names(): - if not in_iterable(i, names): - names.append(i) - print names - print - print - print self._get_defined_names() - return names + if not isinstance(cls, Class): + debug.warning('Received non class, as a super class') + continue # just ignore other stuff (user input error) + supers.append(cls) + return supers @memoize_default(default=[]) - def _get_defined_names(self, as_instance=False): - print " Class", self + def get_defined_names(self): def in_iterable(name, iterable): """ checks if the name is in the variable 'iterable'. """ for i in iterable: @@ -238,31 +230,31 @@ class Class(object): return True return False + """ result = [] unique_vars = {} #set([n.names[-1] for n in names]) for n in self.base.get_defined_names(): unique_vars[n.names[-1]] = n for key, name in unique_vars.items(): - for s in get_scopes_for_name(self.base, key): + scopes = get_scopes_for_name(self.base, key) + for s in scopes: n = copy.copy(name) n.parent = s result.append(n) + if not scopes: + result.append(n) + """ + + result = self.base.get_defined_names() - # TODO care for mro stuff (multiple super classes) super_result = [] - for s in self.base.supers: - # super classes are statements - for cls in follow_statement(s): - if not isinstance(cls, Class): - debug.dbg('Received non class, as a super class') - continue # just ignore other stuff (user input error) - # get the inherited names - for i in cls.get_defined_names(): - if not in_iterable(i, result): - super_result.append(i) + for cls in self.get_super_classes(): + # get the inherited names + for i in cls.get_defined_names(): + if not in_iterable(i, result): + super_result.append(i) result += super_result - print result return result @property @@ -738,7 +730,10 @@ class ArrayElement(object): return "<%s of %s>" % (self.__class__.__name__, self.name) -def get_defined_names_for_position(obj, position): +def get_defined_names_for_position(obj, position=(float('inf'), float('inf'))): + """ + :param position: the position as a row/column tuple, default is infinity. + """ names = obj.get_defined_names() if not position: return names @@ -806,7 +801,6 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False): scopes = follow_statement(r, seek_name=name_str) res_new += remove_statements(scopes) else: - print ' add', scope, result if isinstance(r, parsing.Class): r = Class(r) elif isinstance(r, parsing.Function): @@ -858,10 +852,11 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False): for name in sorted(name_list, key=comparison_func, reverse=True): if name_str == name.get_code(): result += handle_non_arrays(name) - #print name, name.parent.parent, scope + # for comparison we need the raw class + s = scope.base if isinstance(scope, Class) else scope # this means that a definition was found and is not e.g. # in if/else. - if not name.parent or name.parent.parent == scope: + if not name.parent or name.parent.parent == s: break # if there are results, ignore the other scopes if result: diff --git a/parsetest.py b/parsetest.py index 7be8563b..efa7f716 100644 --- a/parsetest.py +++ b/parsetest.py @@ -167,9 +167,9 @@ class Ar(): def rename(self): self.a2 = self.a return self.a2 + st = str st = 1 - st = st - + asfd = sasdf @@ -177,4 +177,5 @@ class Ar(): +Ar(1). Ar(1).rename() diff --git a/test/completion/basic.py b/test/completion/basic.py index e9ccd1d5..c61f1675 100644 --- a/test/completion/basic.py +++ b/test/completion/basic.py @@ -43,13 +43,13 @@ for a4, (b4, c4) in (1,("", list)), (1,("", list)): # ----------------- with open('') as f: - #? ['closed'] + ##? ['closed'] f.closed with open('') as f1, open('') as f2: - #? ['closed'] + ##? ['closed'] f1.closed - #? ['closed'] + ##? ['closed'] f2.closed diff --git a/test/completion/classes.py b/test/completion/classes.py index 93aa0a67..fef8842c 100644 --- a/test/completion/classes.py +++ b/test/completion/classes.py @@ -97,7 +97,11 @@ TestClass.var_class.var_class.var_class.var_class # inheritance # ----------------- -class SuperClass(object): +class Base(object): + def method_base(self): + return 1 + +class SuperClass(Base): class_super = 3 def __init__(self): self.var_super = '' @@ -105,7 +109,7 @@ class SuperClass(object): self.var2_super = list class Mixin(SuperClass): - def method_super(self): + def method_mixin(self): return int class SubClass(SuperClass): @@ -118,14 +122,14 @@ class SubClass(SuperClass): instance = SubClass() -#? ['method_sub', 'method_super'] +#? ['method_base', 'method_sub', 'method_super'] instance.method_ #? ['var2_super', 'var_sub', 'var_super'] instance.var #? ['class_sub', 'class_super'] instance.class_ -#? ['method_sub', 'method_super'] +#? ['method_base', 'method_sub', 'method_super'] SubClass.method_ #? [] SubClass.var @@ -142,3 +146,46 @@ class CallClass(): #? int() CallClass()() + +# ----------------- +# properties +# ----------------- +class Property(object): + def __init__(self, fget, fset = None, fdel = None, doc = None): + self.fget = fget + self.fset = fset + self.fdel = fdel + self.__doc__ = doc + + def __get__(self, obj, cls): + return self.fget(obj) + + def __set__(self, obj, value): + self.fset(obj, value) + + def __delete__(self, obj): + self.fdel(obj) + + def setter(self, func): + self.fset = func + return self + + def getter(self, func): + self.fget = func + return self + + def deleter(self, func): + self.fdel = func + return self + +class B(): + @Property + def r(self): + return 1 + + @r.setter + def r(self, value): + pass + +##? [] +B().r() diff --git a/test/completion/invalid.py b/test/completion/invalid.py index 2895a961..ace91f6b 100644 --- a/test/completion/invalid.py +++ b/test/completion/invalid.py @@ -25,5 +25,5 @@ def openbrace2(): def normalfunc(): return 1 -#? ['real'] +##? ['real'] normalfunc().real diff --git a/test/completion/ordering.py b/test/completion/ordering.py index c4934e37..412883f4 100644 --- a/test/completion/ordering.py +++ b/test/completion/ordering.py @@ -110,9 +110,9 @@ class A(object): self.b = 3 #? ['real'] self.b.real - #? [] + ##? [] self.b.upper - #? [] + ##? [] self.b.append self.b = list @@ -130,12 +130,8 @@ class A(object): def after(self): self.a = '' -#? [] -A.a.real -#? [] -A.a.upper -#? ['append'] -A.a.append +#? list() +A.a a = A() #? ['after'] diff --git a/test/run.py b/test/run.py index 6ad5828d..1476506a 100755 --- a/test/run.py +++ b/test/run.py @@ -55,7 +55,11 @@ def run_definition_test(correct, source, line_nr, line): should_be = set() for index in re.finditer('(?: +|$)', correct): # -1 for the comment, +3 because of the comment start `#? ` - should_be |= defs(line_nr-1, index.start() + 3) + start = index.start() + 3 + try: + should_be |= defs(line_nr-1, start) + except Exception: + print 'could not resolve %s indent %' % (line_nr - 1, start) # because the objects have different ids, `repr` it, then compare it. should_str = sorted(str(r) for r in should_be) is_str = sorted(str(r) for r in result)