コード例 #1
0
def test_jedi_rename(tmp_workspace, config):  # pylint: disable=redefined-outer-name
    # rename the `Test1` class
    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) == 2

    assert changes[0]['textDocument']['uri'] == doc.uri
    assert changes[0]['textDocument']['version'] == doc.version
    assert changes[0].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',
    }]
    path = os.path.join(tmp_workspace.root_path, DOC_NAME_EXTRA)
    uri_extra = uris.from_fs_path(path)
    assert changes[1]['textDocument']['uri'] == uri_extra
    # This also checks whether documents not yet added via textDocument/didOpen
    # but that do need to be renamed in the project have a `null` version
    # number.
    assert changes[1]['textDocument']['version'] is None
    expected = 'from test1 import ShouldBeRenamed\nx = ShouldBeRenamed()\n'
    if os.name == 'nt':
        # The .write method in the temp_workspace_factory functions writes
        # Windows-style line-endings.
        expected = expected.replace('\n', '\r\n')
    assert changes[1].get('edits') == [{
        'range': {
            'start': {
                'line': 0,
                'character': 0
            },
            'end': {
                'line': 2,
                'character': 0
            }
        },
        'newText': expected
    }]
コード例 #2
0
def pylsp_rename(config, workspace, document, position, new_name):  # pylint: disable=unused-argument
    log.debug('Executing rename of %s to %s', document.word_at_position(position), new_name)
    kwargs = _utils.position_to_jedi_linecolumn(document, position)
    kwargs['new_name'] = new_name
    try:
        refactoring = document.jedi_script().rename(**kwargs)
    except NotImplementedError as exc:
        raise Exception('No support for renaming in Python 2/3.5 with Jedi. '
                        'Consider using the rope_rename plugin instead') from exc
    log.debug('Finished rename: %s', refactoring.get_diff())
    changes = []
    for file_path, changed_file in refactoring.get_changed_files().items():
        uri = uris.from_fs_path(str(file_path))
        doc = workspace.get_maybe_document(uri)
        changes.append({
            'textDocument': {
                'uri': uri,
                'version': doc.version if doc else None
            },
            'edits': [
                {
                    'range': {
                        'start': {'line': 0, 'character': 0},
                        'end': {
                            'line': _num_lines(changed_file.get_new_code()),
                            'character': 0,
                        },
                    },
                    'newText': changed_file.get_new_code(),
                }
            ],
        })
    return {'documentChanges': changes}
コード例 #3
0
def test_root_project_with_no_setup_py(pylsp):
    """Default to workspace root."""
    workspace_root = pylsp.workspace.root_path
    test_uri = uris.from_fs_path(os.path.join(workspace_root, 'hello/test.py'))
    pylsp.workspace.put_document(test_uri, 'assert True')
    test_doc = pylsp.workspace.get_document(test_uri)
    assert workspace_root in test_doc.sys_path()
コード例 #4
0
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}
コード例 #5
0
def test_flake8_multiline(workspace):
    config_str = r"""[flake8]
exclude =
    blah/,
    file_2.py
    """

    doc_str = "print('hi')\nimport os\n"

    doc_uri = uris.from_fs_path(
        os.path.join(workspace.root_path, "blah/__init__.py"))
    workspace.put_document(doc_uri, doc_str)

    flake8_settings = get_flake8_cfg_settings(workspace, config_str)

    assert "exclude" in flake8_settings
    assert len(flake8_settings["exclude"]) == 2

    with patch('pylsp.plugins.flake8_lint.Popen') as popen_mock:
        mock_instance = popen_mock.return_value
        mock_instance.communicate.return_value = [bytes(), bytes()]

        doc = workspace.get_document(doc_uri)
        flake8_lint.pylsp_lint(workspace, doc)

    call_args = popen_mock.call_args[0][0]
    assert call_args == ["flake8", "-", "--exclude=blah/,file_2.py"]

    os.unlink(os.path.join(workspace.root_path, "setup.cfg"))
コード例 #6
0
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",
    }]
コード例 #7
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
コード例 #8
0
ファイル: test_pylint_lint.py プロジェクト: bnavigator/spyder
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_flake8_per_file_ignores(workspace):
    config_str = r"""[flake8]
ignores = F403
per-file-ignores =
    **/__init__.py:F401,E402
    test_something.py:E402,
exclude =
    file_1.py
    file_2.py
    """

    doc_str = "print('hi')\nimport os\n"

    doc_uri = uris.from_fs_path(
        os.path.join(workspace.root_path, "blah/__init__.py"))
    workspace.put_document(doc_uri, doc_str)

    flake8_settings = get_flake8_cfg_settings(workspace, config_str)

    assert "perFileIgnores" in flake8_settings
    assert len(flake8_settings["perFileIgnores"]) == 2
    assert "exclude" in flake8_settings
    assert len(flake8_settings["exclude"]) == 2

    doc = workspace.get_document(doc_uri)
    res = flake8_lint.pylsp_lint(workspace, doc)
    assert not res

    os.unlink(os.path.join(workspace.root_path, "setup.cfg"))
