diff --git a/debug.py b/debug.py new file mode 100644 index 00000000..ceee9e4f --- /dev/null +++ b/debug.py @@ -0,0 +1,7 @@ + +def dbg(*args): + if debug_function: + debug_function(*args) + + +debug_function = None diff --git a/evaluate.py b/evaluate.py index afc01e01..b82e2a84 100644 --- a/evaluate.py +++ b/evaluate.py @@ -1,12 +1,15 @@ import parsing import itertools +import modules + class Exec(object): def __init__(self, base): self.base = base def get_parent_until(self, *args): return self.base.get_parent_until(*args) - + + class Instance(Exec): """ This class is used to evaluate instances. """ @@ -47,6 +50,17 @@ class Execution(Exec): """ Get the return vars of a function. """ + def remove_executions(scope, get_returns=False): + if isinstance(scope, Execution): + # there maybe executions of executions + stmts = scope.get_return_types() + else: + if get_returns: + stmts = scope.returns + else: + stmts = [scope] + return stmts + # check cache try: return Execution.cache[self.base] @@ -54,15 +68,9 @@ class Execution(Exec): # cache is not only here as a cache, but also to prevent an # endless recursion. Execution.cache[self.base] = [] - def remove_executions(scope): - if isinstance(scope, Execution): - # there maybe executions of executions - stmts = scope.get_return_types() - else: - stmts = scope.returns - return stmts + result = [] - stmts = remove_executions(self.base) + stmts = remove_executions(self.base, True) print 'stmts=', stmts, self.base, repr(self) #n += self.function.get_set_vars() @@ -75,6 +83,7 @@ class Execution(Exec): else: print 'addstmt', stmt for followed in follow_statement(stmt): + print 'followed', followed result += remove_executions(followed) print 'ret', stmt @@ -227,6 +236,10 @@ def follow_path(path, input): # dbg('cannot execute:', input) elif isinstance(input, parsing.Import): print 'dini mueter, steile griech!' + try: + modules.follow_module(input) + except modules.ModuleNotFound: + dbg('Module not found: ' + str(input)) else: # TODO check default class methods and return them also result = get_scopes_for_name(input, current) diff --git a/functions.py b/functions.py index 92666f47..da79124d 100644 --- a/functions.py +++ b/functions.py @@ -1,6 +1,9 @@ +import re + import parsing import evaluate -import re +import modules +import debug __all__ = ['complete', 'set_debug_function'] @@ -10,7 +13,7 @@ class ParserError(LookupError): pass -class File(object): +class FileWithCursor(modules.File): """ Manages all files, that are parsed and caches them. Important are the params source and module_name, one of them has to @@ -21,34 +24,15 @@ class File(object): :param row: The row, the user is currently in. Only important for the \ main file. """ - def __init__(self, source=None, module_name=None, row=None): - self.source = source - self.module_name = module_name + def __init__(self, module_name, source, row): + super(FileWithCursor, self).__init__(module_name, source) self.row = row - self.line_cache = None # this two are only used, because there is no nonlocal in Python 2 self._row_temp = None self._relevant_temp = None - if not self.module_name and not self.source: - raise AttributeError("Submit a module name or the source code") - elif self.module_name: - self.load_module() - - self.parser = parsing.PyFuzzyParser(source, row) - - def load_module(self): - pass - - def get_line(self, line): - if not self.line_cache: - self.line_cache = self.source.split('\n') - - if 1 <= line <= len(self.line_cache): - return self.line_cache[line - 1] - else: - return None + self._parser = parsing.PyFuzzyParser(source, row) def get_row_path(self, column): """ Get the path under the cursor. """ @@ -179,14 +163,14 @@ def complete(source, row, column, file_callback=None): row = 140 row = 148 column = 200 - f = File(source=source, row=row) + f = FileWithCursor('__main__', source=source, row=row) scope = f.parser.user_scope - # print a dbg title - dbg() - dbg('-' * 70) - dbg(' ' * 62 + 'complete') - dbg('-' * 70) + # print a debug.dbg title + debug.dbg() + debug.dbg('-' * 70) + debug.dbg(' ' * 62 + 'complete') + debug.dbg('-' * 70) print scope print f.parser.user_scope.get_simple_for_line(row) @@ -194,7 +178,7 @@ def complete(source, row, column, file_callback=None): path = f.get_row_path(column) except ParserError as e: path = [] - dbg(e) + debug.dbg(e) result = [] if path and path[0]: @@ -208,7 +192,7 @@ def complete(source, row, column, file_callback=None): if path: scopes = evaluate.follow_path(scope, tuple(path)) - dbg('possible scopes', scopes) + debug.dbg('possible scopes', scopes) compl = [] for s in scopes: compl += s.get_defined_names() @@ -216,7 +200,7 @@ def complete(source, row, column, file_callback=None): else: compl = evaluate.get_names_for_scope(scope) - dbg('possible-compl', compl) + debug.dbg('possible-compl', compl) # make a partial comparison, because the other options have to # be returned as well. @@ -225,20 +209,9 @@ def complete(source, row, column, file_callback=None): return result -def dbg(*args): - if debug_function: - debug_function(*args) - - def set_debug_function(func_cb): """ You can define a callback debug function to get all the debug messages. :param func_cb: The callback function for debug messages, with n params. """ - global debug_function - debug_function = func_cb - #parsing.debug_function = func_cb - evaluate.debug_function = func_cb - - -debug_function = None + debug.debug_function = func_cb diff --git a/modules.py b/modules.py new file mode 100644 index 00000000..968ce0d7 --- /dev/null +++ b/modules.py @@ -0,0 +1,100 @@ +import imp +import sys + +import debug +import parsing + +files = {} +load_module_cb = None +module_find_path = sys.path[1:] + + +class ModuleNotFound(Exception): + pass + + +class File(object): + """ + Manages all files, that are parsed and caches them. + If the source is not given, it is loaded by load_module. + + :param source: The source code of the file. + :param module_name: The module name of the file. + """ + def __init__(self, module_name, source=None): + self.source = source + self.module_name = module_name + self._line_cache = None + self._parser = None + + @property + def parser(self): + if self._parser: + return self._parser + if not self.module_name and not self.source: + raise AttributeError("Submit a module name or the source code") + elif self.module_name: + return self.load_module() + + def load_module(self): + if not self.source: + if i[0]: # is the file pointer + self.source = open(i[0]).read() + print self.source, 'yesssa' + else: + self.source = '' + print 'shizzel' + self._parser = parsing.PyFuzzyParser(self.source) + return self._parser + + def get_line(self, line): + if not self._line_cache: + self._line_cache = self.source.split('\n') + + if 1 <= line <= len(self._line_cache): + return self._line_cache[line - 1] + else: + return None + + +def follow_module(_import): + """ + follows a module name and returns the parser. + :param name: A name from the parser. + :type name: parsing.Name + """ + def follow_str(ns, string): + print ns, string + if ns: + path = ns[1] + else: + path = module_find_path + debug.dbg('search_module', string, path) + i = imp.find_module(string, path) + return i + + # set path together + ns_list = [] + if _import.from_ns: + ns_list += _import.from_ns.names + if _import.namespace: + ns_list += _import.namespace.names + + # now execute those paths + current_namespace = None + rest = None + for i, s in enumerate(ns_list): + try: + current_namespace = follow_str(current_namespace, s) + except ImportError: + if current_namespace: + rest = ns_list[i:] + else: + raise ModuleNotFound( + 'The module you searched has not been found') + + print 'yay', current_namespace + f = File(current_namespace[2], current_namespace[0].read()) + out = f.parser.top.get_names() + print out + return parser diff --git a/parsing.py b/parsing.py index 44b44ad1..cf1aff85 100644 --- a/parsing.py +++ b/parsing.py @@ -38,6 +38,8 @@ import tokenize import cStringIO import re +import debug + class ParserError(Exception): pass @@ -126,7 +128,7 @@ class Scope(Simple): index = index1 if index1 < index2 and index1 > -1 else index2 prefix = string[:index] d = string[index:] - dbg('add_docstr', d, prefix) + debug.dbg('add_docstr', d, prefix) # now clean docstr d = d.replace('\n', ' ') @@ -137,7 +139,7 @@ class Scope(Simple): d = d[1:] while d[-1] in '"\'\t ': d = d[:-1] - dbg("Scope(%s)::docstr = %s" % (self, d)) + debug.dbg("Scope(%s)::docstr = %s" % (self, d)) self.docstr = d def add_import(self, imp): @@ -420,7 +422,8 @@ class Import(Simple): super(Import, self).__init__(indent, line_nr, line_end) self.namespace = namespace - namespace.parent = self + if namespace: + namespace.parent = self self.alias = alias if alias: @@ -510,7 +513,7 @@ class Statement(Simple): is_chain = False close_brackets = False - dbg('tok_list', self.token_list) + debug.dbg('tok_list', self.token_list) for i, tok_temp in enumerate(self.token_list): #print 'tok', tok_temp, result try: @@ -945,7 +948,7 @@ class PyFuzzyParser(object): start_line = self.line_nr token_type, cname, ind = self.next() if token_type != tokenize.NAME: - dbg("class: syntax error - token is not a name@%s (%s: %s)" \ + debug.dbg("class: syntax error - token is not a name@%s (%s: %s)" \ % (self.line_nr, tokenize.tok_name[token_type], cname)) return None @@ -1075,7 +1078,7 @@ class PyFuzzyParser(object): type, tok, position, dummy, self.parserline = self.gen.next() (self.line_nr, indent) = position if self.line_nr == self.user_line: - dbg('user scope found [%s] =%s' % \ + debug.dbg('user scope found [%s] =%s' % \ (self.parserline.replace('\n', ''), repr(self.scope))) self.user_scope = self.scope self.last_token = self.current @@ -1105,11 +1108,11 @@ class PyFuzzyParser(object): while True: try: token_type, tok, indent = self.next() - dbg('main: tok=[%s] type=[%s] indent=[%s]'\ + debug.dbg('main: tok=[%s] type=[%s] indent=[%s]'\ % (tok, token_type, indent)) while token_type == tokenize.DEDENT and self.scope != self.top: - dbg('dedent', self.scope) + debug.dbg('dedent', self.scope) token_type, tok, indent = self.next() if indent <= self.scope.indent: self.scope.line_end = self.line_nr @@ -1121,7 +1124,7 @@ class PyFuzzyParser(object): while indent <= self.scope.indent \ and token_type in [tokenize.NAME] \ and self.scope != self.top: - dbg('syntax_err, dedent @%s - %s<=%s', \ + debug.dbg('syntax_err, dedent @%s - %s<=%s', \ (self.line_nr, indent, self.scope.indent)) self.scope.line_end = self.line_nr self.scope = self.scope.parent @@ -1132,7 +1135,7 @@ class PyFuzzyParser(object): if func is None: print "function: syntax error@%s" % self.line_nr continue - dbg("new scope: function %s" % (func.name)) + debug.dbg("new scope: function %s" % (func.name)) freshscope = True self.scope = self.scope.add_scope(func, decorators) decorators = [] @@ -1141,7 +1144,7 @@ class PyFuzzyParser(object): if cls is None: continue freshscope = True - dbg("new scope: class %s" % (cls.name)) + debug.dbg("new scope: class %s" % (cls.name)) self.scope = self.scope.add_scope(cls, decorators) decorators = [] # import stuff @@ -1175,7 +1178,7 @@ class PyFuzzyParser(object): if tok == ':': f = Flow('for', statement, indent, self.line_nr, \ value_list) - dbg("new scope: flow for@%s" % (f.line_nr)) + debug.dbg("new scope: flow for@%s" % (f.line_nr)) self.scope = self.scope.add_statement(f) elif tok in ['if', 'while', 'try', 'with'] + extended_flow: @@ -1195,7 +1198,8 @@ class PyFuzzyParser(object): statement.code += ',' + n.get_code() if tok == ':': f = Flow(command, statement, indent, self.line_nr) - dbg("new scope: flow %s@%s" % (command, self.line_nr)) + debug.dbg("new scope: flow %s@%s" + % (command, self.line_nr)) if command in extended_flow: # the last statement has to be another part of # the flow statement @@ -1207,7 +1211,7 @@ class PyFuzzyParser(object): stmt, tok = self._parse_statement(self.current) if stmt: self.scope.add_statement(stmt) - dbg('global_vars', stmt.used_vars) + debug.dbg('global_vars', stmt.used_vars) for name in stmt.used_vars: # add the global to the top, because there it is # important. @@ -1235,14 +1239,6 @@ class PyFuzzyParser(object): except StopIteration: # thrown on EOF break #except StopIteration: - # dbg("parse error: %s, %s @ %s" % + # debug.dbg("parse error: %s, %s @ %s" % # (sys.exc_info()[0], sys.exc_info()[1], self.parserline)) return self.top - - -def dbg(*args): - if debug_function: - debug_function(*args) - - -debug_function = None diff --git a/test.py b/test.py index d11ed14e..0db87136 100644 --- a/test.py +++ b/test.py @@ -130,7 +130,7 @@ import time class c1(): c2,c5 = c1(), c1().c3() def c3(self): - import time as c4 + from os import F_OK as c4 c5 = c4 c5 = 1 return c5+1+'asdf' @@ -145,4 +145,4 @@ c = c1().c3().sleep() asdf = c1; asdf2 = asdf b= asdf2 -c = b().test() +c = b().c3().F_OK