forked from VimPlug/jedi
Add py__package__ to the ModuleWrapper, which makes relative imports easy to implement and fixed a lot of other things.
This commit is contained in:
@@ -81,6 +81,19 @@ class ImportWrapper(pr.Base):
|
|||||||
try:
|
try:
|
||||||
module = self._evaluator.wrap(self._import.get_parent_until())
|
module = self._evaluator.wrap(self._import.get_parent_until())
|
||||||
import_path = self._import.path_for_name(self._name)
|
import_path = self._import.path_for_name(self._name)
|
||||||
|
from_import_name = None
|
||||||
|
try:
|
||||||
|
from_names = self._import.get_from_names()
|
||||||
|
except AttributeError:
|
||||||
|
# Is an import_name
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if len(from_names) + 1 == len(import_path):
|
||||||
|
# We have to fetch the from_names part first and then check
|
||||||
|
# if from_names exists in the modules.
|
||||||
|
from_import_name = import_path[-1]
|
||||||
|
import_path = from_names
|
||||||
|
|
||||||
importer = get_importer(self._evaluator, tuple(import_path),
|
importer = get_importer(self._evaluator, tuple(import_path),
|
||||||
module, self._import.level)
|
module, self._import.level)
|
||||||
try:
|
try:
|
||||||
@@ -90,34 +103,50 @@ class ImportWrapper(pr.Base):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
if module is None:
|
if module is None:
|
||||||
|
# TODO does that really happen? Why?
|
||||||
return []
|
return []
|
||||||
|
|
||||||
#if self._import.is_nested() and not self.nested_resolve:
|
#if self._import.is_nested() and not self.nested_resolve:
|
||||||
# scopes = [NestedImportModule(module, self._import)]
|
# scopes = [NestedImportModule(module, self._import)]
|
||||||
scopes = [module]
|
types = [module]
|
||||||
|
|
||||||
|
if from_import_name is not None:
|
||||||
|
types = list(chain.from_iterable(
|
||||||
|
self._evaluator.find_types(s, from_import_name, is_goto)
|
||||||
|
for s in types))
|
||||||
|
if not types:
|
||||||
|
importer = get_importer(self._evaluator, tuple(import_path),
|
||||||
|
module, self._import.level)
|
||||||
|
module, _ = importer.follow_file_system()
|
||||||
|
if module is None:
|
||||||
|
types = []
|
||||||
|
else:
|
||||||
|
types = [module]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# goto only accepts `Name`
|
# goto only accepts `Name`
|
||||||
if is_goto and not rest:
|
if is_goto and not rest:
|
||||||
scopes = [s.name for s in scopes]
|
types = [s.name for s in types]
|
||||||
|
|
||||||
# follow the rest of the import (not FS -> classes, functions)
|
# follow the rest of the import (not FS -> classes, functions)
|
||||||
if rest:
|
if rest:
|
||||||
if is_goto:
|
if is_goto:
|
||||||
scopes = list(chain.from_iterable(
|
types = list(chain.from_iterable(
|
||||||
self._evaluator.find_types(s, rest[0], is_goto=True)
|
self._evaluator.find_types(s, rest[0], is_goto=True)
|
||||||
for s in scopes))
|
for s in types))
|
||||||
else:
|
else:
|
||||||
if self._import.type == 'import_from' \
|
if self._import.type == 'import_from' \
|
||||||
or importer.str_import_path == ('os', 'path'):
|
or importer.str_import_path == ('os', 'path'):
|
||||||
scopes = importer.follow_rest(scopes[0], rest)
|
types = importer.follow_rest(types[0], rest)
|
||||||
else:
|
else:
|
||||||
scopes = []
|
types = []
|
||||||
debug.dbg('after import: %s', scopes)
|
debug.dbg('after import: %s', types)
|
||||||
if not scopes:
|
#if not types:
|
||||||
analysis.add(self._evaluator, 'import-error', importer.import_path[-1])
|
# analysis.add(self._evaluator, 'import-error', importer.import_path[-1])
|
||||||
finally:
|
finally:
|
||||||
self._evaluator.recursion_detector.pop_stmt()
|
self._evaluator.recursion_detector.pop_stmt()
|
||||||
return scopes
|
return types
|
||||||
|
|
||||||
|
|
||||||
class NestedImportModule(pr.Module):
|
class NestedImportModule(pr.Module):
|
||||||
@@ -158,7 +187,7 @@ def get_importer(evaluator, import_path, module, level=0):
|
|||||||
cache and speeds up libraries like ``numpy``.
|
cache and speeds up libraries like ``numpy``.
|
||||||
"""
|
"""
|
||||||
if level:
|
if level:
|
||||||
base = module.py__name__().split('.')
|
base = module.py__package__().split('.')
|
||||||
if level > len(base):
|
if level > len(base):
|
||||||
# TODO add import error.
|
# TODO add import error.
|
||||||
debug.warning('Attempted relative import beyond top-level package.')
|
debug.warning('Attempted relative import beyond top-level package.')
|
||||||
@@ -171,6 +200,7 @@ def get_importer(evaluator, import_path, module, level=0):
|
|||||||
|
|
||||||
check_import_path = tuple(unicode(i) for i in import_path)
|
check_import_path = tuple(unicode(i) for i in import_path)
|
||||||
try:
|
try:
|
||||||
|
print(check_import_path)
|
||||||
return evaluator.import_cache[check_import_path]
|
return evaluator.import_cache[check_import_path]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
importer = _Importer(evaluator, import_path, module, level=0)
|
importer = _Importer(evaluator, import_path, module, level=0)
|
||||||
@@ -452,7 +482,8 @@ class _Importer(object):
|
|||||||
paths = base.py__path__()
|
paths = base.py__path__()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# The module is not a package.
|
# The module is not a package.
|
||||||
raise NotImplementedError
|
analysis.add(self._evaluator, 'import-error', import_path[-1])
|
||||||
|
return None
|
||||||
else:
|
else:
|
||||||
debug.dbg('search_module %s in paths %s', module_name, paths)
|
debug.dbg('search_module %s in paths %s', module_name, paths)
|
||||||
for path in paths:
|
for path in paths:
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ __
|
|||||||
import os
|
import os
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import imp
|
import imp
|
||||||
|
import re
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
from jedi._compatibility import use_metaclass, unicode, Python3Method
|
from jedi._compatibility import use_metaclass, unicode, Python3Method
|
||||||
@@ -760,6 +761,14 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
|
|||||||
def name(self):
|
def name(self):
|
||||||
return helpers.FakeName(unicode(self.base.name), self, (1, 0))
|
return helpers.FakeName(unicode(self.base.name), self, (1, 0))
|
||||||
|
|
||||||
|
def _get_init_directory(self):
|
||||||
|
for suffix, _, _ in imp.get_suffixes():
|
||||||
|
ending = '__init__' + suffix
|
||||||
|
if self.py__file__().endswith(ending):
|
||||||
|
# Remove the ending, including the separator.
|
||||||
|
return self.py__file__()[:-len(ending) - 1]
|
||||||
|
return None
|
||||||
|
|
||||||
def py__name__(self):
|
def py__name__(self):
|
||||||
for name, module in self._evaluator.modules.items():
|
for name, module in self._evaluator.modules.items():
|
||||||
if module == self:
|
if module == self:
|
||||||
@@ -768,6 +777,12 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
|
|||||||
def py__file__(self):
|
def py__file__(self):
|
||||||
return self._module.path
|
return self._module.path
|
||||||
|
|
||||||
|
def py__package__(self):
|
||||||
|
if self._get_init_directory() is None:
|
||||||
|
return re.sub(r'\.?[^\.]+$', '', self.py__name__())
|
||||||
|
else:
|
||||||
|
return self.py__name__()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def py__path__(self):
|
def py__path__(self):
|
||||||
"""
|
"""
|
||||||
@@ -778,14 +793,12 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
|
|||||||
def return_value():
|
def return_value():
|
||||||
return [path]
|
return [path]
|
||||||
|
|
||||||
for suffix, _, _ in imp.get_suffixes():
|
path = self._get_init_directory()
|
||||||
ending = '__init__' + suffix
|
|
||||||
if self.py__file__().endswith(ending):
|
if path is None:
|
||||||
# Remove the ending, including the separator.
|
|
||||||
path = self.py__file__()[:-len(ending) - 1]
|
|
||||||
return return_value
|
|
||||||
else:
|
|
||||||
raise AttributeError('Only packages have __path__ attributes.')
|
raise AttributeError('Only packages have __path__ attributes.')
|
||||||
|
else:
|
||||||
|
return return_value
|
||||||
|
|
||||||
@memoize_default()
|
@memoize_default()
|
||||||
def _sub_modules_dict(self):
|
def _sub_modules_dict(self):
|
||||||
|
|||||||
@@ -948,6 +948,17 @@ class ImportFrom(Import):
|
|||||||
return dict((alias, name) for name, alias in self._as_name_tuples()
|
return dict((alias, name) for name, alias in self._as_name_tuples()
|
||||||
if alias is not None)
|
if alias is not None)
|
||||||
|
|
||||||
|
def get_from_names(self):
|
||||||
|
for n in self.children[1:]:
|
||||||
|
if n not in ('.', '...'):
|
||||||
|
break
|
||||||
|
if is_node(n, 'dotted_name'): # from x.y import
|
||||||
|
return n.children[::2]
|
||||||
|
elif n == 'import': # from . import
|
||||||
|
return []
|
||||||
|
else: # from x import
|
||||||
|
return [n]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def level(self):
|
def level(self):
|
||||||
"""The level parameter of ``__import__``."""
|
"""The level parameter of ``__import__``."""
|
||||||
@@ -987,15 +998,7 @@ class ImportFrom(Import):
|
|||||||
The import paths defined in an import statement. Typically an array
|
The import paths defined in an import statement. Typically an array
|
||||||
like this: ``[<Name: datetime>, <Name: date>]``.
|
like this: ``[<Name: datetime>, <Name: date>]``.
|
||||||
"""
|
"""
|
||||||
for n in self.children[1:]:
|
dotted = self.get_from_names()
|
||||||
if n not in ('.', '...'):
|
|
||||||
break
|
|
||||||
if is_node(n, 'dotted_name'): # from x.y import
|
|
||||||
dotted = n.children[::2]
|
|
||||||
elif n == 'import': # from . import
|
|
||||||
dotted = []
|
|
||||||
else: # from x import
|
|
||||||
dotted = [n]
|
|
||||||
|
|
||||||
if self.children[-1] == '*':
|
if self.children[-1] == '*':
|
||||||
return [dotted]
|
return [dotted]
|
||||||
|
|||||||
@@ -119,8 +119,7 @@ def test_import_priorities():
|
|||||||
See also #536.
|
See also #536.
|
||||||
"""
|
"""
|
||||||
from import_tree import the_pkg, invisible_pkg
|
from import_tree import the_pkg, invisible_pkg
|
||||||
# TODO currently not enabled, later we should actually fix this.
|
#? int()
|
||||||
##? int()
|
|
||||||
invisible_pkg
|
invisible_pkg
|
||||||
# The renamed invisible_pkg in that module (look at ``__init__.py``!)
|
# The renamed invisible_pkg in that module (look at ``__init__.py``!)
|
||||||
#? float()
|
#? float()
|
||||||
|
|||||||
Reference in New Issue
Block a user