1
0
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:
Dave Halter
2015-04-21 16:12:24 +02:00
parent 5c65e9cdaa
commit 18c4b5f7dc
4 changed files with 76 additions and 30 deletions

View File

@@ -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:

View File

@@ -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):

View File

@@ -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]

View File

@@ -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()