again all sort of import related stuff, relative imports work now! fixes #2

This commit is contained in:
David Halter
2012-09-13 15:03:10 +02:00
parent c2d1b2d865
commit 6f0cbf18db
6 changed files with 71 additions and 38 deletions

View File

@@ -1509,7 +1509,7 @@ def goto(scopes, search_name=None, statement_path_offset=1):
if isinstance(s, imports.ImportPath): if isinstance(s, imports.ImportPath):
try: try:
s = s.follow()[0] s = s.follow()[0]
except imports.ModuleNotFound: except IndexError:
continue continue
else: else:
if not isinstance(s, parsing.Module): if not isinstance(s, parsing.Module):

View File

@@ -193,21 +193,14 @@ def _prepare_goto(position, source_path, module, goto_path,
if isinstance(user_stmt, parsing.Import): if isinstance(user_stmt, parsing.Import):
import_names = user_stmt.get_all_import_names() import_names = user_stmt.get_all_import_names()
count = 0 count = 0
found_count = None kill_count = -1
for i in import_names: for i in import_names:
for name_part in i.names: for name_part in i.names:
count += 1 count += 1
if name_part.start_pos <= position <= name_part.end_pos: if position <= name_part.end_pos:
found_count = count kill_count += 1
if found_count is None:
found_count = count
if is_like_search:
# is_like_search will decrease also one, so change this here.
found_count += 1
else:
return []
scopes = [imports.ImportPath(user_stmt, is_like_search, scopes = [imports.ImportPath(user_stmt, is_like_search,
kill_count=(count - found_count), direct_resolve=True)] kill_count=kill_count, direct_resolve=True)]
else: else:
# just parse one statement, take it and evaluate it # just parse one statement, take it and evaluate it
r = parsing.PyFuzzyParser(goto_path, source_path, no_docstr=True) r = parsing.PyFuzzyParser(goto_path, source_path, no_docstr=True)
@@ -244,10 +237,7 @@ def get_definition(source, line, column, source_path):
for s in scopes.copy(): for s in scopes.copy():
if isinstance(s, imports.ImportPath): if isinstance(s, imports.ImportPath):
scopes.remove(s) scopes.remove(s)
try: scopes.update(resolve_import_paths(set(s.follow())))
scopes.update(resolve_import_paths(set(s.follow())))
except imports.ModuleNotFound:
pass
return scopes return scopes
pos = (line, column) pos = (line, column)

View File

@@ -21,8 +21,14 @@ class ImportPath(object):
""" """
An ImportPath is the path of a `parsing.Import` object. An ImportPath is the path of a `parsing.Import` object.
""" """
class GlobalNamespace(object): class _GlobalNamespace(object):
pass def get_defined_names(self):
return []
def get_imports(self):
return []
GlobalNamespace = _GlobalNamespace()
def __init__(self, import_stmt, is_like_search=False, kill_count=0, def __init__(self, import_stmt, is_like_search=False, kill_count=0,
direct_resolve=False): direct_resolve=False):
@@ -45,7 +51,6 @@ class ImportPath(object):
for i in range(kill_count + int(is_like_search)): for i in range(kill_count + int(is_like_search)):
self.import_path.pop() self.import_path.pop()
def __repr__(self): def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, self.import_stmt) return '<%s: %s>' % (self.__class__.__name__, self.import_stmt)
@@ -79,8 +84,13 @@ class ImportPath(object):
names = [] names = []
for scope in self.follow(): for scope in self.follow():
if scope is ImportPath.GlobalNamespace: if scope is ImportPath.GlobalNamespace:
names += self.get_module_names() if self.import_stmt.relative_count == 0:
names += self.get_module_names([self.file_path]) names += self.get_module_names()
path = os.path.abspath(self.file_path)
for i in range(self.import_stmt.relative_count - 1):
path = os.path.dirname(path)
names += self.get_module_names([path])
else: else:
if on_import_stmt and isinstance(scope, parsing.Module) \ if on_import_stmt and isinstance(scope, parsing.Module) \
and scope.path.endswith('__init__.py'): and scope.path.endswith('__init__.py'):
@@ -117,7 +127,13 @@ class ImportPath(object):
Returns the imported modules. Returns the imported modules.
""" """
if self.import_path: if self.import_path:
scope, rest = self._follow_file_system() try:
scope, rest = self._follow_file_system()
except ModuleNotFound:
debug.warning('Module not found: ' + str(self.import_stmt))
return []
return [ImportPath.GlobalNamespace]
if len(rest) > 1 or rest and self.is_like_search: if len(rest) > 1 or rest and self.is_like_search:
scopes = [] scopes = []
elif rest: elif rest:
@@ -214,10 +230,7 @@ def strip_imports(scopes):
if isinstance(s, parsing.Import): if isinstance(s, parsing.Import):
# this is something like a statement following. # this is something like a statement following.
evaluate.statement_path.append(s) evaluate.statement_path.append(s)
try: result += ImportPath(s).follow()
result += ImportPath(s).follow()
except ModuleNotFound:
debug.warning('Module not found: ' + str(s))
else: else:
result.append(s) result.append(s)
return result return result

View File

