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)
예제 #3
0
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'
예제 #4
0
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'
예제 #5
0
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",
    ]
예제 #6
0
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"
    ]
예제 #7
0
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
예제 #8
0
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)
예제 #9
0
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)
예제 #10
0
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
예제 #11
0
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",
    }]
예제 #14
0
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
예제 #15
0
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]
예제 #16
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}
예제 #18
0
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
예제 #19
0
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]}
예제 #20
0
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()
예제 #21
0
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'
예제 #23
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
예제 #24
0
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)
예제 #25
0
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