mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-07 22:44:27 +08:00
cleaned up and fixed most cases of import related stuff, fixes #2
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
Jedi - a clever Python auto-completion
|
Jedi - a clever Python auto-completion
|
||||||
============================
|
============================
|
||||||
|
|
||||||
** now in alpha testing phase **
|
**now in alpha testing phase**
|
||||||
*If you have any comments or feature request, please tell me! I really want to
|
*If you have any comments or feature request, please tell me! I really want to
|
||||||
know, what you think of Jedi.*
|
know, what you think of Jedi.*
|
||||||
|
|
||||||
@@ -55,8 +55,8 @@ Jedi supports many of the widely used Python features:
|
|||||||
However, it does not yet support (and probably will in future versions, because
|
However, it does not yet support (and probably will in future versions, because
|
||||||
they are on my todo list):
|
they are on my todo list):
|
||||||
|
|
||||||
- getattr() / __getattr__ / __getattribute__
|
- `getattr()` / `__getattr__` / `__getattribute__`
|
||||||
- sys.path modifications
|
- `sys.path` modifications
|
||||||
- manipulations of instances outside the instance variables, without using
|
- manipulations of instances outside the instance variables, without using
|
||||||
functions
|
functions
|
||||||
- mro
|
- mro
|
||||||
|
|||||||
36
evaluate.py
36
evaluate.py
@@ -1448,39 +1448,17 @@ def goto(scopes, search_name=None, statement_path_offset=1):
|
|||||||
except imports.ModuleNotFound:
|
except imports.ModuleNotFound:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
try:
|
if not isinstance(s, parsing.Module):
|
||||||
s = statement_path[0]
|
s = statement_path[0]
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
definitions.append(s)
|
definitions.append(s)
|
||||||
else:
|
else:
|
||||||
def remove_unreal_imports(names):
|
|
||||||
"""
|
|
||||||
These imports are only virtual, because of multi-line imports.
|
|
||||||
"""
|
|
||||||
new_names = []
|
|
||||||
for n in names:
|
|
||||||
par = n.parent()
|
|
||||||
# This is a special case: If the Import is "virtual" (which
|
|
||||||
# means the position is not defined), follow those modules.
|
|
||||||
if isinstance(par, parsing.Import) and not par.start_pos[0]:
|
|
||||||
module_count = 0
|
|
||||||
for scope in imports.ImportPath(par).follow():
|
|
||||||
if isinstance(scope, parsing.Import):
|
|
||||||
temp = scope.get_defined_names()
|
|
||||||
new_names += remove_unreal_imports(temp)
|
|
||||||
elif isinstance(scope, parsing.Module) \
|
|
||||||
and not module_count:
|
|
||||||
# only first module (others are star imports)
|
|
||||||
module_count += 1
|
|
||||||
new_names.append(scope.get_module_name(n.names))
|
|
||||||
else:
|
|
||||||
new_names.append(n)
|
|
||||||
return new_names
|
|
||||||
|
|
||||||
names = []
|
names = []
|
||||||
for s in scopes:
|
for s in scopes:
|
||||||
names += s.get_defined_names()
|
if isinstance(s, imports.ImportPath):
|
||||||
names = remove_unreal_imports(names)
|
modules = s.follow()
|
||||||
|
if modules:
|
||||||
|
names.append(modules[0].get_module_name())
|
||||||
|
else:
|
||||||
|
names += s.get_defined_names()
|
||||||
definitions = [n for n in names if n.names[-1] == search_name]
|
definitions = [n for n in names if n.names[-1] == search_name]
|
||||||
return definitions
|
return definitions
|
||||||
|
|||||||
34
functions.py
34
functions.py
@@ -144,7 +144,7 @@ def complete(source, line, column, source_path):
|
|||||||
path, dot, like = _get_completion_parts(path)
|
path, dot, like = _get_completion_parts(path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
scopes = _prepare_goto(source, pos, source_path, f, path, True)
|
scopes = _prepare_goto(pos, source_path, f, path, True)
|
||||||
except NotFoundError:
|
except NotFoundError:
|
||||||
scope_generator = evaluate.get_names_for_scope(f.parser.user_scope,
|
scope_generator = evaluate.get_names_for_scope(f.parser.user_scope,
|
||||||
pos)
|
pos)
|
||||||
@@ -159,7 +159,11 @@ def complete(source, line, column, source_path):
|
|||||||
# TODO is this really the right way? just ignore the functions? \
|
# TODO is this really the right way? just ignore the functions? \
|
||||||
# do the magic functions first? and then recheck here?
|
# do the magic functions first? and then recheck here?
|
||||||
if not isinstance(s, evaluate.Function):
|
if not isinstance(s, evaluate.Function):
|
||||||
for c in s.get_defined_names():
|
if isinstance(s, imports.ImportPath):
|
||||||
|
names = s.get_defined_names(on_import_stmt=True)
|
||||||
|
else:
|
||||||
|
names = s.get_defined_names()
|
||||||
|
for c in names:
|
||||||
completions.append((c, s))
|
completions.append((c, s))
|
||||||
|
|
||||||
completions = [(c, s) for c, s in completions
|
completions = [(c, s) for c, s in completions
|
||||||
@@ -174,7 +178,7 @@ def complete(source, line, column, source_path):
|
|||||||
return c
|
return c
|
||||||
|
|
||||||
|
|
||||||
def _prepare_goto(source, position, source_path, module, goto_path,
|
def _prepare_goto(position, source_path, module, goto_path,
|
||||||
is_like_search=False):
|
is_like_search=False):
|
||||||
scope = module.parser.user_scope
|
scope = module.parser.user_scope
|
||||||
debug.dbg('start: %s in %s' % (goto_path, scope))
|
debug.dbg('start: %s in %s' % (goto_path, scope))
|
||||||
@@ -187,7 +191,23 @@ def _prepare_goto(source, position, source_path, module, goto_path,
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
if isinstance(user_stmt, parsing.Import):
|
if isinstance(user_stmt, parsing.Import):
|
||||||
scopes = [imports.ImportPath(user_stmt, is_like_search)]
|
import_names = user_stmt.get_all_import_names()
|
||||||
|
count = 0
|
||||||
|
found_count = None
|
||||||
|
for i in import_names:
|
||||||
|
for name_part in i.names:
|
||||||
|
count += 1
|
||||||
|
if name_part.start_pos <= position <= name_part.end_pos:
|
||||||
|
found_count = count
|
||||||
|
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,
|
||||||
|
kill_count=(count - found_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)
|
||||||
@@ -231,7 +251,7 @@ def get_definition(source, line, column, source_path):
|
|||||||
op = f.get_operator_under_cursor()
|
op = f.get_operator_under_cursor()
|
||||||
scopes = set([keywords.get_operator(op, pos)] if op else [])
|
scopes = set([keywords.get_operator(op, pos)] if op else [])
|
||||||
else:
|
else:
|
||||||
scopes = set(_prepare_goto(source, pos, source_path, f, goto_path))
|
scopes = set(_prepare_goto(pos, source_path, f, goto_path))
|
||||||
|
|
||||||
for s in scopes.copy():
|
for s in scopes.copy():
|
||||||
if isinstance(s, imports.ImportPath):
|
if isinstance(s, imports.ImportPath):
|
||||||
@@ -265,7 +285,7 @@ def goto(source, line, column, source_path):
|
|||||||
if next(context) in ('class', 'def'):
|
if next(context) in ('class', 'def'):
|
||||||
definitions = set([f.parser.user_scope])
|
definitions = set([f.parser.user_scope])
|
||||||
else:
|
else:
|
||||||
scopes = _prepare_goto(source, pos, source_path, f, goto_path)
|
scopes = _prepare_goto(pos, source_path, f, goto_path)
|
||||||
definitions = evaluate.goto(scopes, search_name_new)
|
definitions = evaluate.goto(scopes, search_name_new)
|
||||||
|
|
||||||
d = [Definition(d) for d in set(definitions)]
|
d = [Definition(d) for d in set(definitions)]
|
||||||
@@ -303,7 +323,7 @@ def related_names(source, line, column, source_path):
|
|||||||
elif isinstance(f.parser.user_stmt, (parsing.Param, parsing.Import)):
|
elif isinstance(f.parser.user_stmt, (parsing.Param, parsing.Import)):
|
||||||
definitions = [f.parser.user_stmt]
|
definitions = [f.parser.user_stmt]
|
||||||
else:
|
else:
|
||||||
scopes = _prepare_goto(source, pos, source_path, f, goto_path)
|
scopes = _prepare_goto(pos, source_path, f, goto_path)
|
||||||
definitions = evaluate.goto(scopes, search_name_new)
|
definitions = evaluate.goto(scopes, search_name_new)
|
||||||
|
|
||||||
module = set([d.get_parent_until() for d in definitions])
|
module = set([d.get_parent_until() for d in definitions])
|
||||||
|
|||||||
43
imports.py
43
imports.py
@@ -24,23 +24,27 @@ class ImportPath(object):
|
|||||||
class GlobalNamespace(object):
|
class GlobalNamespace(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __init__(self, import_stmt, is_like_search=False):
|
def __init__(self, import_stmt, is_like_search=False, kill_count=0,
|
||||||
|
direct_resolve=False):
|
||||||
self.import_stmt = import_stmt
|
self.import_stmt = import_stmt
|
||||||
|
self.is_like_search = is_like_search
|
||||||
|
self.direct_resolve = direct_resolve
|
||||||
|
self.is_partial_import = bool(kill_count)
|
||||||
|
self.file_path = os.path.dirname(import_stmt.get_parent_until().path)
|
||||||
|
|
||||||
|
# rest is import_path resolution
|
||||||
self.import_path = []
|
self.import_path = []
|
||||||
if import_stmt.from_ns:
|
if import_stmt.from_ns:
|
||||||
self.import_path += import_stmt.from_ns.names
|
self.import_path += import_stmt.from_ns.names
|
||||||
if import_stmt.namespace:
|
if import_stmt.namespace:
|
||||||
if self.is_nested_import():
|
if self.is_nested_import() and not direct_resolve:
|
||||||
self.import_path.append(import_stmt.namespace.names[0])
|
self.import_path.append(import_stmt.namespace.names[0])
|
||||||
else:
|
else:
|
||||||
self.import_path += import_stmt.namespace.names
|
self.import_path += import_stmt.namespace.names
|
||||||
|
|
||||||
self.is_like_search = is_like_search
|
for i in range(kill_count + int(is_like_search)):
|
||||||
if is_like_search:
|
|
||||||
# drop one path part, because that is used by the like search
|
|
||||||
self.import_path.pop()
|
self.import_path.pop()
|
||||||
|
|
||||||
self.file_path = os.path.dirname(import_stmt.get_parent_until().path)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<%s: %s>' % (self.__class__.__name__, self.import_stmt)
|
return '<%s: %s>' % (self.__class__.__name__, self.import_stmt)
|
||||||
@@ -52,7 +56,8 @@ class ImportPath(object):
|
|||||||
>>> import foo.bar
|
>>> import foo.bar
|
||||||
"""
|
"""
|
||||||
return not self.import_stmt.alias and not self.import_stmt.from_ns \
|
return not self.import_stmt.alias and not self.import_stmt.from_ns \
|
||||||
and len(self.import_stmt.namespace.names) > 1
|
and len(self.import_stmt.namespace.names) > 1 \
|
||||||
|
and not self.direct_resolve
|
||||||
|
|
||||||
def get_nested_import(self, parent):
|
def get_nested_import(self, parent):
|
||||||
"""
|
"""
|
||||||
@@ -70,16 +75,30 @@ class ImportPath(object):
|
|||||||
debug.dbg('Generated a nested import: %s' % new)
|
debug.dbg('Generated a nested import: %s' % new)
|
||||||
return new
|
return new
|
||||||
|
|
||||||
def get_defined_names(self):
|
def get_defined_names(self, on_import_stmt=False):
|
||||||
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()
|
names += self.get_module_names()
|
||||||
names += self.get_module_names([self.file_path])
|
names += self.get_module_names([self.file_path])
|
||||||
else:
|
else:
|
||||||
for s, n in evaluate.get_names_for_scope(scope,
|
if on_import_stmt and isinstance(scope, parsing.Module) \
|
||||||
|
and scope.path.endswith('__init__.py'):
|
||||||
|
pkg_path = os.path.dirname(scope.path)
|
||||||
|
names += self.get_module_names([pkg_path])
|
||||||
|
for s, scope_names in evaluate.get_names_for_scope(scope,
|
||||||
include_builtin=False):
|
include_builtin=False):
|
||||||
names += n
|
for n in scope_names:
|
||||||
|
if not isinstance(n.parent(), parsing.Import) \
|
||||||
|
and (self.import_stmt.from_ns is None \
|
||||||
|
or self.is_partial_import):
|
||||||
|
# from_ns must be defined to access module
|
||||||
|
# values plus a partial import means that there
|
||||||
|
# is something after the import, which
|
||||||
|
# automatically implies that there must not be
|
||||||
|
# any non-module scope.
|
||||||
|
continue
|
||||||
|
names.append(n)
|
||||||
return names
|
return names
|
||||||
|
|
||||||
def get_module_names(self, search_path=None):
|
def get_module_names(self, search_path=None):
|
||||||
@@ -99,7 +118,7 @@ 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()
|
scope, rest = self._follow_file_system()
|
||||||
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:
|
||||||
@@ -119,7 +138,7 @@ class ImportPath(object):
|
|||||||
debug.dbg('after import', scopes)
|
debug.dbg('after import', scopes)
|
||||||
return scopes
|
return scopes
|
||||||
|
|
||||||
def follow_file_system(self):
|
def _follow_file_system(self):
|
||||||
"""
|
"""
|
||||||
Find a module with a path (of the module, like usb.backend.libusb10).
|
Find a module with a path (of the module, like usb.backend.libusb10).
|
||||||
"""
|
"""
|
||||||
|
|||||||
17
parsing.py
17
parsing.py
@@ -35,6 +35,7 @@ import tokenize
|
|||||||
import re
|
import re
|
||||||
import keyword
|
import keyword
|
||||||
import weakref
|
import weakref
|
||||||
|
import os
|
||||||
|
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
@@ -287,8 +288,12 @@ class Module(Scope):
|
|||||||
n += self.global_vars
|
n += self.global_vars
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def get_module_name(self, names):
|
def get_module_name(self):
|
||||||
""" This is used for the goto function. """
|
""" This is used for the goto function. """
|
||||||
|
sep = (os.path.sep,) * 2
|
||||||
|
r = re.search(r'([^%s]+?)(%s__init__)?(\.py)?$' % sep, self.path)
|
||||||
|
string = r.group(1)
|
||||||
|
names = [(string, (0, 0))]
|
||||||
if not self._name:
|
if not self._name:
|
||||||
self._name = Name(names, self.start_pos, self.end_pos, self)
|
self._name = Name(names, self.start_pos, self.end_pos, self)
|
||||||
return self._name
|
return self._name
|
||||||
@@ -557,6 +562,16 @@ class Import(Simple):
|
|||||||
else:
|
else:
|
||||||
return [self.namespace]
|
return [self.namespace]
|
||||||
|
|
||||||
|
def get_all_import_names(self):
|
||||||
|
n = []
|
||||||
|
if self.from_ns:
|
||||||
|
n.append(self.from_ns)
|
||||||
|
if self.namespace:
|
||||||
|
n.append(self.namespace)
|
||||||
|
if self.alias:
|
||||||
|
n.append(self.alias)
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
class Statement(Simple):
|
class Statement(Simple):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -165,6 +165,25 @@ from . import import_tree as imp_tree
|
|||||||
#? str()
|
#? str()
|
||||||
imp_tree.a
|
imp_tree.a
|
||||||
|
|
||||||
from . import datetime
|
from . import datetime as mod1
|
||||||
#? []
|
#? []
|
||||||
mod1.
|
mod1.
|
||||||
|
|
||||||
|
#? str()
|
||||||
|
imp_tree.a
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
#? 6 datetime
|
||||||
|
from datetime.time import time
|
||||||
|
|
||||||
|
#? []
|
||||||
|
import datetime.
|
||||||
|
#? []
|
||||||
|
import datetime.date
|
||||||
|
|
||||||
|
#? 18 ['mod1', 'random', 'pkg']
|
||||||
|
from import_tree. import pkg
|
||||||
|
|
||||||
|
#? 18 ['pkg']
|
||||||
|
from import_tree.p import pkg
|
||||||
|
|||||||
2
test/completion/thirdparty/pylab_.py
vendored
2
test/completion/thirdparty/pylab_.py
vendored
@@ -5,7 +5,7 @@ pylab.
|
|||||||
#! ['module numpy']
|
#! ['module numpy']
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
#! ['import random', 'module random']
|
#! ['module random']
|
||||||
import numpy.random
|
import numpy.random
|
||||||
|
|
||||||
#? ['array2string']
|
#? ['array2string']
|
||||||
|
|||||||
Reference in New Issue
Block a user