def complete_jedi(context: CompletionContext): """Completes python code using Jedi and xonsh operators""" if context.python is None: return None xonsh_execer: XonshSession = builtins.__xonsh__ # type: ignore ctx = context.python.ctx or {} # if this is the first word and it's a known command, don't complete. # taken from xonsh/completers/python.py if context.command and context.command.arg_index != 0: first = context.command.args[0].value if first in xonsh_execer.commands_cache and first not in ctx: # type: ignore return None filter_func = get_filter_function() jedi.settings.case_insensitive_completion = not xonsh_execer.env.get( "CASE_SENSITIVE_COMPLETIONS" ) source = context.python.multiline_code index = context.python.cursor_index row = source.count("\n", 0, index) + 1 column = ( index - source.rfind("\n", 0, index) - 1 ) # will be `index - (-1) - 1` if there's no newline extra_ctx = {"__xonsh__": xonsh_execer} try: extra_ctx["_"] = _ except NameError: pass if JEDI_NEW_API: script = jedi.Interpreter(source, [ctx, extra_ctx]) else: script = jedi.Interpreter(source, [ctx, extra_ctx], line=row, column=column) script_comp = set() try: if JEDI_NEW_API: script_comp = script.complete(row, column) else: script_comp = script.completions() except Exception: pass res = set(create_completion(comp) for comp in script_comp if should_complete(comp)) if index > 0: last_char = source[index - 1] res.update( RichCompletion(t, prefix_len=1) for t in XONSH_SPECIAL_TOKENS if filter_func(t, last_char) ) else: res.update(RichCompletion(t, prefix_len=0) for t in XONSH_SPECIAL_TOKENS) return res
def complete_jedi(prefix, line, start, end, ctx): """Completes python code using Jedi and xonsh operators""" # if this is the first word and it's a known command, don't complete. # taken from xonsh/completers/python.py if line.lstrip() != "": first = line.split(maxsplit=1)[0] if prefix == first and first in __xonsh__.commands_cache and first not in ctx: return set() filter_func = get_filter_function() jedi.settings.case_insensitive_completion = not __xonsh__.env.get( "CASE_SENSITIVE_COMPLETIONS") if PTK_COMPLETER: # 'is not None' won't work with lazyobject document = PTK_COMPLETER.current_document source = document.text row = document.cursor_position_row + 1 column = document.cursor_position_col else: source = line row = 1 column = end extra_ctx = {"__xonsh__": __xonsh__} try: extra_ctx["_"] = _ except NameError: pass if JEDI_NEW_API: script = jedi.Interpreter(source, [ctx, extra_ctx]) else: script = jedi.Interpreter(source, [ctx, extra_ctx], line=row, column=column) script_comp = set() try: if JEDI_NEW_API: script_comp = script.complete(row, column) else: script_comp = script.completions() except Exception: pass # make sure _* names are completed only when # the user writes the first underscore complete_underscores = prefix.endswith("_") return set( itertools.chain( (create_completion(comp) for comp in script_comp if complete_underscores or not comp.name.startswith("_") or not comp.complete.startswith("_")), (t for t in XONSH_SPECIAL_TOKENS if filter_func(t, prefix)), ))
def test_keyword_argument(): def f(some_keyword_argument): pass c, = jedi.Interpreter("f(some_keyw", [{'f': f}]).complete() assert c.name == 'some_keyword_argument=' assert c.complete == 'ord_argument=' # Make it impossible for jedi to find the source of the function. f.__name__ = 'xSOMETHING' c, = jedi.Interpreter("x(some_keyw", [{'x': f}]).complete() assert c.name == 'some_keyword_argument='
def test_completion_param_annotations(): # Need to define this function not directly in Python. Otherwise Jedi is to # clever and uses the Python code instead of the signature object. code = 'def foo(a: 1, b: str, c: int = 1.0) -> bytes: pass' exec_(code, locals()) script = jedi.Interpreter('foo', [locals()]) c, = script.completions() a, b, c = c.params assert a.infer() == [] assert [d.name for d in b.infer()] == ['str'] assert {d.name for d in c.infer()} == {'int', 'float'} d, = jedi.Interpreter('foo()', [locals()]).goto_definitions() assert d.name == 'bytes'
def test_keyword_argument(): def f(some_keyword_argument): pass c, = jedi.Interpreter("f(some_keyw", [{'f': f}]).completions() assert c.name == 'some_keyword_argument' assert c.complete == 'ord_argument=' # This needs inspect.signature to work. if is_py3: # Make it impossible for jedi to find the source of the function. f.__name__ = 'xSOMETHING' c, = jedi.Interpreter("x(some_keyw", [{'x': f}]).completions() assert c.name == 'some_keyword_argument'
def get_completions(self, info): """Gets Python completions based on the current cursor position within the %%init_spark cell. Based on https://github.com/Calysto/metakernel/blob/master/metakernel/magics/python_magic.py Parameters ---------- info : dict Information about the current caret position """ if jedi is None: return [] text = info['code'] position = (info['line_num'], info['column']) interpreter = jedi.Interpreter(text, [self.env]) lines = common.splitlines(text) name = get_on_completion_name(interpreter._get_module_node(), lines, position) before = text[:len(text) - len(name)] completions = interpreter.completions() completions = [before + c.name_with_symbols for c in completions] return [c[info['start']:] for c in completions]
def shell_complete(self, identities, msg): code = msg['content']['code'] cursor_pos = msg['content']['cursor_pos'] linenr, colnr = cursor_pos_to_lc(code, cursor_pos) script = jedi.Interpreter(code, [self.user_ns, self.user_global_ns], line=linenr, column=colnr) completions = script.completions() matches = [c.name_with_symbols for c in completions] rests = [ len(c.name_with_symbols) - len(c.complete) for c in completions ] replace_start = cursor_pos - rests[0] if len(rests) > 0 else cursor_pos content = { 'matches': matches, 'cursor_start': replace_start, 'cursor_end': cursor_pos, 'status': 'ok' } metadata = {} self.send(self.shell_stream, 'complete_reply', content, metadata=metadata, parent_header=msg['header'], identities=identities)
def _jedi_complete(*args, **kws): import jedi import json i = jedi.Interpreter(*args, **kws) cs = i.completions() d = [dict(name=c.name, complete=c.complete, docstring=c.docstring()) for c in cs] print(json.dumps(d))
def get_reticulate_completions(document, complete_event): word = document.get_word_before_cursor() prefix_length = settings.completion_prefix_length if len(word ) < prefix_length and not complete_event.completion_requested: return [] glo = rcopy(rcall(("reticulate", "py_run_string"), "globals()")) loc = rcopy(rcall(("reticulate", "py_run_string"), "locals()")) try: script = jedi.Interpreter(document.text, column=document.cursor_position_col, line=document.cursor_position_row + 1, path="input-text", namespaces=[glo, loc]) return [ Completion( text_type(c.name_with_symbols), len(text_type(c.complete)) - len(text_type(c.name_with_symbols))) for c in script.completions() ] except Exception: return []
def get_jedi_script_from_document(document, locals, globals): try: return jedi.Interpreter(document.text, column=document.cursor_position_col, line=document.cursor_position_row + 1, path='input-text', namespaces=[locals, globals]) except jedi.common.MultiLevelStopIteration: # This happens when the document is just a backslash. return None except ValueError: # Invalid cursor position. # ValueError('`column` parameter is not in a valid range.') return None except AttributeError: # Workaround for #65: https://github.com/jonathanslenders/python-prompt-toolkit/issues/65 # See also: https://github.com/davidhalter/jedi/issues/508 return None except IndexError: # Workaround Jedi issue #514: for https://github.com/davidhalter/jedi/issues/514 return None except KeyError: # Workaroud for a crash when the input is "u'", the start of a unicode string. return None
def get_completions(code: str, cursor: Optional[int] = None, namespaces: Optional[List] = None) -> List[str]: """ Get code autocompletion candidates Note that this function requires to have the jedi module loaded. Parameters ---------- code the Python code to complete. cursor optional position in the code at which to autocomplete namespaces a list of namespaces Returns ------- a list of autocompleted modules """ import jedi import __main__ if namespaces is None: namespaces = [__main__.__dict__] if cursor is None: cursor = len(code) code = code[:cursor] interp = jedi.Interpreter(code, namespaces) completions = interp.completions() return [x.name for x in completions]
def test_dir_magic_method(): class CompleteAttrs(object): def __getattr__(self, name): if name == 'foo': return 1 if name == 'bar': return 2 raise AttributeError(name) def __dir__(self): if is_py3: names = object.__dir__(self) else: names = dir(object()) return ['foo', 'bar'] + names itp = jedi.Interpreter("ca.", [{'ca': CompleteAttrs()}]) completions = itp.completions() names = [c.name for c in completions] assert ('__dir__' in names) == is_py3 assert '__class__' in names assert 'foo' in names assert 'bar' in names foo = [c for c in completions if c.name == 'foo'][0] assert foo._goto_definitions() == []
def test_illegal_class_instance(): class X: __class__ = 1 X.__name__ = 'asdf' d, = jedi.Interpreter('foo', [{'foo': X()}]).infer() v, = d._name.infer() assert not v.is_instance()
def update_calltips(self): if not check_module("jedi", "0.17"): return import jedi line, column = self.getCursorPosition() # Simulate QGIS python default imports init_lines = [ "from qgis.core import *", "from qgis.gui import *", "from qgis.utils import iface", "from PyQt5.QtCore import *", "from PyQt5.QtWidgets import *", "from PyQt5.QtGui import *", "iface: QgisInterface = iface", ] init_text = "\n".join(init_lines) + "\n" script = jedi.Interpreter(code=init_text + self.text(), namespaces=(globals(), locals())) try: res = script.get_signatures(line + 1 + len(init_lines), column) except TypeError: res = None if not res: self.callTips.hide() return bracket_line, bracket_column = res[0].bracket_start pos = self.positionFromLineIndex(bracket_line - 1 - len(init_lines), bracket_column) x = self.SendScintilla(QsciScintilla.SCI_POINTXFROMPOSITION, 0, pos) y = self.SendScintilla(QsciScintilla.SCI_POINTYFROMPOSITION, 0, pos) self.callTips.show_signatures(res, QPoint(x, y))
def test_dir_magic_method(allow_unsafe_getattr): class CompleteAttrs(object): def __getattr__(self, name): if name == 'foo': return 1 if name == 'bar': return 2 raise AttributeError(name) def __dir__(self): return ['foo', 'bar'] + object.__dir__(self) itp = jedi.Interpreter("ca.", [{'ca': CompleteAttrs()}]) completions = itp.complete() names = [c.name for c in completions] assert ('__dir__' in names) is True assert '__class__' in names assert 'foo' in names assert 'bar' in names foo = [c for c in completions if c.name == 'foo'][0] if allow_unsafe_getattr: inst, = foo.infer() assert inst.name == 'int' assert inst.type == 'instance' else: assert foo.infer() == []
def get_jedi_script_from_document(document, locals, globals): import jedi # We keep this import in-line, to improve start-up time. # Importing Jedi is 'slow'. try: return jedi.Interpreter(document.text, column=document.cursor_position_col, line=document.cursor_position_row + 1, path='input-text', namespaces=[locals, globals]) except ValueError: # Invalid cursor position. # ValueError('`column` parameter is not in a valid range.') return None except AttributeError: # Workaround for #65: https://github.com/jonathanslenders/python-prompt-toolkit/issues/65 # See also: https://github.com/davidhalter/jedi/issues/508 return None except IndexError: # Workaround Jedi issue #514: for https://github.com/davidhalter/jedi/issues/514 return None except KeyError: # Workaroud for a crash when the input is "u'", the start of a unicode string. return None except Exception: # Workaround for: https://github.com/jonathanslenders/ptpython/issues/91 return None
def test_sys_path_docstring(): # Was an issue in #1298 import jedi s = jedi.Interpreter("from sys import path\npath", line=2, column=4, namespaces=[locals()]) s.completions()[0].docstring()
def test_param_completion(): def foo(bar): pass lambd = lambda xyz: 3 _assert_interpreter_complete('foo(bar', locals(), ['bar']) assert bool(jedi.Interpreter('lambd(xyz', [locals()]).completions()) == is_py3
def test_mixed_module_cache(): """Caused by #1479""" interpreter = jedi.Interpreter('jedi', [{'jedi': jedi}]) d, = interpreter.infer() assert d.name == 'jedi' inference_state = interpreter._inference_state jedi_module, = inference_state.module_cache.get(('jedi', )) assert isinstance(jedi_module, ModuleValue)
def test_more_complex_instances(): class Something: def foo(self, other): return self class Base: def wow(self): return Something() script = jedi.Interpreter('Base().wow().foo', [locals()]) c, = script.complete() assert c.name == 'foo' x = Base() script = jedi.Interpreter('x.wow().foo', [locals()]) c, = script.complete() assert c.name == 'foo'
def get_interpreter_completions(source: str, namespaces: List[Dict]): import jedi interpreter = jedi.Interpreter(source, namespaces) if hasattr(interpreter, "completions"): # up to jedi 0.17 return _tweak_completions(interpreter.completions()) else: return _tweak_completions(interpreter.complete())
def test_completion_params(): foo = lambda a, b=3: None script = jedi.Interpreter('foo', [locals()]) c, = script.completions() assert [p.name for p in c.params] == ['a', 'b'] assert c.params[0]._goto_definitions() == [] t, = c.params[1]._goto_definitions() assert t.name == 'int'
def test_name_not_inferred_properly(): """ In IPython notebook it is typical that some parts of the code that is provided was already executed. In that case if something is not properly inferred, it should still infer from the variables it already knows. """ x = 1 d, = jedi.Interpreter('x = UNDEFINED; x', [locals()]).infer() assert d.name == 'int'
def test_property_content(): class Foo3(object): @property def bar(self): return 1 foo = Foo3() def_, = jedi.Interpreter('foo.bar', [locals()]).infer() assert def_.name == 'int'
def test_param_completion(): def foo(bar): pass lambd = lambda xyz: 3 _assert_interpreter_complete('foo(bar', locals(), ['bar']) # TODO we're not yet using the Python3.5 inspect.signature, yet. assert not jedi.Interpreter('lambd(xyz', [locals()]).completions()
def test_param_annotation_completion(class_is_findable): class Foo: bar = 3 if not class_is_findable: Foo.__name__ = 'asdf' code = 'def CallFoo(x: Foo):\n x.ba' def_, = jedi.Interpreter(code, [locals()]).complete() assert def_.name == 'bar'
def test__wrapped__(): from functools import lru_cache @lru_cache(maxsize=128) def syslogs_to_df(): pass c, = jedi.Interpreter('syslogs_to_df', [locals()]).complete() # Apparently the function starts on the line where the decorator starts. assert c.line == syslogs_to_df.__wrapped__.__code__.co_firstlineno + 1
def test_completion_params(): foo = lambda a, b=3: None script = jedi.Interpreter('foo', [locals()]) c, = script.complete() sig, = c.get_signatures() assert [p.name for p in sig.params] == ['a', 'b'] assert sig.params[0].infer() == [] t, = sig.params[1].infer() assert t.name == 'int'
def jedi_completions(text, offset): ''' autocomplete() must be called first. Not used yet. Returns the same completions jedi would. Examples -------- from riptable.rt_misc import jedi_completions st = Struct({'a': 5}) jedi_completions('st', 2) ''' def position_to_cursor(text: str, offset: int): before = text[:offset] blines = before.split('\n') line = before.count('\n') col = len(blines[-1]) return line, col def cursor_to_position(text: str, line: int, column: int) -> int: lines = text.split('\n') return sum(len(l) + 1 for l in lines[:line]) + column try: ipc = Hooker._ipcompleter cursor_line, cursor_column = position_to_cursor(text, offset) namespaces = [ipc.namespace] if ipc.global_namespace is not None: namespaces.append(ipc.global_namespace) completion_filter = lambda x: x offset = cursor_to_position(text, cursor_line, cursor_column) # filter output if we are completing for object members if offset: pre = text[offset - 1] # if pre == '.': # if self.omit__names == 2: # completion_filter = lambda c:not c.name.startswith('_') # elif self.omit__names == 1: # completion_filter = lambda c:not (c.name.startswith('__') and c.name.endswith('__')) # elif self.omit__names == 0: # completion_filter = lambda x:x # else: # raise ValueError("Don't understand self.omit__names == {}".format(self.omit__names)) import jedi interpreter = jedi.Interpreter(text[:offset], namespaces, column=cursor_column, line=cursor_line + 1) return interpreter.completions() except Exception: return []
def test_simple_completions(code, completions): x = [str] y = {1} z = {1: str, 2: list} import collections deq = collections.deque([1]) counter = collections.Counter(['asdf']) string = '' defs = jedi.Interpreter(code, [locals()]).complete() assert [d.name for d in defs] == completions