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
+43 -12
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:
+20 -7
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):