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
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
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
def config(tmpdir): config = Config(uris.from_fs_path(str(tmpdir)), {}, 0, {}) config.update(pyls_settings()) return config
def workspace(tmpdir): """Return a workspace.""" ws = Workspace(uris.from_fs_path(str(tmpdir)), Mock()) ws._config = Config(ws.root_uri, {}, 0, {}) return ws
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
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
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
def config(workspace): # pylint: disable=redefined-outer-name """Return a config object.""" return Config(workspace.root_uri, {}, 0, {})