1
0
forked from VimPlug/jedi

added support for implicit ns packages and added tests

This commit is contained in:
daniel
2017-01-16 18:36:35 -05:00
committed by Dave Halter
parent ef1b1f41e4
commit 5513f72987
8 changed files with 174 additions and 11 deletions
+18 -8
View File
@@ -16,7 +16,7 @@ import os
import pkgutil
import sys
from jedi._compatibility import find_module, unicode
from jedi._compatibility import find_module, unicode, ImplicitNSInfo
from jedi import debug
from jedi import settings
from jedi.common import source_to_unicode, unite
@@ -306,8 +306,10 @@ class Importer(object):
# At the moment we are only using one path. So this is
# not important to be correct.
try:
if not isinstance(path, list):
path = [path]
module_file, module_path, is_pkg = \
find_module(import_parts[-1], [path])
find_module(import_parts[-1], path, fullname=module_name)
break
except ImportError:
module_path = None
@@ -323,7 +325,7 @@ class Importer(object):
sys.path, temp = sys_path, sys.path
try:
module_file, module_path, is_pkg = \
find_module(import_parts[-1])
find_module(import_parts[-1], fullname=module_name)
finally:
sys.path = temp
except ImportError:
@@ -343,7 +345,12 @@ class Importer(object):
source = module_file.read()
module_file.close()
if module_file is None and not module_path.endswith(('.py', '.zip', '.egg')):
if isinstance(module_path, ImplicitNSInfo):
from jedi.evaluate.representation import ImplicitNamespaceContext
fullname, paths = module_path.name, module_path.paths
module = ImplicitNamespaceContext(self._evaluator, fullname=fullname)
module.paths = paths
elif module_file is None and not module_path.endswith(('.py', '.zip', '.egg')):
module = compiled.load_module(self._evaluator, module_path)
else:
module = _load_module(self._evaluator, module_path, source, sys_path, parent_module)
@@ -384,7 +391,7 @@ class Importer(object):
:param only_modules: Indicates wheter it's possible to import a
definition that is not defined in a module.
"""
from jedi.evaluate.representation import ModuleContext
from jedi.evaluate.representation import ModuleContext, ImplicitNamespaceContext
names = []
if self.import_path:
# flask
@@ -405,13 +412,16 @@ class Importer(object):
# Non-modules are not completable.
if context.api_type != 'module': # not a module
continue
# namespace packages
if isinstance(context, ModuleContext) and \
context.py__file__().endswith('__init__.py'):
if isinstance(context, ModuleContext) and context.py__file__().endswith('__init__.py'):
paths = context.py__path__()
names += self._get_module_names(paths, in_module=context)
# implicit namespace packages
elif isinstance(context, ImplicitNamespaceContext):
paths = context.paths
names += self._get_module_names(paths)
if only_modules:
# In the case of an import like `from x.` we don't need to
# add all the variables.
+63
View File
@@ -39,6 +39,7 @@ import os
import pkgutil
import imp
import re
from itertools import chain
from jedi._compatibility import use_metaclass
from jedi.parser import tree
@@ -557,3 +558,65 @@ class ModuleContext(use_metaclass(CachedMetaClass, context.TreeContext)):
def py__class__(self):
return compiled.get_special_object(self.evaluator, 'MODULE_CLASS')
class ImplicitNSName(AbstractNameDefinition):
"""
Accessing names for implicit namespace packages should infer to nothing.
This object will prevent Jedi from raising exceptions
"""
def __init__(self, implicit_ns_context, string_name):
self.implicit_ns_context = implicit_ns_context
self.string_name = string_name
def infer(self):
return []
def get_root_context(self):
return self.implicit_ns_context
class ImplicitNamespaceContext(use_metaclass(CachedMetaClass, context.TreeContext)):
"""
Provides support for implicit namespace packages
"""
api_type = 'module'
parent_context = None
def __init__(self, evaluator, fullname):
super(ImplicitNamespaceContext, self).__init__(evaluator, parent_context=None)
self.evaluator = evaluator
self.fullname = fullname
def get_filters(self, search_global, until_position=None, origin_scope=None):
yield DictFilter(self._sub_modules_dict())
@property
@memoize_default()
def name(self):
string_name = self.py__package__().rpartition('.')[-1]
return ImplicitNSName(self, string_name)
def py__file__(self):
return None
def py__package__(self):
"""Return the fullname
"""
return self.fullname
@property
def py__path__(self):
return lambda: [self.paths]
@memoize_default()
def _sub_modules_dict(self):
names = {}
paths = self.paths
file_names = chain.from_iterable(os.listdir(path) for path in paths)
mods = [file_name.rpartition('.')[0] if '.' in file_name else file_name for file_name in file_names]
for name in mods:
names[name] = imports.SubModuleName(self, name)
return names