1
0
forked from VimPlug/jedi

Remove the module path from the parser tree.

Some static analysis tests are still failing.
This commit is contained in:
Dave Halter
2017-03-27 18:13:32 +02:00
parent b60ec024fa
commit 8a35a04439
14 changed files with 102 additions and 101 deletions

View File

@@ -5,8 +5,6 @@ from jedi import debug
from jedi.parser.python import tree
from jedi.evaluate.compiled import CompiledObject
from jedi.common import unite
CODES = {
'attribute-error': (1, AttributeError, 'Potential AttributeError.'),
@@ -82,7 +80,9 @@ def add(node_context, error_name, node, message=None, typ=Error, payload=None):
if _check_for_exception_catch(node_context, node, exception, payload):
return
module_path = node.get_root_node().path
# TODO this path is probably not right
module_context = node_context.get_root_context()
module_path = module_context.py__file__()
instance = typ(error_name, module_path, node.start_pos, message)
debug.warning(str(instance), format=False)
node_context.evaluator.analysis.append(instance)

View File

@@ -29,25 +29,12 @@ class MixedObject(object):
fewer special cases, because we in Python you don't have the same freedoms
to modify the runtime.
"""
def __init__(self, evaluator, parent_context, compiled_object, tree_name):
def __init__(self, evaluator, parent_context, compiled_object, tree_context):
self.evaluator = evaluator
self.parent_context = parent_context
self.compiled_object = compiled_object
self._context = tree_context
self.obj = compiled_object.obj
self._tree_name = tree_name
name_module = tree_name.get_root_node()
if parent_context.tree_node.get_root_node() != name_module:
from jedi.evaluate.representation import ModuleContext
module_context = ModuleContext(evaluator, name_module)
name = compiled_object.get_root_context().py__name__()
imports.add_module(evaluator, name, module_context)
else:
module_context = parent_context.get_root_context()
self._context = module_context.create_context(
tree_name.parent,
node_is_context=True,
node_is_object=True
)
# We have to overwrite everything that has to do with trailers, name
# lookups and filters to make it possible to route name lookups towards
@@ -132,10 +119,10 @@ def find_syntax_node_name(evaluator, python_object):
path = inspect.getsourcefile(python_object)
except TypeError:
# The type might not be known (e.g. class_with_dict.__weakref__)
return None
return None, None
if path is None or not os.path.exists(path):
# The path might not exist or be e.g. <stdin>.
return None
return None, None
module = _load_module(evaluator, path, python_object)
@@ -143,17 +130,17 @@ def find_syntax_node_name(evaluator, python_object):
# We don't need to check names for modules, because there's not really
# a way to write a module in a module in Python (and also __name__ can
# be something like ``email.utils``).
return module.name
return module, path
name_str = python_object.__name__
if name_str == '<lambda>':
return None # It's too hard to find lambdas.
return None, None # It's too hard to find lambdas.
# Doesn't always work (e.g. os.stat_result)
try:
names = module.used_names[name_str]
except KeyError:
return None
return None, None
names = [n for n in names if n.is_definition()]
try:
@@ -170,22 +157,44 @@ def find_syntax_node_name(evaluator, python_object):
# There's a chance that the object is not available anymore, because
# the code has changed in the background.
if line_names:
return line_names[-1]
return line_names[-1].parent, path
# It's really hard to actually get the right definition, here as a last
# resort we just return the last one. This chance might lead to odd
# completions at some points but will lead to mostly correct type
# inference, because people tend to define a public name in a module only
# once.
return names[-1]
return names[-1].parent, path
@compiled.compiled_objects_cache('mixed_cache')
def create(evaluator, obj, parent_context=None, *args):
tree_name = find_syntax_node_name(evaluator, obj)
tree_node, path = find_syntax_node_name(evaluator, obj)
compiled_object = compiled.create(
evaluator, obj, parent_context=parent_context.compiled_object)
if tree_name is None:
if tree_node is None:
return compiled_object
return MixedObject(evaluator, parent_context, compiled_object, tree_name)
module_node = tree_node.get_root_node()
if parent_context.tree_node.get_root_node() == module_node:
module_context = parent_context.get_root_context()
else:
from jedi.evaluate.representation import ModuleContext
module_context = ModuleContext(evaluator, module_node, path=path)
name = compiled_object.get_root_context().py__name__()
imports.add_module(evaluator, name, module_context)
tree_context = module_context.create_context(
tree_node,
node_is_context=True,
node_is_object=True
)
return MixedObject(
evaluator,
parent_context,
compiled_object,
tree_context=tree_context
)

View File

@@ -459,7 +459,7 @@ def _load_module(evaluator, path=None, code=None, sys_path=None, parent_module=N
module_node = parse(code=code, path=path, cache=True, diff_cache=True)
from jedi.evaluate.representation import ModuleContext
return ModuleContext(evaluator, module_node)
return ModuleContext(evaluator, module_node, path=path)
else:
return compiled.load_module(evaluator, path)
@@ -489,7 +489,7 @@ def get_modules_containing_name(evaluator, modules, name):
return None
else:
module_node = parser_cache_item.parser.get_root_node()
return er.ModuleContext(evaluator, module_node)
return er.ModuleContext(evaluator, module_node, path=path)
def check_fs(path):
with open(path, 'rb') as f:

View File

@@ -150,7 +150,11 @@ def py__getitem__(context, typ, node):
return context.eval_node(nodes[0])
from jedi.evaluate.representation import ModuleContext
typing = ModuleContext(context.evaluator, _get_typing_replacement_module())
typing = ModuleContext(
context.evaluator,
module_node=_get_typing_replacement_module(),
path=None
)
factories = typing.py__getattribute__("factory")
assert len(factories) == 1
factory = list(factories)[0]

View File

@@ -57,7 +57,8 @@ from jedi.evaluate import imports
from jedi.evaluate import helpers
from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \
GlobalNameFilter, DictFilter, ContextName, AbstractNameDefinition, \
ParamName, AnonymousInstanceParamName, TreeNameDefinition
ParamName, AnonymousInstanceParamName, TreeNameDefinition, \
ContextNameMixin
from jedi.evaluate.dynamic import search_params
from jedi.evaluate import context
@@ -406,13 +407,26 @@ class ModuleAttributeName(AbstractNameDefinition):
)
class ModuleName(ContextNameMixin, AbstractNameDefinition):
start_pos = 1, 0
def __init__(self, context, name):
self._context = context
self._name = name
@property
def string_name(self):
return self._name
class ModuleContext(use_metaclass(CachedMetaClass, context.TreeContext)):
api_type = 'module'
parent_context = None
def __init__(self, evaluator, module_node):
def __init__(self, evaluator, module_node, path):
super(ModuleContext, self).__init__(evaluator, parent_context=None)
self.tree_node = module_node
self._path = path
def get_filters(self, search_global, until_position=None, origin_scope=None):
yield ParserTreeFilter(
@@ -449,10 +463,21 @@ class ModuleContext(use_metaclass(CachedMetaClass, context.TreeContext)):
# All the additional module attributes are strings.
return dict((n, ModuleAttributeName(self, n)) for n in names)
@property
def _string_name(self):
""" This is used for the goto functions. """
if self._path is None:
return '' # no path -> empty name
else:
sep = (re.escape(os.path.sep),) * 2
r = re.search(r'([^%s]*?)(%s__init__)?(\.py|\.so)?$' % sep, self._path)
# Remove PEP 3149 names
return re.sub('\.[a-z]+-\d{2}[mud]{0,3}$', '', r.group(1))
@property
@memoize_default()
def name(self):
return ContextName(self, self.tree_node.name)
return ModuleName(self, self._string_name)
def _get_init_directory(self):
"""
@@ -478,10 +503,10 @@ class ModuleContext(use_metaclass(CachedMetaClass, context.TreeContext)):
"""
In contrast to Python's __file__ can be None.
"""
if self.tree_node.path is None:
if self._path is None:
return None
return os.path.abspath(self.tree_node.path)
return os.path.abspath(self._path)
def py__package__(self):
if self._get_init_directory() is None:
@@ -539,7 +564,7 @@ class ModuleContext(use_metaclass(CachedMetaClass, context.TreeContext)):
Lists modules in the directory of this module (if this module is a
package).
"""
path = self.tree_node.path
path = self._path
names = {}
if path is not None and path.endswith(os.path.sep + '__init__.py'):
mods = pkgutil.iter_modules([os.path.dirname(path)])
@@ -559,6 +584,11 @@ class ModuleContext(use_metaclass(CachedMetaClass, context.TreeContext)):
def py__class__(self):
return compiled.get_special_object(self.evaluator, 'MODULE_CLASS')
def __repr__(self):
return "<%s: %s@%s-%s>" % (
self.__class__.__name__, self._string_name,
self.tree_node.start_pos[0], self.tree_node.end_pos[0])
class ImplicitNSName(AbstractNameDefinition):
"""

View File

@@ -222,7 +222,7 @@ def _get_paths_from_buildout_script(evaluator, buildout_script_path):
return
from jedi.evaluate.representation import ModuleContext
for path in _check_module(ModuleContext(evaluator, module_node)):
for path in _check_module(ModuleContext(evaluator, module_node, buildout_script_path)):
yield path