diff --git a/builtin.py b/builtin.py index 15be61f5..ab81ad70 100644 --- a/builtin.py +++ b/builtin.py @@ -39,7 +39,6 @@ class CachedModule(object): def _load_module(self): source = self._get_source() - print source self._parser = parsing.PyFuzzyParser(source, self.path or self.name) p_time = None if not self.path else os.path.getmtime(self.path) @@ -116,6 +115,7 @@ class Parser(CachedModule): should work much better for builtins. """ regex = r'^(def|class)\s+([\w\d]+)' + def process_code(code, depth=0): funcs = {} matches = list(re.finditer(regex, code, re.MULTILINE)) @@ -175,12 +175,11 @@ class Parser(CachedModule): continue # this has a builtin_function_or_method exe = getattr(scope, n) - #print exe, inspect.isbuiltin(exe) or inspect.ismethoddescriptor(exe) if inspect.isbuiltin(exe) or inspect.ismethoddescriptor(exe): funcs[n] = exe - elif type(exe) == type: + elif inspect.isclass(exe): classes[n] = exe - elif type(exe).__name__ == 'member_descriptor': + elif inspect.ismemberdescriptor(exe): members[n] = exe else: stmts[n] = exe @@ -237,10 +236,11 @@ class Parser(CachedModule): # class members (functions) properties? for name, func in members.items(): + # recursion problem in properties TODO remove + if name in ['fget', 'fset', 'fdel']: continue ret = 'pass' code += '@property\ndef %s(self):\n' % (name) - block = get_doc(func, indent=True) + '%s\n\n' % ret - code += parsing.indent_block(block) + code += parsing.indent_block(get_doc(func) + '%s\n\n' % ret) # variables for name, value in stmts.items(): @@ -328,7 +328,7 @@ class _Builtin(object): if sys.hexversion >= 0x03000000: name = 'builtins' else: - name='__builtin__' + name = '__builtin__' _builtins = Parser(name=name) @property diff --git a/evaluate.py b/evaluate.py index 3266a53e..1c1d7bc1 100644 --- a/evaluate.py +++ b/evaluate.py @@ -5,7 +5,8 @@ follow_statement -> follow_call -> follow_paths -> follow_path `get_names_for_scope` and `get_scopes_for_name` are search functions TODO doc -TODO list comprehensions, priority? +TODO list comprehensions, priority? +1 +TODO `a = b if b else None` expressions TODO evaluate asserts (type safety) python 3 stuff: @@ -14,9 +15,10 @@ TODO annotations ? how ? type evaluation and return? TODO nonlocal statement TODO getattr / __getattr__ / __getattribute__ ? + TODO descriptors (also for classes, for instances it should work) TODO @staticmethod @classmethod (implement descriptors, builtins are done) -TODO variable assignments in classes (see test/completion/classes @230) +TODO variable assignments in classes (see test/completion/classes @230) +1 """ from _compatibility import next, property import sys @@ -219,7 +221,6 @@ class InstanceElement(object): par = InstanceElement(self.instance, par) return par - def get_parent_until(self, *classes): scope = self.var.get_parent_until(*classes) return InstanceElement(self.instance, scope) @@ -233,6 +234,7 @@ class InstanceElement(object): class Class(object): __metaclass__ = CachedMetaClass + def __init__(self, base): self.base = base @@ -288,6 +290,7 @@ class Function(object): """ """ __metaclass__ = CachedMetaClass + def __init__(self, func, is_decorated=False): """ This should not be called directly """ self.base_func = func @@ -330,7 +333,7 @@ class Function(object): # this is here, that the wrapper gets executed f = wrappers[0] - debug.dbg('decorator end') + debug.dbg('decorator end', f) if f != self.base_func and isinstance(f, parsing.Function): f = Function(f) return f @@ -564,12 +567,6 @@ class Execution(Executable): Call the default method with the own instance (self implements all the necessary functions). Add also the params. """ - # result = self.get_params() + parsing.Scope._get_set_vars(self) - # print '\n\ndef', result, 'par', self, self.parent - # print 'set', parsing.Scope._get_set_vars(self) - # print 'set', [r.parent for r in parsing.Scope._get_set_vars(self)] - # print 'para', [r.parent.parent for r in self.get_params()] - # return result return self.get_params() + parsing.Scope._get_set_vars(self) @property @@ -599,7 +596,7 @@ class Execution(Executable): def __getattr__(self, name): if name not in ['indent', 'line_nr', 'imports']: - raise AttributeError('Tried to access %s. Why?' % name) + raise AttributeError('Tried to access %s: %s. Why?' % (name, self)) return getattr(self.base, name) @property @@ -755,14 +752,15 @@ class ArrayElement(object): return "<%s of %s>" % (self.__class__.__name__, self.name) -def get_defined_names_for_position(obj, position=(float('inf'), float('inf'))): +def get_defined_names_for_position(obj, position=None): """ :param position: the position as a row/column tuple, default is infinity. """ names = obj.get_defined_names() # instances have special rules, always return all the possible completions, # because class variables are always valid and the `self.` variables, too. - if not position or isinstance(obj, Instance): + if not position or isinstance(obj, Instance) or isinstance(obj, Function) \ + and isinstance(obj.decorated_func, Instance): return names names_new = [] for n in names: @@ -907,10 +905,7 @@ def get_scopes_for_name(scope, name_str, position=None, search_global=False): if search_global: scope_generator = get_names_for_scope(scope, position=position) else: - if position: - names = get_defined_names_for_position(scope, position) - else: - names = scope.get_defined_names() + names = get_defined_names_for_position(scope, position) scope_generator = iter([(scope, names)]) return remove_statements(filter_name(scope_generator)) diff --git a/mixin/__builtin__.py b/mixin/__builtin__.py index cab49506..b46d4596 100644 --- a/mixin/__builtin__.py +++ b/mixin/__builtin__.py @@ -43,9 +43,9 @@ class staticmethod(): class classmethod(): def __init__(self, func): - self.func = func + self._func = func def __get__(self, obj, cls): - def method(*args, **kwargs): - self.func(cls, *args, **kwargs) - return method + def _method(*args, **kwargs): + self._func(cls, *args, **kwargs) + return _method diff --git a/test/completion/classes.py b/test/completion/classes.py index 51473f9f..0aa083e5 100644 --- a/test/completion/classes.py +++ b/test/completion/classes.py @@ -150,36 +150,9 @@ CallClass()() # ----------------- # properties # ----------------- -class Property(): - 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 + @property def r(self): return 1 @r.setter @@ -187,7 +160,7 @@ class B(): pass def t(self): return '' - p = Property(t) + ##p = property(t) #? [] B().r() diff --git a/test/run.py b/test/run.py index 71799b28..2fc390ad 100755 --- a/test/run.py +++ b/test/run.py @@ -22,12 +22,11 @@ def run_completion_test(correct, source, line_nr, line): try: completions = functions.complete(source, line_nr, 999, completion_test_dir) - except Exception: + except (Exception, functions.evaluate.MultiLevelAttributeError): print('test @%s: %s' % (line_nr-1, line)) print(traceback.format_exc()) return 1 else: - # TODO remove sorted? completions should be sorted? # TODO remove set! duplicates should not be normal comp_str = str(sorted(set([str(c) for c in completions]))) if comp_str != correct: @@ -47,7 +46,7 @@ def run_definition_test(correct, source, line_nr, line, correct_start): completion_test_dir)) try: result = defs(line_nr, 999) - except Exception: + except (Exception, functions.evaluate.MultiLevelAttributeError): print('test @%s: %s' % (line_nr-1, line)) print(traceback.format_exc()) return 1