@@ -515,7 +515,7 @@ class Import(Simple):
:type defunct: bool :type defunct: bool
""" """
def __init__(self, start_pos, end_pos, namespace, alias=None, \ def __init__(self, start_pos, end_pos, namespace, alias=None, \
from_ns=None, star=False, relative_count=None, defunct=False): from_ns=None, star=False, relative_count=0, defunct=False):
super(Import, self).__init__(start_pos, end_pos) super(Import, self).__init__(start_pos, end_pos)
self.namespace = namespace self.namespace = namespace
@@ -1138,6 +1138,10 @@ class PyFuzzyParser(object):
else: else:
token_type, tok = pre_used_token token_type, tok = pre_used_token
if token_type != tokenize.NAME and tok != '*':
# token maybe a name or star
return (None, token_type, tok)
append((tok, self.start_pos)) append((tok, self.start_pos))
first_pos = self.start_pos first_pos = self.start_pos
while True: while True:
@@ -1175,6 +1179,8 @@ class PyFuzzyParser(object):
while True: while True:
defunct = False defunct = False
token_type, tok = self.next() token_type, tok = self.next()
if token_type == tokenize.ENDMARKER:
break
if brackets and tok == '\n': if brackets and tok == '\n':
self.next() self.next()
if tok == '(': # python allows only one `(` in the statement. if tok == '(': # python allows only one `(` in the statement.
@@ -1421,15 +1427,16 @@ class PyFuzzyParser(object):
else: else:
n, token_type, tok = self._parsedotname(self.current) n, token_type, tok = self._parsedotname(self.current)
tok_list.pop() # removed last entry, because we add Name tok_list.pop() # removed last entry, because we add Name
tok_list.append(n) if n:
if tok == '(': tok_list.append(n)
# it must be a function if tok == '(':
used_funcs.append(n) # it must be a function
else: used_funcs.append(n)
used_vars.append(n) else:
if string and re.match(r'[\w\d\'"]', string[-1]): used_vars.append(n)
string += ' ' if string and re.match(r'[\w\d\'"]', string[-1]):
string += ".".join(n.names) string += ' '
string += ".".join(n.names)
continue continue
elif '=' in tok and not tok in ['>=', '<=', '==', '!=']: elif '=' in tok and not tok in ['>=', '<=', '==', '!=']:
# there has been an assignement -> change vars # there has been an assignement -> change vars
@@ -1474,7 +1481,7 @@ class PyFuzzyParser(object):
except AttributeError: except AttributeError:
debug.warning('return in non-function') debug.warning('return in non-function')
if list_comp or tok in always_break: if tok in always_break:
self.gen.push_back(self._current_full) self.gen.push_back(self._current_full)
return stmt, tok return stmt, tok
@@ -1584,9 +1591,11 @@ class PyFuzzyParser(object):
debug.warning("from: syntax error@%s" % debug.warning("from: syntax error@%s" %
self.start_pos[0]) self.start_pos[0])
defunct = True defunct = True
if tok != 'import':
self.gen.push_back(self._current_full)
names = self._parseimportlist() names = self._parseimportlist()
for name, alias, defunct2 in names: for name, alias, defunct2 in names:
star = name.names[0] == '*' star = name is not None and name.names[0] == '*'
if star: if star:
name = None name = None
i = Import(first_pos, self.end_pos, name, alias, mod, i = Import(first_pos, self.end_pos, name, alias, mod,
@@ -1596,6 +1605,7 @@ class PyFuzzyParser(object):
if not names: if not names:
i = Import(first_pos, self.end_pos, mod, defunct=True, i = Import(first_pos, self.end_pos, mod, defunct=True,
relative_count=relative_count) relative_count=relative_count)
print i, repr(mod)
self._check_user_stmt(i) self._check_user_stmt(i)
self.scope.add_import(i) self.scope.add_import(i)
self.freshscope = False self.freshscope = False

View File

@@ -172,6 +172,10 @@ mod1.
#? str() #? str()
imp_tree.a imp_tree.a
# -----------------
# special positions -> edge cases
# -----------------
import datetime import datetime
#? 6 datetime #? 6 datetime
@@ -187,3 +191,13 @@ from import_tree. import pkg
#? 18 ['pkg'] #? 18 ['pkg']
from import_tree.p import pkg from import_tree.p import pkg
#? 17 ['import_tree']
from .import_tree import
#? 10 ['run']
from ..run import
#? ['run']
from .. import run
#? []
from not_a_module import

View File

@@ -87,6 +87,12 @@ class TestRegression(unittest.TestCase):
assert self.get_def("import sys_blabla", (1, 8)) == [] assert self.get_def("import sys_blabla", (1, 8)) == []
assert len(self.get_def("import sys", (1, 8))) == 1 assert len(self.get_def("import sys", (1, 8))) == 1
def test_complete_on_empty_import(self):
# should just list the files in the directory
assert 10 < len(self.complete("from .", (1, 5))) < 30
assert 10 < len(self.complete("from . import", (1, 5))) < 30
assert 10 < len(self.complete("from . import classes", (1, 5))) < 30
def test_new(self): def test_new(self):
""" This is just to try out things, removing or deleting it is ok. """ """ This is just to try out things, removing or deleting it is ok. """
s = ("def abc(): pass\n" s = ("def abc(): pass\n"