Пример #1
0
def config(workspace):  # pylint: disable=redefined-outer-name
    """Return a config object."""
    cfg = Config(workspace.root_uri, {}, 0, {})
    cfg._plugin_settings = {
        'plugins': {
            'pylint': {
                'enabled': False,
                'args': [],
                'executable': None
            }
        }
    }
    return cfg
Пример #2
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
Пример #3
0
def pylsp_lint(config: Config, workspace: Workspace, document: Document,
               is_saved: bool) -> List[Dict[str, Any]]:
    """
    Lints.

    Parameters
    ----------
    config : Config
        The pylsp config.
    workspace : Workspace
        The pylsp workspace.
    document : Document
        The document to be linted.
    is_saved : bool
        Weather the document is saved.

    Returns
    -------
    List[Dict[str, Any]]
        List of the linting data.

    """
    settings = config.plugin_settings("mypy-ls")
    log.info(
        "lint settings = %s document.path = %s is_saved = %s",
        settings,
        document.path,
        is_saved,
    )

    live_mode = settings.get("live_mode", True)
    dmypy = settings.get("dmypy", False)

    if dmypy and live_mode:
        # dmypy can only be efficiently run on files that have been saved, see:
        # https://github.com/python/mypy/issues/9309
        log.warning("live_mode is not supported with dmypy, disabling")
        live_mode = False

    args = ["--show-column-numbers"]

    global tmpFile
    if live_mode and not is_saved and tmpFile:
        log.info("live_mode tmpFile = %s", live_mode)
        tmpFile = open(tmpFile.name, "w")
        tmpFile.write(document.source)
        tmpFile.close()
        args.extend(["--shadow-file", document.path, tmpFile.name])
    elif not is_saved and document.path in last_diagnostics:
        # On-launch the document isn't marked as saved, so fall through and run
        # the diagnostics anyway even if the file contents may be out of date.
        log.info(
            "non-live, returning cached diagnostics len(cached) = %s",
            last_diagnostics[document.path],
        )
        return last_diagnostics[document.path]

    if mypyConfigFile:
        args.append("--config-file")
        args.append(mypyConfigFile)

    args.append(document.path)

    if settings.get("strict", False):
        args.append("--strict")

    if not dmypy:
        args.extend(["--incremental", "--follow-imports", "silent"])

        log.info("executing mypy args = %s", args)
        report, errors, _ = mypy_api.run(args)
    else:
        args = ["run", "--"] + args

        log.info("executing dmypy args = %s", args)
        report, errors, _ = mypy_api.run_dmypy(args)

    log.debug("report:\n%s", report)
    log.debug("errors:\n%s", errors)

    diagnostics = []
    for line in report.splitlines():
        log.debug("parsing: line = %r", line)
        diag = parse_line(line, document)
        if diag:
            diagnostics.append(diag)

    log.info("mypy-ls len(diagnostics) = %s", len(diagnostics))

    last_diagnostics[document.path] = diagnostics
    return diagnostics
Пример #4
0
def config(tmpdir):
    config = Config(uris.from_fs_path(str(tmpdir)), {}, 0, {})
    config.update(pyls_settings())
    return config
Пример #5
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
Пример #6
0
def pylsp_document_symbols(config: Config, workspace: Workspace,
                           document: Document) -> List[Dict]:
    """Cell and block comment extraction."""

    settings = config.plugin_settings('pyls_spyder')
    group_cells = settings.get('group_cells', True)
    enable_block_comments = settings.get('enable_block_comments', True)
    lines = document.lines
    cells = []
    blocks = []

    cell_stack = []
    unnamed_cell = 1
    unnamed_block = 1

    for line_num, line in enumerate(lines):
        cell_rule, cell_match = CELL_REGEX.match(line)
        block_rule, block_match = BLOCK_REGEX.match(line)

        if cell_match is not None:
            percentages = cell_match.group(1)
            cell_name = cell_match.group(2).strip()

            if cell_name == '':
                cell_name = 'Unnamed cell {0}'.format(unnamed_cell)
                unnamed_cell += 1

            if not group_cells or cell_rule != CELL_PERCENTAGE:
                cells.append(
                    create_symbol(cell_name, document, line_num, line_num + 1))
            else:
                current_line, current_level, current_name = peek_symbol(
                    cell_stack)
                cell_level = len(percentages) - 1
                if cell_level > current_level:
                    cell_stack.insert(0, (line_num, cell_level, cell_name))
                else:
                    while current_level >= cell_level:
                        cell_stack.pop(0)
                        cells.append(
                            create_symbol(current_name, document, current_line,
                                          line_num))
                        (current_line, current_level,
                         current_name) = peek_symbol(cell_stack)
                    cell_stack.insert(0, (line_num, cell_level, cell_name))
        elif block_match is not None and enable_block_comments:
            block_name = block_match.group(1)
            if block_name is None:
                block_name = ''
            else:
                block_name = block_name.strip()

            if block_name == '':
                block_name = 'Unnamed comment {0}'.format(unnamed_block)
                unnamed_block += 1
            blocks.append(
                create_symbol(block_name, document, line_num, line_num + 1,
                              False))

    for line, _, name in cell_stack:
        cells.append(create_symbol(name, document, line, line_num + 1))

    spyder_symbols = cells + blocks
    spyder_symbols = sorted(
        spyder_symbols, key=lambda x: x['location']['range']['start']['line'])
    return spyder_symbols
