diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index cd9c8d7d..ff5e665e 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -472,14 +472,18 @@ class Interpreter(Script): self.namespaces = namespaces self._inference_state.allow_descriptor_getattr = self._allow_descriptor_getattr_default - def _get_module(self): - return interpreter.MixedModuleValue( - self._inference_state, - self._module_node, - self.namespaces, + @cache.memoize_method + def _get_module_context(self): + tree_module_value = ModuleValue( + self._inference_state, self._module_node, file_io=KnownContentFileIO(self.path, self._code), + string_names=('__main__',), code_lines=self._code_lines, ) + return interpreter.MixedModuleContext( + tree_module_value, + self.namespaces, + ) def names(source=None, path=None, encoding='utf-8', all_scopes=False, diff --git a/jedi/api/interpreter.py b/jedi/api/interpreter.py index 147a8d48..df5c2804 100644 --- a/jedi/api/interpreter.py +++ b/jedi/api/interpreter.py @@ -2,11 +2,10 @@ TODO Some parts of this module are still not well documented. """ -from jedi.inference.value import ModuleValue from jedi.inference import compiled from jedi.inference.compiled import mixed from jedi.inference.compiled.access import create_access_path -from jedi.inference.base_value import ValueWrapper +from jedi.inference.context import ModuleContext def _create(inference_state, obj): @@ -20,28 +19,20 @@ class NamespaceObject(object): self.__dict__ = dct -class MixedModuleValue(ValueWrapper): - type = 'mixed_module' - - def __init__(self, inference_state, tree_module, namespaces, file_io, code_lines): - module_value = ModuleValue( - inference_state, tree_module, - file_io=file_io, - string_names=('__main__',), - code_lines=code_lines - ) - super(MixedModuleValue, self).__init__(module_value) +class MixedModuleContext(ModuleContext): + def __init__(self, tree_module_value, namespaces): + super(MixedModuleContext, self).__init__(tree_module_value) self._namespace_objects = [NamespaceObject(n) for n in namespaces] def get_filters(self, *args, **kwargs): - for filter in self._wrapped_value.get_filters(*args, **kwargs): + for filter in self._value.as_context().get_filters(*args, **kwargs): yield filter for namespace_obj in self._namespace_objects: compiled_object = _create(self.inference_state, namespace_obj) mixed_object = mixed.MixedObject( compiled_object=compiled_object, - tree_value=self._wrapped_value + tree_value=self._value ) for filter in mixed_object.get_filters(*args, **kwargs): yield filter diff --git a/jedi/inference/__init__.py b/jedi/inference/__init__.py index 59402c8e..2e5aa9f1 100644 --- a/jedi/inference/__init__.py +++ b/jedi/inference/__init__.py @@ -246,7 +246,7 @@ class InferenceState(object): if is_classdef: c = ClassValue(self, context, name.parent) else: - c = FunctionValue.from_value(context, name.parent) + c = FunctionValue.from_context(context, name.parent) return ValueSet([c]) if type_ == 'expr_stmt': @@ -349,7 +349,7 @@ class InferenceState(object): new_dotted.children[index - 1:] = [] values = context.infer_node(new_dotted) return unite( - value.goto(name, name_value=value) + value.goto(name, name_context=value.as_context()) for value in values ) diff --git a/jedi/inference/compiled/mixed.py b/jedi/inference/compiled/mixed.py index eab0551d..e10a6c7c 100644 --- a/jedi/inference/compiled/mixed.py +++ b/jedi/inference/compiled/mixed.py @@ -96,6 +96,21 @@ class MixedName(compiled.CompiledName): @underscore_memoization def infer(self): + def access_to_value(parent_value, access): + if parent_value is None: + parent_context = None + else: + parent_context = parent_value.as_context() + + if parent_context is None or isinstance(parent_context, MixedObject): + return _create(self._inference_state, access, parent_context=parent_context) + else: + return ValueSet({ + create_cached_compiled_object( + parent_context.inference_state, access, parent_context + ) + }) + # TODO use logic from compiled.CompiledObjectFilter access_paths = self.parent_context.access_handle.getattr_paths( self.string_name, @@ -104,12 +119,7 @@ class MixedName(compiled.CompiledName): assert len(access_paths) values = [None] for access in access_paths: - values = ValueSet.from_sets( - _create(self._inference_state, access, parent_context=c) - if c is None or isinstance(c, MixedObject) - else ValueSet({create_cached_compiled_object(c.inference_state, access, c)}) - for c in values - ) + values = ValueSet.from_sets(access_to_value(v, access) for v in values) return values @property @@ -278,7 +288,8 @@ def _create(inference_state, access_handle, parent_context, *args): tree_node, node_is_value=True, node_is_object=True - ) + )._value + # TODO private access! }) if tree_node.type == 'classdef': if not access_handle.is_class(): diff --git a/jedi/inference/compiled/value.py b/jedi/inference/compiled/value.py index 07779b21..f4cb51a5 100644 --- a/jedi/inference/compiled/value.py +++ b/jedi/inference/compiled/value.py @@ -17,7 +17,7 @@ from jedi.inference.compiled.access import _sentinel from jedi.inference.cache import inference_state_function_cache from jedi.inference.helpers import reraise_getitem_errors from jedi.inference.signature import BuiltinSignature -from jedi.inference.context import AbstractContext +from jedi.inference.context import CompiledContext class CheckAttribute(object): @@ -264,7 +264,7 @@ class CompiledObject(Value): return NO_VALUES def _as_context(self): - return AbstractContext(self) + return CompiledContext(self) class CompiledName(AbstractNameDefinition): @@ -515,7 +515,9 @@ def _create_from_name(inference_state, compiled_object, name): value = None for access_path in access_paths: value = create_cached_compiled_object( - inference_state, access_path, parent_context=value + inference_state, + access_path, + parent_context=None if value is None else value.as_context(), ) return value diff --git a/jedi/inference/context.py b/jedi/inference/context.py index cdc7ecc2..c1211785 100644 --- a/jedi/inference/context.py +++ b/jedi/inference/context.py @@ -118,6 +118,14 @@ class ModuleContext(AbstractContext): for f in filters: # Python 2... yield f + @property + def string_names(self): + return self._value.string_names + + @property + def code_lines(self): + return self._value.code_lines + def get_value(self): """ This is the only function that converts a context back to a value. @@ -127,6 +135,14 @@ class ModuleContext(AbstractContext): return self._value +class NamespaceContext(AbstractContext): + def get_filters(self, until_position=None, origin_scope=None): + return self._value.get_filters() + + def py__file__(self): + return self._value.py__file__() + + class ClassContext(AbstractContext): def get_filters(self, until_position=None, origin_scope=None): yield self.get_global_filter(until_position, origin_scope) @@ -164,3 +180,8 @@ class CompForContext(AbstractContext): def get_filters(self, until_position=None, origin_scope=None): yield ParserTreeFilter(self) + + +class CompiledContext(AbstractContext): + def get_filters(self, until_position=None, origin_scope=None): + return self._value.get_filters() diff --git a/jedi/inference/imports.py b/jedi/inference/imports.py index aeec3c73..44064f18 100644 --- a/jedi/inference/imports.py +++ b/jedi/inference/imports.py @@ -93,7 +93,7 @@ def infer_import(context, tree_name, is_goto=False): from_import_name, name_context=context, analysis_errors=False - ) for c in types.as_context() + ) for c in types ]) else: types = types.py__getattribute__( diff --git a/jedi/inference/star_args.py b/jedi/inference/star_args.py index 8580be75..46ad1ddf 100644 --- a/jedi/inference/star_args.py +++ b/jedi/inference/star_args.py @@ -35,7 +35,6 @@ def _iter_nodes_for_param(param_name): # anyway trailer = search_ancestor(argument, 'trailer') if trailer is not None: # Make sure we're in a function - raise NotImplementedError context = execution_context.create_context(trailer) if _goes_to_param_name(param_name, context, name): values = _to_callables(context, trailer) @@ -62,17 +61,17 @@ def _goes_to_param_name(param_name, context, potential_name): for p in found) -def _to_callables(value, trailer): +def _to_callables(context, trailer): from jedi.inference.syntax_tree import infer_trailer atom_expr = trailer.parent index = atom_expr.children[0] == 'await' # Infer atom first - values = value.infer_node(atom_expr.children[index]) + values = context.infer_node(atom_expr.children[index]) for trailer2 in atom_expr.children[index + 1:]: if trailer == trailer2: break - values = infer_trailer(value, values, trailer2) + values = infer_trailer(context, values, trailer2) return values diff --git a/jedi/inference/sys_path.py b/jedi/inference/sys_path.py index c57692b8..bfc1b536 100644 --- a/jedi/inference/sys_path.py +++ b/jedi/inference/sys_path.py @@ -153,12 +153,12 @@ def _get_paths_from_buildout_script(inference_state, buildout_script_path): return from jedi.inference.value import ModuleValue - module = ModuleValue( + module_context = ModuleValue( inference_state, module_node, file_io, string_names=None, code_lines=get_cached_code_lines(inference_state.grammar, buildout_script_path), ).as_context() - for path in check_sys_path_modifications(module): + for path in check_sys_path_modifications(module_context): yield path diff --git a/jedi/inference/value/namespace.py b/jedi/inference/value/namespace.py index c9e21682..cddf2abe 100644 --- a/jedi/inference/value/namespace.py +++ b/jedi/inference/value/namespace.py @@ -3,6 +3,7 @@ from jedi.inference.filters import DictFilter from jedi.inference.names import ValueNameMixin, AbstractNameDefinition from jedi.inference.base_value import Value from jedi.inference.value.module import SubModuleDictMixin +from jedi.inference.context import NamespaceContext class ImplicitNSName(ValueNameMixin, AbstractNameDefinition): @@ -60,5 +61,8 @@ class ImplicitNamespaceValue(Value, SubModuleDictMixin): def is_stub(self): return False + def as_context(self): + return NamespaceContext(self) + def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, self._fullname) diff --git a/test/test_inference/test_buildout_detection.py b/test/test_inference/test_buildout_detection.py index cd495d3c..b7627841 100644 --- a/test/test_inference/test_buildout_detection.py +++ b/test/test_inference/test_buildout_detection.py @@ -10,8 +10,8 @@ from ..helpers import cwd_at def check_module_test(Script, code): - module_value = Script(code)._get_module() - return check_sys_path_modifications(module_value) + module_context = Script(code)._get_module_context() + return check_sys_path_modifications(module_context) @cwd_at('test/examples/buildout_project/src/proj_name') diff --git a/test/test_inference/test_imports.py b/test/test_inference/test_imports.py index 7e09b958..a79f37eb 100644 --- a/test/test_inference/test_imports.py +++ b/test/test_inference/test_imports.py @@ -318,12 +318,13 @@ def test_get_modules_containing_name(inference_state, path, goal, is_package): is_package=is_package, ) assert module - input_module, found_module = imports.get_modules_containing_name( + module_context = module.as_context() + input_module, found_module = imports.get_module_contexts_containing_name( inference_state, - [module], + [module_context], 'string_that_only_exists_here' ) - assert input_module is module + assert input_module is module_context assert found_module.string_names == goal @@ -424,7 +425,7 @@ def test_level_to_import_path(level, directory, project_path, result): def test_import_name_calculation(Script): s = Script(path=os.path.join(test_dir, 'completion', 'isinstance.py')) - m = s._get_module() + m = s._get_module_context() assert m.string_names == ('test', 'completion', 'isinstance') @@ -434,7 +435,7 @@ def test_pre_defined_imports_module(Script, environment, name): name = '__builtin__' path = os.path.join(root_dir, name + '.py') - module = Script('', path=path)._get_module() + module = Script('', path=path)._get_module_context() assert module.string_names == (name,) assert module.inference_state.builtins_module.py__file__() != path diff --git a/test/test_inference/test_precedence.py b/test/test_inference/test_precedence.py index ccb640d9..4b41bcd8 100644 --- a/test/test_inference/test_precedence.py +++ b/test/test_inference/test_precedence.py @@ -14,5 +14,5 @@ def test_equals(Script, environment, source): pytest.skip("Ellipsis does not exists in 2") script = Script(source) node = script._module_node.children[0] - first, = script._get_module().infer_node(node) + first, = script._get_module_context().infer_node(node) assert isinstance(first, CompiledObject) and first.get_safe_value() is True diff --git a/test/test_inference/test_sys_path.py b/test/test_inference/test_sys_path.py index 67b8d87e..ad23e738 100644 --- a/test/test_inference/test_sys_path.py +++ b/test/test_inference/test_sys_path.py @@ -14,7 +14,7 @@ def test_paths_from_assignment(Script): def paths(src): script = Script(src, path='/foo/bar.py') expr_stmt = script._module_node.children[0] - return set(sys_path._paths_from_assignment(script._get_module(), expr_stmt)) + return set(sys_path._paths_from_assignment(script._get_module_context(), expr_stmt)) # Normalize paths for Windows. path_a = os.path.abspath('/foo/a') diff --git a/test/test_parso_integration/test_error_correction.py b/test/test_parso_integration/test_error_correction.py index 6ab16971..02bd9af3 100644 --- a/test/test_parso_integration/test_error_correction.py +++ b/test/test_parso_integration/test_error_correction.py @@ -22,7 +22,7 @@ def test_string_literals(Script): """) script = Script(dedent(source)) - assert script._get_module().tree_node.end_pos == (6, 0) + assert script._get_module_context().tree_node.end_pos == (6, 0) assert script.completions() @@ -47,4 +47,4 @@ def test_decorator_string_issue(Script): s = Script(source) assert s.completions() - assert s._get_module().tree_node.get_code() == source + assert s._get_module_context().tree_node.get_code() == source