def test_jedi_completion_extra_paths(tmpdir, workspace): # Create a tempfile with some content and pass to extra_paths temp_doc_content = ''' def spam(): pass ''' p = tmpdir.mkdir("extra_path") extra_paths = [str(p)] p = p.join("foo.py") p.write(temp_doc_content) # Content of doc to test completion doc_content = """import foo foo.s""" doc = Document(DOC_URI, workspace, doc_content) # After 'foo.s' without extra paths com_position = {'line': 1, 'character': 5} completions = pylsp_jedi_completions(doc._config, doc, com_position) assert completions is None # Update config extra paths settings = {'pylsp': {'plugins': {'jedi': {'extra_paths': extra_paths}}}} doc.update_config(settings) # After 'foo.s' with extra paths com_position = {'line': 1, 'character': 5} completions = pylsp_jedi_completions(doc._config, doc, com_position) assert completions[0]['label'] == 'spam()'
def test_symbols_all_scopes_with_jedi_environment(workspace): doc = Document(DOC_URI, workspace, DOC) # Update config extra environment env_path = '/tmp/pyenv/bin/python' settings = {'pylsp': {'plugins': {'jedi': {'environment': env_path}}}} doc.update_config(settings) symbols = pylsp_document_symbols(doc._config, doc) helper_check_symbols_all_scope(symbols)
def test_document_line_edit(workspace): doc = Document('file:///uri', workspace, u'itshelloworld') doc.apply_change({ 'text': u'goodbye', 'range': { 'start': {'line': 0, 'character': 3}, 'end': {'line': 0, 'character': 8} } }) assert doc.source == u'itsgoodbyeworld'
def test_document_empty_edit(workspace): doc = Document('file:///uri', workspace, u'') doc.apply_change({ 'range': { 'start': {'line': 0, 'character': 0}, 'end': {'line': 0, 'character': 0} }, 'text': u'f' }) assert doc.source == u'f'
def test_document_end_of_file_edit(workspace): old = [ "print 'a'\n", "print 'b'\n" ] doc = Document('file:///uri', workspace, u''.join(old)) doc.apply_change({'text': u'o', 'range': { 'start': {'line': 2, 'character': 0}, 'end': {'line': 2, 'character': 0} }}) assert doc.lines == [ "print 'a'\n", "print 'b'\n", "o", ]
def test_document_multiline_edit(workspace): old = [ "def hello(a, b):\n", " print a\n", " print b\n" ] doc = Document('file:///uri', workspace, u''.join(old)) doc.apply_change({'text': u'print a, b', 'range': { 'start': {'line': 1, 'character': 4}, 'end': {'line': 2, 'character': 11} }}) assert doc.lines == [ "def hello(a, b):\n", " print a, b\n" ]
def test_jedi_completion_resolve_at_most(config, workspace): # Over 'i' in os.path.isabs(...) com_position = {'line': 1, 'character': 15} doc = Document(DOC_URI, workspace, DOC) # Do not resolve any labels config.update( {'plugins': { 'jedi_completion': { 'resolve_at_most_labels': 0 } }}) items = pylsp_jedi_completions(config, doc, com_position) labels = {i['label'] for i in items} assert 'isabs' in labels # Resolve all items config.update( {'plugins': { 'jedi_completion': { 'resolve_at_most_labels': math.inf } }}) items = pylsp_jedi_completions(config, doc, com_position) labels = {i['label'] for i in items} assert 'isabs(path)' in labels
def test_per_file_caching(config, workspace): # Ensure that diagnostics are cached per-file. with temp_document(DOC, workspace) as doc: assert pylint_lint.pylsp_lint(config, doc, True) assert not pylint_lint.pylsp_lint( config, Document(uris.from_fs_path(__file__), workspace), False)
def test_builtin_definition(config, workspace): # Over 'i' in dict cursor_pos = {'line': 8, 'character': 24} # No go-to def for builtins doc = Document(DOC_URI, workspace, DOC) assert not pylsp_definitions(config, doc, cursor_pos)
def test_completion_with_class_objects(config, workspace): doc_text = 'class FOOBAR(Object): pass\nFOOB' com_position = {'line': 1, 'character': 4} doc = Document(DOC_URI, workspace, doc_text) config.capabilities['textDocument'] = { 'completion': { 'completionItem': { 'snippetSupport': True } } } config.update({ 'plugins': { 'jedi_completion': { 'include_params': True, 'include_class_objects': True, } } }) completions = pylsp_jedi_completions(config, doc, com_position) assert len(completions) == 2 assert completions[0]['label'] == 'FOOBAR' assert completions[0]['kind'] == lsp.CompletionItemKind.Class assert completions[1]['label'] == 'FOOBAR object' assert completions[1]['kind'] == lsp.CompletionItemKind.TypeParameter
def test_jedi_method_completion(config, workspace): # Over the 'y' in 'print Hello().every' com_position = {'line': 20, 'character': 19} doc = Document(DOC_URI, workspace, DOC) config.capabilities['textDocument'] = { 'completion': { 'completionItem': { 'snippetSupport': True } } } config.update({'plugins': {'jedi_completion': {'include_params': True}}}) completions = pylsp_jedi_completions(config, doc, com_position) everyone_method = [ completion for completion in completions if completion['label'] == 'everyone(a, b, c, d)' ][0] # Ensure we only generate snippets for positional args assert everyone_method['insertTextFormat'] == lsp.InsertTextFormat.Snippet assert everyone_method['insertText'] == 'everyone(${1:a}, ${2:b})$0' # Disable param snippets config.update({'plugins': {'jedi_completion': {'include_params': False}}}) completions = pylsp_jedi_completions(config, doc, com_position) everyone_method = [ completion for completion in completions if completion['label'] == 'everyone(a, b, c, d)' ][0] assert 'insertTextFormat' not in everyone_method assert everyone_method['insertText'] == 'everyone'
def test_line_endings(config, workspace, newline): doc = Document(DOC_URI, workspace, f'import os;import sys{2 * newline}dict(a=1)') res = pylsp_format_document(config, doc) assert res[0][ 'newText'] == f'import os{newline}import sys{2 * newline}dict(a=1){newline}'
def test_rope_rename(tmp_workspace, config): # pylint: disable=redefined-outer-name position = {"line": 0, "character": 6} DOC_URI = uris.from_fs_path(os.path.join(tmp_workspace.root_path, DOC_NAME)) doc = Document(DOC_URI, tmp_workspace) result = pylsp_rename(config, tmp_workspace, doc, position, "ShouldBeRenamed") assert len(result.keys()) == 1 changes = result.get("documentChanges") assert len(changes) == 1 changes = changes[0] # Note that this test differs from test_jedi_rename, because rope does not # seem to modify files that haven't been opened with textDocument/didOpen. assert changes.get("edits") == [{ "range": { "start": { "line": 0, "character": 0 }, "end": { "line": 5, "character": 0 }, }, "newText": "class ShouldBeRenamed():\n pass\n\nclass Test2(ShouldBeRenamed):\n pass\n", }]
def test_no_signature(workspace): # Over blank line sig_position = {'line': 9, 'character': 0} doc = Document(DOC_URI, workspace, DOC) sigs = signature.pylsp_signature_help(doc, sig_position)['signatures'] assert not sigs
def test_numpy_hover(workspace): # Over the blank line no_hov_position = {'line': 1, 'character': 0} # Over 'numpy' in import numpy as np numpy_hov_position_1 = {'line': 2, 'character': 8} # Over 'np' in import numpy as np numpy_hov_position_2 = {'line': 2, 'character': 17} # Over 'np' in np.sin numpy_hov_position_3 = {'line': 3, 'character': 1} # Over 'sin' in np.sin numpy_sin_hov_position = {'line': 3, 'character': 4} doc = Document(DOC_URI, workspace, NUMPY_DOC) contents = '' assert contents in pylsp_hover(doc, no_hov_position)['contents'] contents = 'NumPy\n=====\n\nProvides\n' assert contents in pylsp_hover(doc, numpy_hov_position_1)['contents'][0] contents = 'NumPy\n=====\n\nProvides\n' assert contents in pylsp_hover(doc, numpy_hov_position_2)['contents'][0] contents = 'NumPy\n=====\n\nProvides\n' assert contents in pylsp_hover(doc, numpy_hov_position_3)['contents'][0] # https://github.com/davidhalter/jedi/issues/1746 # pylint: disable=import-outside-toplevel import numpy as np if np.lib.NumpyVersion(np.__version__) < '1.20.0': contents = 'Trigonometric sine, element-wise.\n\n' assert contents in pylsp_hover(doc, numpy_sin_hov_position)['contents'][0]
def test_undefined_name_pyflakes(workspace): doc = Document(DOC_URI, workspace, DOC_UNDEFINED_NAME_ERR) diag = pyflakes_lint.pylsp_lint(doc)[0] assert diag['message'] == 'undefined name \'b\'' assert diag['range']['start'] == {'line': 0, 'character': 4} assert diag['severity'] == lsp.DiagnosticSeverity.Error
def test_references(tmp_workspace): # pylint: disable=redefined-outer-name # Over 'Test1' in class Test1(): position = {'line': 0, 'character': 8} DOC1_URI = uris.from_fs_path(os.path.join(tmp_workspace.root_path, DOC1_NAME)) doc1 = Document(DOC1_URI, tmp_workspace) refs = pylsp_references(doc1, position) # Definition, the import and the instantiation assert len(refs) == 3 # Briefly check excluding the definitions (also excludes imports, only counts uses) no_def_refs = pylsp_references(doc1, position, exclude_declaration=True) assert len(no_def_refs) == 1 # Make sure our definition is correctly located doc1_ref = [u for u in refs if u['uri'] == DOC1_URI][0] assert doc1_ref['range']['start'] == {'line': 0, 'character': 6} assert doc1_ref['range']['end'] == {'line': 0, 'character': 11} # Make sure our import is correctly located doc2_import_ref = [u for u in refs if u['uri'] != DOC1_URI][0] assert doc2_import_ref['range']['start'] == {'line': 0, 'character': 18} assert doc2_import_ref['range']['end'] == {'line': 0, 'character': 23} doc2_usage_ref = [u for u in refs if u['uri'] != DOC1_URI][1] assert doc2_usage_ref['range']['start'] == {'line': 3, 'character': 4} assert doc2_usage_ref['range']['end'] == {'line': 3, 'character': 9}
def test_syntax_error_pyflakes(workspace): doc = Document(DOC_URI, workspace, DOC_SYNTAX_ERR) diag = pyflakes_lint.pylsp_lint(doc)[0] assert diag['message'] == 'invalid syntax' assert diag['range']['start'] == {'line': 0, 'character': 12} assert diag['severity'] == lsp.DiagnosticSeverity.Error
def test_parse_line_with_context(monkeypatch, word, bounds, workspace): doc = Document(DOC_URI, workspace) monkeypatch.setattr(Document, "word_at_position", lambda *args: word) diag = plugin.parse_line(TEST_LINE, doc) assert diag["message"] == '"Request" has no attribute "id"' assert diag["range"]["start"] == {"line": 278, "character": bounds[0]} assert diag["range"]["end"] == {"line": 278, "character": bounds[1]}
def test_option_overrides(tmpdir, last_diagnostics_monkeypatch, workspace): import sys from stat import S_IRWXU from textwrap import dedent sentinel = tmpdir / "ran" source = dedent("""\ #!{} import os, sys, pathlib pathlib.Path({!r}).touch() os.execv({!r}, sys.argv) """).format(sys.executable, str(sentinel), sys.executable) wrapper = tmpdir / "bin/wrapper" wrapper.write(source, ensure=True) wrapper.chmod(S_IRWXU) overrides = ["--python-executable", wrapper.strpath, True] last_diagnostics_monkeypatch.setattr( FakeConfig, "plugin_settings", lambda _, p: {"overrides": overrides} if p == "pylsp_mypy" else {}, ) assert not sentinel.exists() diags = plugin.pylsp_lint( config=FakeConfig(), workspace=workspace, document=Document(DOC_URI, workspace, DOC_TYPE_ERR), is_saved=False, ) assert len(diags) == 1 assert sentinel.exists()
def test_option_overrides_dmypy(last_diagnostics_monkeypatch, workspace): overrides = ["--python-executable", "/tmp/fake", True] last_diagnostics_monkeypatch.setattr( FakeConfig, "plugin_settings", lambda _, p: { "overrides": overrides, "dmypy": True, "live_mode": False, } if p == "pylsp_mypy" else {}, ) m = Mock(wraps=lambda a, **_: Mock(returncode=0, **{"stdout.decode": lambda: ""})) last_diagnostics_monkeypatch.setattr(plugin.subprocess, "run", m) plugin.pylsp_lint( config=FakeConfig(), workspace=workspace, document=Document(DOC_URI, workspace, DOC_TYPE_ERR), is_saved=False, ) expected = [ "dmypy", "run", "--", "--python-executable", "/tmp/fake", "--show-column-numbers", __file__, ] m.assert_called_with(expected, stderr=-1, stdout=-1)
def test_multistatement_snippet(config, workspace): config.capabilities['textDocument'] = { 'completion': {'completionItem': {'snippetSupport': True}}} config.update({'plugins': {'jedi_completion': {'include_params': True}}}) document = 'a = 1; from datetime import date' doc = Document(DOC_URI, workspace, document) position = {'line': 0, 'character': len(document)} completions = pylsp_jedi_completions(config, doc, position) assert completions[0]['insertText'] == 'date' document = 'from datetime import date; a = date' doc = Document(DOC_URI, workspace, document) position = {'line': 0, 'character': len(document)} completions = pylsp_jedi_completions(config, doc, position) assert completions[0]['insertText'] == 'date(${1:year}, ${2:month}, ${3:day})$0'
def temp_document(doc_text, workspace): with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_file: name = temp_file.name temp_file.write(doc_text) doc = Document(uris.from_fs_path(name), workspace) return name, doc
def temp_document(doc_text, workspace): try: with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_file: name = temp_file.name temp_file.write(doc_text) yield Document(uris.from_fs_path(name), workspace) finally: os.remove(name)
def test_basic(workspace, config): doc = Document(uris.from_fs_path(str(data / "file.py")), workspace) diagnostics = pyls_lint(config, doc) assert diagnostics == [ build_diagnostic("foo", (7, 4), (7, 7), "deprecated at some point"), build_diagnostic("imported", (9, 0), (9, 8), "test reason"), ]
def test_line_endings(workspace, newline): doc = Document(DOC_URI, workspace, f'import os;import sys{2 * newline}dict(a=1)') res = pylsp_format_document(doc) assert apply_text_edits( doc, res) == f'import os{newline}import sys{2 * newline}dict(a=1){newline}'
def test_matplotlib_completions(config, workspace): doc_mpl = "import matplotlib.pyplot as plt; plt." com_position = {'line': 0, 'character': len(doc_mpl)} doc = Document(DOC_URI, workspace, doc_mpl) items = pylsp_jedi_completions(config, doc, com_position) assert items assert any('plot' in i['label'] for i in items)
def test_pandas_completions(config, workspace): doc_pandas = "import pandas as pd; pd." com_position = {'line': 0, 'character': len(doc_pandas)} doc = Document(DOC_URI, workspace, doc_pandas) items = pylsp_jedi_completions(config, doc, com_position) assert items assert any('DataFrame' in i['label'] for i in items)
def test_numpy_completions(config, workspace): doc_numpy = "import numpy as np; np." com_position = {'line': 0, 'character': len(doc_numpy)} doc = Document(DOC_URI, workspace, doc_numpy) items = pylsp_jedi_completions(config, doc, com_position) assert items assert any('array' in i['label'] for i in items)
def test_pyqt_completion(config, workspace): # Over 'QA' in 'from PyQt5.QtWidgets import QApplication' doc_pyqt = "from PyQt5.QtWidgets import QA" com_position = {'line': 0, 'character': len(doc_pyqt)} doc = Document(DOC_URI, workspace, doc_pyqt) completions = pylsp_jedi_completions(config, doc, com_position) assert completions is not None