Пример #7
0
def pylsp_lint(config: Config, workspace: Workspace, document: Document,
               is_saved: bool) -> List[Dict[str, Any]]:
    """
    Lints.

    Parameters
    ----------
    config : Config
        The pylsp config.
    workspace : Workspace
        The pylsp workspace.
    document : Document
        The document to be linted.
    is_saved : bool
        Weather the document is saved.

    Returns
    -------
    List[Dict[str, Any]]
        List of the linting data.

    """
    settings = config.plugin_settings("pylsp_mypy")
    oldSettings1 = config.plugin_settings("mypy-ls")
    if oldSettings1 != {}:
        warnings.warn(
            DeprecationWarning(
                "Your configuration uses the namespace mypy-ls, this should be changed to pylsp_mypy"
            ))
    oldSettings2 = config.plugin_settings("mypy_ls")
    if oldSettings2 != {}:
        warnings.warn(
            DeprecationWarning(
                "Your configuration uses the namespace mypy_ls, this should be changed to pylsp_mypy"
            ))
    if settings == {}:
        settings = oldSettings1
        if settings == {}:
            settings = oldSettings2

    log.info(
        "lint settings = %s document.path = %s is_saved = %s",
        settings,
        document.path,
        is_saved,
    )

    live_mode = settings.get("live_mode", True)
    dmypy = settings.get("dmypy", False)

    if dmypy and live_mode:
        # dmypy can only be efficiently run on files that have been saved, see:
        # https://github.com/python/mypy/issues/9309
        log.warning("live_mode is not supported with dmypy, disabling")
        live_mode = False

    args = ["--show-column-numbers"]

    global tmpFile
    if live_mode and not is_saved:
        if tmpFile:
            tmpFile = open(tmpFile.name, "w")
        else:
            tmpFile = tempfile.NamedTemporaryFile("w", delete=False)
        log.info("live_mode tmpFile = %s", tmpFile.name)
        tmpFile.write(document.source)
        tmpFile.close()
        args.extend(["--shadow-file", document.path, tmpFile.name])
    elif not is_saved and document.path in last_diagnostics:
        # On-launch the document isn't marked as saved, so fall through and run
        # the diagnostics anyway even if the file contents may be out of date.
        log.info(
            "non-live, returning cached diagnostics len(cached) = %s",
            last_diagnostics[document.path],
        )
        return last_diagnostics[document.path]

    mypyConfigFile = mypyConfigFileMap.get(workspace.root_path)
    if mypyConfigFile:
        args.append("--config-file")
        args.append(mypyConfigFile)

    args.append(document.path)

    if settings.get("strict", False):
        args.append("--strict")

    overrides = settings.get("overrides", [True])

    if not dmypy:
        args.extend(["--incremental", "--follow-imports", "silent"])
        args = apply_overrides(args, overrides)

        if shutil.which("mypy"):
            # mypy exists on path
            # -> use mypy on path
            log.info("executing mypy args = %s on path", args)
            completed_process = subprocess.run(["mypy", *args],
                                               stdout=subprocess.PIPE,
                                               stderr=subprocess.PIPE)
            report = completed_process.stdout.decode()
            errors = completed_process.stderr.decode()
        else:
            # mypy does not exist on path, but must exist in the env pylsp-mypy is installed in
            # -> use mypy via api
            log.info("executing mypy args = %s via api", args)
            report, errors, _ = mypy_api.run(args)
    else:
        # If dmypy daemon is non-responsive calls to run will block.
        # Check daemon status, if non-zero daemon is dead or hung.
        # If daemon is hung, kill will reset
        # If daemon is dead/absent, kill will no-op.
        # In either case, reset to fresh state

        if shutil.which("dmypy"):
            # dmypy exists on path
            # -> use mypy on path
            completed_process = subprocess.run(
                ["dmypy", *apply_overrides(args, overrides)],
                stderr=subprocess.PIPE)
            _err = completed_process.stderr.decode()
            _status = completed_process.returncode
            if _status != 0:
                log.info(
                    "restarting dmypy from status: %s message: %s via path",
                    _status, _err.strip())
                subprocess.run(["dmypy", "kill"])
        else:
            # dmypy does not exist on path, but must exist in the env pylsp-mypy is installed in
            # -> use dmypy via api
            _, _err, _status = mypy_api.run_dmypy(["status"])
            if _status != 0:
                log.info(
                    "restarting dmypy from status: %s message: %s via api",
                    _status, _err.strip())
                mypy_api.run_dmypy(["kill"])

        # run to use existing daemon or restart if required
        args = ["run", "--"] + apply_overrides(args, overrides)

        if shutil.which("dmypy"):
            # dmypy exists on path
            # -> use mypy on path
            log.info("dmypy run args = %s via path", args)
            completed_process = subprocess.run(["dmypy", *args],
                                               stdout=subprocess.PIPE,
                                               stderr=subprocess.PIPE)
            report = completed_process.stdout.decode()
            errors = completed_process.stderr.decode()
        else:
            # dmypy does not exist on path, but must exist in the env pylsp-mypy is installed in
            # -> use dmypy via api
            log.info("dmypy run args = %s via api", args)
            report, errors, _ = mypy_api.run_dmypy(args)

    log.debug("report:\n%s", report)
    log.debug("errors:\n%s", errors)

    diagnostics = []
    for line in report.splitlines():
        log.debug("parsing: line = %r", line)
        diag = parse_line(line, document)
        if diag:
            diagnostics.append(diag)

    log.info("pylsp-mypy len(diagnostics) = %s", len(diagnostics))

    last_diagnostics[document.path] = diagnostics
    return diagnostics
Пример #8
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
Пример #9
0
def config(workspace):  # pylint: disable=redefined-outer-name
    """Return a config object."""
    return Config(workspace.root_uri, {}, 0, {})