| | """ |
| | Used only for REPL Completion. |
| | """ |
| |
|
| | import inspect |
| | from pathlib import Path |
| |
|
| | from jedi.parser_utils import get_cached_code_lines |
| |
|
| | from jedi import settings |
| | from jedi.cache import memoize_method |
| | from jedi.inference import compiled |
| | from jedi.file_io import FileIO |
| | from jedi.inference.names import NameWrapper |
| | from jedi.inference.base_value import ValueSet, ValueWrapper, NO_VALUES |
| | from jedi.inference.value import ModuleValue |
| | from jedi.inference.cache import inference_state_function_cache, \ |
| | inference_state_method_cache |
| | from jedi.inference.compiled.access import ALLOWED_GETITEM_TYPES, get_api_type |
| | from jedi.inference.gradual.conversion import to_stub |
| | from jedi.inference.context import CompiledContext, CompiledModuleContext, \ |
| | TreeContextMixin |
| |
|
| | _sentinel = object() |
| |
|
| |
|
| | class MixedObject(ValueWrapper): |
| | """ |
| | A ``MixedObject`` is used in two ways: |
| | |
| | 1. It uses the default logic of ``parser.python.tree`` objects, |
| | 2. except for getattr calls and signatures. The names dicts are generated |
| | in a fashion like ``CompiledValue``. |
| | |
| | This combined logic makes it possible to provide more powerful REPL |
| | completion. It allows side effects that are not noticable with the default |
| | parser structure to still be completable. |
| | |
| | The biggest difference from CompiledValue to MixedObject is that we are |
| | generally dealing with Python code and not with C code. This will generate |
| | fewer special cases, because we in Python you don't have the same freedoms |
| | to modify the runtime. |
| | """ |
| | def __init__(self, compiled_value, tree_value): |
| | super().__init__(tree_value) |
| | self.compiled_value = compiled_value |
| | self.access_handle = compiled_value.access_handle |
| |
|
| | def get_filters(self, *args, **kwargs): |
| | yield MixedObjectFilter( |
| | self.inference_state, self.compiled_value, self._wrapped_value) |
| |
|
| | def get_signatures(self): |
| | |
| | |
| | return self.compiled_value.get_signatures() |
| |
|
| | @inference_state_method_cache(default=NO_VALUES) |
| | def py__call__(self, arguments): |
| | |
| | values = to_stub(self._wrapped_value) |
| | if not values: |
| | values = self._wrapped_value |
| | return values.py__call__(arguments) |
| |
|
| | def get_safe_value(self, default=_sentinel): |
| | if default is _sentinel: |
| | return self.compiled_value.get_safe_value() |
| | else: |
| | return self.compiled_value.get_safe_value(default) |
| |
|
| | @property |
| | def array_type(self): |
| | return self.compiled_value.array_type |
| |
|
| | def get_key_values(self): |
| | return self.compiled_value.get_key_values() |
| |
|
| | def py__simple_getitem__(self, index): |
| | python_object = self.compiled_value.access_handle.access._obj |
| | if type(python_object) in ALLOWED_GETITEM_TYPES: |
| | return self.compiled_value.py__simple_getitem__(index) |
| | return self._wrapped_value.py__simple_getitem__(index) |
| |
|
| | def negate(self): |
| | return self.compiled_value.negate() |
| |
|
| | def _as_context(self): |
| | if self.parent_context is None: |
| | return MixedModuleContext(self) |
| | return MixedContext(self) |
| |
|
| | def __repr__(self): |
| | return '<%s: %s; %s>' % ( |
| | type(self).__name__, |
| | self.access_handle.get_repr(), |
| | self._wrapped_value, |
| | ) |
| |
|
| |
|
| | class MixedContext(CompiledContext, TreeContextMixin): |
| | @property |
| | def compiled_value(self): |
| | return self._value.compiled_value |
| |
|
| |
|
| | class MixedModuleContext(CompiledModuleContext, MixedContext): |
| | pass |
| |
|
| |
|
| | class MixedName(NameWrapper): |
| | """ |
| | The ``CompiledName._compiled_value`` is our MixedObject. |
| | """ |
| | def __init__(self, wrapped_name, parent_tree_value): |
| | super().__init__(wrapped_name) |
| | self._parent_tree_value = parent_tree_value |
| |
|
| | @property |
| | def start_pos(self): |
| | values = list(self.infer()) |
| | if not values: |
| | |
| | return 0, 0 |
| | return values[0].name.start_pos |
| |
|
| | @memoize_method |
| | def infer(self): |
| | compiled_value = self._wrapped_name.infer_compiled_value() |
| | tree_value = self._parent_tree_value |
| | if tree_value.is_instance() or tree_value.is_class(): |
| | tree_values = tree_value.py__getattribute__(self.string_name) |
| | if compiled_value.is_function(): |
| | return ValueSet({MixedObject(compiled_value, v) for v in tree_values}) |
| |
|
| | module_context = tree_value.get_root_context() |
| | return _create(self._inference_state, compiled_value, module_context) |
| |
|
| |
|
| | class MixedObjectFilter(compiled.CompiledValueFilter): |
| | def __init__(self, inference_state, compiled_value, tree_value): |
| | super().__init__(inference_state, compiled_value) |
| | self._tree_value = tree_value |
| |
|
| | def _create_name(self, *args, **kwargs): |
| | return MixedName( |
| | super()._create_name(*args, **kwargs), |
| | self._tree_value, |
| | ) |
| |
|
| |
|
| | @inference_state_function_cache() |
| | def _load_module(inference_state, path): |
| | return inference_state.parse( |
| | path=path, |
| | cache=True, |
| | diff_cache=settings.fast_parser, |
| | cache_path=settings.cache_directory |
| | ).get_root_node() |
| |
|
| |
|
| | def _get_object_to_check(python_object): |
| | """Check if inspect.getfile has a chance to find the source.""" |
| | try: |
| | python_object = inspect.unwrap(python_object) |
| | except ValueError: |
| | |
| | pass |
| |
|
| | if (inspect.ismodule(python_object) |
| | or inspect.isclass(python_object) |
| | or inspect.ismethod(python_object) |
| | or inspect.isfunction(python_object) |
| | or inspect.istraceback(python_object) |
| | or inspect.isframe(python_object) |
| | or inspect.iscode(python_object)): |
| | return python_object |
| |
|
| | try: |
| | return python_object.__class__ |
| | except AttributeError: |
| | raise TypeError |
| |
|
| |
|
| | def _find_syntax_node_name(inference_state, python_object): |
| | original_object = python_object |
| | try: |
| | python_object = _get_object_to_check(python_object) |
| | path = inspect.getsourcefile(python_object) |
| | except (OSError, TypeError): |
| | |
| | return None |
| | path = None if path is None else Path(path) |
| | try: |
| | if path is None or not path.exists(): |
| | |
| | return None |
| | except OSError: |
| | |
| | |
| | |
| | |
| | return None |
| |
|
| | file_io = FileIO(path) |
| | module_node = _load_module(inference_state, path) |
| |
|
| | if inspect.ismodule(python_object): |
| | |
| | |
| | |
| | code_lines = get_cached_code_lines(inference_state.grammar, path) |
| | return module_node, module_node, file_io, code_lines |
| |
|
| | try: |
| | name_str = python_object.__name__ |
| | except AttributeError: |
| | |
| | return None |
| |
|
| | if name_str == '<lambda>': |
| | return None |
| |
|
| | |
| | names = module_node.get_used_names().get(name_str, []) |
| | |
| | |
| | |
| | names = [ |
| | n for n in names |
| | if n.parent.type in ('funcdef', 'classdef') and n.parent.name == n |
| | ] |
| | if not names: |
| | return None |
| |
|
| | try: |
| | code = python_object.__code__ |
| | |
| | |
| | |
| | |
| | line_nr = code.co_firstlineno |
| | except AttributeError: |
| | pass |
| | else: |
| | line_names = [name for name in names if name.start_pos[0] == line_nr] |
| | |
| | |
| | if line_names: |
| | names = line_names |
| |
|
| | code_lines = get_cached_code_lines(inference_state.grammar, path) |
| | |
| | |
| | |
| | |
| | |
| | tree_node = names[-1].parent |
| | if tree_node.type == 'funcdef' and get_api_type(original_object) == 'instance': |
| | |
| | |
| | |
| | return None |
| | return module_node, tree_node, file_io, code_lines |
| |
|
| |
|
| | @inference_state_function_cache() |
| | def _create(inference_state, compiled_value, module_context): |
| | |
| | |
| | python_object = compiled_value.access_handle.access._obj |
| | result = _find_syntax_node_name(inference_state, python_object) |
| | if result is None: |
| | |
| | if type(python_object) in (dict, list, tuple): |
| | return ValueSet({compiled_value}) |
| |
|
| | tree_values = to_stub(compiled_value) |
| | if not tree_values: |
| | return ValueSet({compiled_value}) |
| | else: |
| | module_node, tree_node, file_io, code_lines = result |
| |
|
| | if module_context is None or module_context.tree_node != module_node: |
| | root_compiled_value = compiled_value.get_root_context().get_value() |
| | |
| | name = root_compiled_value.py__name__() |
| | string_names = tuple(name.split('.')) |
| | module_value = ModuleValue( |
| | inference_state, module_node, |
| | file_io=file_io, |
| | string_names=string_names, |
| | code_lines=code_lines, |
| | is_package=root_compiled_value.is_package(), |
| | ) |
| | if name is not None: |
| | inference_state.module_cache.add(string_names, ValueSet([module_value])) |
| | module_context = module_value.as_context() |
| |
|
| | tree_values = ValueSet({module_context.create_value(tree_node)}) |
| | if tree_node.type == 'classdef': |
| | if not compiled_value.is_class(): |
| | |
| | tree_values = tree_values.execute_with_values() |
| |
|
| | return ValueSet( |
| | MixedObject(compiled_value, tree_value=tree_value) |
| | for tree_value in tree_values |
| | ) |
| |
|