コード例 #10
0
ファイル: test_pylint_lint.py プロジェクト: bnavigator/spyder
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)
コード例 #11
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"),
    ]
コード例 #12
0
def test_pycodestyle_config(workspace):
    """ Test that we load config files properly.

    Config files are loaded in the following order:
        tox.ini pep8.cfg setup.cfg pycodestyle.cfg

    Each overriding the values in the last.

    These files are first looked for in the current document's
    directory and then each parent directory until any one is found
    terminating at the workspace root.

    If any section called 'pycodestyle' exists that will be solely used
    and any config in a 'pep8' section will be ignored
    """
    doc_uri = uris.from_fs_path(os.path.join(workspace.root_path, 'test.py'))
    workspace.put_document(doc_uri, DOC)
    doc = workspace.get_document(doc_uri)

    # Make sure we get a warning for 'indentation contains tabs'
    diags = pycodestyle_lint.pylsp_lint(workspace, doc)
    assert [d for d in diags if d['code'] == 'W191']

    content = {
        'setup.cfg': ('[pycodestyle]\nignore = W191, E201, E128', True),
        'tox.ini': ('', False)
    }

    for conf_file, (content, working) in list(content.items()):
        # Now we'll add config file to ignore it
        with open(os.path.join(workspace.root_path, conf_file),
                  'w+',
                  encoding='utf-8') as f:
            f.write(content)
        workspace._config.settings.cache_clear()

        # And make sure we don't get any warnings
        diags = pycodestyle_lint.pylsp_lint(workspace, doc)
        assert len([d for d in diags
                    if d['code'] == 'W191']) == (0 if working else 1)
        assert len([d for d in diags
                    if d['code'] == 'E201']) == (0 if working else 1)
        assert [d for d in diags if d['code'] == 'W391']

        os.unlink(os.path.join(workspace.root_path, conf_file))

    # Make sure we can ignore via the PYLS config as well
    workspace._config.update(
        {'plugins': {
            'pycodestyle': {
                'ignore': ['W191', 'E201']
            }
        }})
    # And make sure we only get one warning
    diags = pycodestyle_lint.pylsp_lint(workspace, doc)
    assert not [d for d in diags if d['code'] == 'W191']
    assert not [d for d in diags if d['code'] == 'E201']
    assert [d for d in diags if d['code'] == 'W391']
コード例 #13
0
def pylsp(tmpdir):
    """ Return an initialized python LS """
    ls = PythonLSPServer(StringIO, StringIO)

    ls.m_initialize(processId=1,
                    rootUri=uris.from_fs_path(str(tmpdir)),
                    initializationOptions={})

    return ls
コード例 #14
0
def test_config_file(tmpdir, workspace):
    # a config file in the same directory as the source file will be used
    conf = tmpdir.join('.style.yapf')
    conf.write('[style]\ncolumn_limit = 14')
    src = tmpdir.join('test.py')
    doc = Document(uris.from_fs_path(src.strpath), workspace, DOC)

    # A was split on multiple lines because of column_limit from config file
    assert pylsp_format_document(doc)[0]['newText'] == "A = [\n    'h', 'w',\n    'a'\n]\n\nB = ['h', 'w']\n"
コード例 #15
0
def pylsp_w_workspace_folders(tmpdir):
    """ Return an initialized python LS """
    ls = PythonLSPServer(StringIO, StringIO)

    folder1 = tmpdir.mkdir('folder1')
    folder2 = tmpdir.mkdir('folder2')

    ls.m_initialize(processId=1,
                    rootUri=uris.from_fs_path(str(folder1)),
                    initializationOptions={},
                    workspaceFolders=[{
                        'uri': uris.from_fs_path(str(folder1)),
                        'name': 'folder1'
                    }, {
                        'uri': uris.from_fs_path(str(folder2)),
                        'name': 'folder2'
                    }])

    workspace_folders = [folder1, folder2]
    return (ls, workspace_folders)
