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:
|
||||
module = self._evaluator.wrap(self._import.get_parent_until())
|
||||
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),
|
||||
module, self._import.level)
|
||||
try:
|
||||
@@ -90,34 +103,50 @@ class ImportWrapper(pr.Base):
|
||||
return []
|
||||
|
||||
if module is None:
|
||||
# TODO does that really happen? Why?
|
||||
return []
|
||||
|
||||
#if self._import.is_nested() and not self.nested_resolve:
|
||||
# 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`
|
||||
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)
|
||||
if rest:
|
||||
if is_goto:
|
||||
scopes = list(chain.from_iterable(
|
||||
types = list(chain.from_iterable(
|
||||
self._evaluator.find_types(s, rest[0], is_goto=True)
|
||||
for s in scopes))
|
||||
for s in types))
|
||||
else:
|
||||
if self._import.type == 'import_from' \
|
||||
or importer.str_import_path == ('os', 'path'):
|
||||
scopes = importer.follow_rest(scopes[0], rest)
|
||||
types = importer.follow_rest(types[0], rest)
|
||||
else:
|
||||
scopes = []
|
||||
debug.dbg('after import: %s', scopes)
|
||||
if not scopes:
|
||||
analysis.add(self._evaluator, 'import-error', importer.import_path[-1])
|
||||
types = []
|
||||
debug.dbg('after import: %s', types)
|
||||
#if not types:
|
||||
# analysis.add(self._evaluator, 'import-error', importer.import_path[-1])
|
||||
finally:
|
||||
self._evaluator.recursion_detector.pop_stmt()
|
||||
return scopes
|
||||
return types
|
||||
|
||||
|
||||
class NestedImportModule(pr.Module):
|
||||
@@ -158,7 +187,7 @@ def get_importer(evaluator, import_path, module, level=0):
|
||||
cache and speeds up libraries like ``numpy``.
|
||||
"""
|
||||
if level:
|
||||
base = module.py__name__().split('.')
|
||||
base = module.py__package__().split('.')
|
||||
if level > len(base):
|
||||
# TODO add import error.
|
||||
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)
|
||||
try:
|
||||
print(check_import_path)
|
||||
return evaluator.import_cache[check_import_path]
|
||||
except KeyError:
|
||||
importer = _Importer(evaluator, import_path, module, level=0)
|
||||
@@ -452,7 +482,8 @@ class _Importer(object):
|
||||
paths = base.py__path__()
|
||||
except AttributeError:
|
||||
# The module is not a package.
|
||||
raise NotImplementedError
|
||||
analysis.add(self._evaluator, 'import-error', import_path[-1])
|
||||
return None
|
||||
else:
|
||||
debug.dbg('search_module %s in paths %s', module_name, paths)
|
||||
for path in paths:
|
||||
|
||||
@@ -31,6 +31,7 @@ __
|
||||
import os
|
||||
import pkgutil
|
||||
import imp
|
||||
import re
|
||||
from itertools import chain
|
||||
|
||||
from jedi._compatibility import use_metaclass, unicode, Python3Method
|
||||
@@ -760,6 +761,14 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
|
||||
def name(self):
|
||||
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):
|
||||
for name, module in self._evaluator.modules.items():
|
||||
if module == self:
|
||||
@@ -768,6 +777,12 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
|
||||
def py__file__(self):
|
||||
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
|
||||
def py__path__(self):
|
||||
"""
|
||||
@@ -778,14 +793,12 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
|
||||
def return_value():
|
||||
return [path]
|
||||
|
||||
for suffix, _, _ in imp.get_suffixes():
|
||||
ending = '__init__' + suffix
|
||||
if self.py__file__().endswith(ending):
|
||||
# Remove the ending, including the separator.
|
||||
path = self.py__file__()[:-len(ending) - 1]
|
||||
return return_value
|
||||
else:
|
||||
path = self._get_init_directory()
|
||||
|
||||
if path is None:
|
||||
raise AttributeError('Only packages have __path__ attributes.')
|
||||
else:
|
||||
return return_value
|
||||
|
||||
@memoize_default()
|
||||
def _sub_modules_dict(self):
|
||||
|
||||
@@ -948,6 +948,17 @@ class ImportFrom(Import):
|
||||
return dict((alias, name) for name, alias in self._as_name_tuples()
|
||||
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
|
||||
def level(self):
|
||||
"""The level parameter of ``__import__``."""
|
||||
@@ -987,15 +998,7 @@ class ImportFrom(Import):
|
||||
The import paths defined in an import statement. Typically an array
|
||||
like this: ``[<Name: datetime>, <Name: date>]``.
|
||||
"""
|
||||
for n in self.children[1:]:
|
||||
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]
|
||||
dotted = self.get_from_names()
|
||||
|
||||
if self.children[-1] == '*':
|
||||
return [dotted]
|
||||
|
||||
@@ -119,8 +119,7 @@ def test_import_priorities():
|
||||
See also #536.
|
||||
"""
|
||||
from import_tree import the_pkg, invisible_pkg
|
||||
# TODO currently not enabled, later we should actually fix this.
|
||||
##? int()
|
||||
#? int()
|
||||
invisible_pkg
|
||||
# The renamed invisible_pkg in that module (look at ``__init__.py``!)
|
||||
#? float()
|
||||
|
||||
Reference in New Issue
Block a user