コード例 #16
0
def test_references_builtin(tmp_workspace):  # pylint: disable=redefined-outer-name
    # Over 'UnicodeError':
    position = {'line': 4, 'character': 7}
    doc2_uri = uris.from_fs_path(os.path.join(str(tmp_workspace.root_path), DOC2_NAME))
    doc2 = Document(doc2_uri, tmp_workspace)

    refs = pylsp_references(doc2, position)
    assert len(refs) >= 1

    expected = {'start': {'line': 4, 'character': 7},
                'end': {'line': 4, 'character': 19}}
    ranges = [r['range'] for r in refs]
    assert expected in ranges
コード例 #17
0
def test_non_root_project(pylsp, metafiles):
    repo_root = os.path.join(pylsp.workspace.root_path, 'repo-root')
    os.mkdir(repo_root)
    project_root = os.path.join(repo_root, 'project-root')
    os.mkdir(project_root)

    for metafile in metafiles:
        with open(os.path.join(project_root, metafile), 'w+') as f:
            f.write('# ' + metafile)

    test_uri = uris.from_fs_path(os.path.join(project_root, 'hello/test.py'))
    pylsp.workspace.put_document(test_uri, 'assert True')
    test_doc = pylsp.workspace.get_document(test_uri)
    assert project_root in test_doc.sys_path()
コード例 #18
0
def test_multiple_workspaces(tmpdir, last_diagnostics_monkeypatch):
    DOC_SOURCE = """
def foo():
    return
    unreachable = 1
"""
    DOC_ERR_MSG = "Statement is unreachable"

    # Initialize two workspace folders.
    folder1 = tmpdir.mkdir("folder1")
    ws1 = Workspace(uris.from_fs_path(str(folder1)), Mock())
    ws1._config = Config(ws1.root_uri, {}, 0, {})
    folder2 = tmpdir.mkdir("folder2")
    ws2 = Workspace(uris.from_fs_path(str(folder2)), Mock())
    ws2._config = Config(ws2.root_uri, {}, 0, {})

    # Create configuration file for workspace folder 1.
    mypy_config = folder1.join("mypy.ini")
    mypy_config.write(
        "[mypy]\nwarn_unreachable = True\ncheck_untyped_defs = True")

    # Initialize settings for both folders.
    plugin.pylsp_settings(ws1._config)
    plugin.pylsp_settings(ws2._config)

    # Test document in workspace 1 (uses mypy.ini configuration).
    doc1 = Document(DOC_URI, ws1, DOC_SOURCE)
    diags = plugin.pylsp_lint(ws1._config, ws1, doc1, is_saved=False)
    assert len(diags) == 1
    diag = diags[0]
    assert diag["message"] == DOC_ERR_MSG

    # Test document in workspace 2 (without mypy.ini configuration)
    doc2 = Document(DOC_URI, ws2, DOC_SOURCE)
    diags = plugin.pylsp_lint(ws2._config, ws2, doc2, is_saved=False)
    assert len(diags) == 0
コード例 #19
0
def test_document_path_completions(tmpdir, workspace_other_root_path):
    # Create a dummy module out of the workspace's root_path and try to get
    # completions for it in another file placed next to it.
    module_content = '''
def foo():
    pass
'''

    p = tmpdir.join("mymodule.py")
    p.write(module_content)

    # Content of doc to test completion
    doc_content = """import mymodule
mymodule.f"""
    doc_path = str(tmpdir) + os.path.sep + 'myfile.py'
    doc_uri = uris.from_fs_path(doc_path)
    doc = Document(doc_uri, workspace_other_root_path, doc_content)

    com_position = {'line': 1, 'character': 10}
    completions = pylsp_jedi_completions(doc._config, doc, com_position)
    assert completions[0]['label'] == 'foo()'
コード例 #20
0
def test_per_file_ignores_alternative_syntax(workspace):
    config_str = r"""[flake8]
per-file-ignores = **/__init__.py:F401,E402
    """

    doc_str = "print('hi')\nimport os\n"

    doc_uri = uris.from_fs_path(
        os.path.join(workspace.root_path, "blah/__init__.py"))
    workspace.put_document(doc_uri, doc_str)

    flake8_settings = get_flake8_cfg_settings(workspace, config_str)

    assert "perFileIgnores" in flake8_settings
    assert len(flake8_settings["perFileIgnores"]) == 2

    doc = workspace.get_document(doc_uri)
    res = flake8_lint.pylsp_lint(workspace, doc)
    assert not res

    os.unlink(os.path.join(workspace.root_path, "setup.cfg"))
コード例 #21
0
def pylsp_rename(config, workspace, document, position, new_name):
    rope_config = config.settings(document_path=document.path).get('rope', {})
    rope_project = workspace._rope_project_builder(rope_config)

    rename = Rename(
        rope_project,
        libutils.path_to_resource(rope_project, document.path),
        document.offset_at_position(position)
    )

    log.debug("Executing rename of %s to %s", document.word_at_position(position), new_name)
    changeset = rename.get_changes(new_name, in_hierarchy=True, docs=True)
    log.debug("Finished rename: %s", changeset.changes)
    changes = []
    for change in changeset.changes:
        uri = uris.from_fs_path(change.resource.path)
        doc = workspace.get_maybe_document(uri)
        changes.append({
            'textDocument': {
                'uri': uri,
                'version': doc.version if doc else None
            },
            'edits': [
                {
                    'range': {
                        'start': {'line': 0, 'character': 0},
                        'end': {
                            'line': _num_lines(change.resource),
                            'character': 0,
                        },
                    },
                    'newText': change.new_contents,
                }
            ]
        })
    return {'documentChanges': changes}
コード例 #22
0
ファイル: test_pylint_lint.py プロジェクト: bnavigator/spyder
def test_lint_free_pylint(config, workspace):
    # Can't use temp_document because it might give us a file that doesn't
    # match pylint's naming requirements. We should be keeping this file clean
    # though, so it works for a test of an empty lint.
    assert not pylint_lint.pylsp_lint(
        config, Document(uris.from_fs_path(__file__), workspace), True)
コード例 #23
0
def test_get_missing_document(tmpdir, pylsp):
    source = 'TEXT'
    doc_path = tmpdir.join("test_document.py")
    doc_path.write(source)
    doc_uri = uris.from_fs_path(str(doc_path))
    assert pylsp.workspace.get_document(doc_uri).source == 'TEXT'
コード例 #24
0
# Copyright 2017 Palantir Technologies, Inc.
import os
import pathlib

import pytest
from pylsp import uris

DOC_URI = uris.from_fs_path(__file__)


def path_as_uri(path):
    return pathlib.Path(os.path.abspath(path)).as_uri()


def test_local(pylsp):
    """ Since the workspace points to the test directory """
    assert pylsp.workspace.is_local()


def test_put_document(pylsp):
    pylsp.workspace.put_document(DOC_URI, 'content')
    assert DOC_URI in pylsp.workspace._docs


def test_get_document(pylsp):
    pylsp.workspace.put_document(DOC_URI, 'TEXT')
    assert pylsp.workspace.get_document(DOC_URI).source == 'TEXT'


def test_get_missing_document(tmpdir, pylsp):
    source = 'TEXT'
コード例 #25
0
 def write_doc(text):
     temp_file.write(text)
     temp_file.close()
     doc = Document(uris.from_fs_path(temp_file.name), workspace)
     return doc
コード例 #26
0
def workspace(tmpdir, config):
    ws = Workspace(uris.from_fs_path(str(tmpdir)), Mock())
    ws._config = config
    return ws
コード例 #27
0
def config(tmpdir):
    config = Config(uris.from_fs_path(str(tmpdir)), {}, 0, {})
    config.update(pyls_settings())
    return config
コード例 #28
0
# Copyright 2017-2020 Palantir Technologies, Inc.
# Copyright 2021- Python Language Server Contributors.

import os
from pylsp import lsp, uris
from pylsp.workspace import Document
from pylsp.plugins import pydocstyle_lint

DOC_URI = uris.from_fs_path(
    os.path.join(os.path.dirname(__file__), "pydocstyle.py"))
TEST_DOC_URI = uris.from_fs_path(__file__)

DOC = """import sys

def hello():
\tpass

import json
"""


def test_pydocstyle(config, workspace):
    doc = Document(DOC_URI, workspace, DOC)
    diags = pydocstyle_lint.pylsp_lint(config, doc)

    assert all(d['source'] == 'pydocstyle' for d in diags)

    # One we're expecting is:
    assert diags[0] == {
        'code': 'D100',
        'message': 'D100: Missing docstring in public module',
コード例 #29
0
def workspace(tmpdir):
    """Return a workspace."""
    ws = Workspace(uris.from_fs_path(str(tmpdir)), Mock())
    ws._config = Config(ws.root_uri, {}, 0, {})
    return ws
コード例 #30
0
def workspace_other_root_path(tmpdir):
    """Return a workspace with a root_path other than tmpdir."""
    ws_path = str(tmpdir.mkdir('test123').mkdir('test456'))
    ws = Workspace(uris.from_fs_path(ws_path), Mock())
    ws._config = Config(ws.root_uri, {}, 0, {})
    return ws