Skip to content

Commit

Permalink
make progress-reporting configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
syphar committed Dec 16, 2022
1 parent 8c4704d commit 7335813
Showing 1 changed file with 165 additions and 152 deletions.
317 changes: 165 additions & 152 deletions pylsp_mypy/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,36 @@ def apply_overrides(args: List[str], overrides: List[Any]) -> List[str]:
@hookimpl
def pylsp_lint(
config: Config, workspace: Workspace, document: Document, is_saved: bool
) -> List[Dict[str, Any]]:
settings = config.plugin_settings("pylsp_mypy")
oldSettings1 = config.plugin_settings("mypy-ls")
if oldSettings1 != {}:
raise DeprecationWarning(
"Your configuration uses the namespace mypy-ls, this should be changed to pylsp_mypy"
)
oldSettings2 = config.plugin_settings("mypy_ls")
if oldSettings2 != {}:
raise DeprecationWarning(
"Your configuration uses the namespace mypy_ls, this should be changed to pylsp_mypy"
)
if settings == {}:
settings = oldSettings1
if settings == {}:
settings = oldSettings2

if settings.get("report_progress", False):
with workspace.report_progress("lint: mypy"):
return get_diagnostics(config, workspace, document, settings, is_saved)
else:
return get_diagnostics(config, workspace, document, settings, is_saved)


def get_diagnostics(
config: Config,
workspace: Workspace,
document: Document,
settings: Dict[str, Any],
is_saved: bool,
) -> List[Dict[str, Any]]:
"""
Lints.
Expand All @@ -143,176 +173,159 @@ def pylsp_lint(
List of the linting data.
"""
with workspace.report_progress("lint: mypy"):
settings = config.plugin_settings("pylsp_mypy")
oldSettings1 = config.plugin_settings("mypy-ls")
if oldSettings1 != {}:
raise DeprecationWarning(
"Your configuration uses the namespace mypy-ls, this should be changed to pylsp_mypy"
)
oldSettings2 = config.plugin_settings("mypy_ls")
if oldSettings2 != {}:
raise 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(
"lint settings = %s document.path = %s is_saved = %s",
settings,
document.path,
is_saved,
"non-live, returning cached diagnostics len(cached) = %s",
last_diagnostics[document.path],
)
return last_diagnostics[document.path]

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)
mypyConfigFile = mypyConfigFileMap.get(workspace.root_path)
if mypyConfigFile:
args.append("--config-file")
args.append(mypyConfigFile)

args.append(document.path)
args.append(document.path)

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

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

if not dmypy:
args.extend(["--incremental", "--follow-imports", "silent"])
args = apply_overrides(args, overrides)
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, **windows_flag
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, **windows_flag
)
report = completed_process.stdout.decode()
errors = completed_process.stderr.decode()
exit_status = completed_process.returncode
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, exit_status = 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", "status"], stderr=subprocess.PIPE, **windows_flag
)
errors = completed_process.stderr.decode()
exit_status = completed_process.returncode
if exit_status != 0:
log.info(
"restarting dmypy from status: %s message: %s via path",
exit_status,
errors.strip(),
)
report = completed_process.stdout.decode()
errors = completed_process.stderr.decode()
exit_status = completed_process.returncode
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, exit_status = mypy_api.run(args)
subprocess.run(["dmypy", "restart"], **windows_flag)
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", "status"], stderr=subprocess.PIPE, **windows_flag
# dmypy does not exist on path, but must exist in the env pylsp-mypy is installed in
# -> use dmypy via api
_, errors, exit_status = mypy_api.run_dmypy(["status"])
if exit_status != 0:
log.info(
"restarting dmypy from status: %s message: %s via api",
exit_status,
errors.strip(),
)
errors = completed_process.stderr.decode()
exit_status = completed_process.returncode
if exit_status != 0:
log.info(
"restarting dmypy from status: %s message: %s via path",
exit_status,
errors.strip(),
)
subprocess.run(["dmypy", "restart"], **windows_flag)
else:
# dmypy does not exist on path, but must exist in the env pylsp-mypy is installed in
# -> use dmypy via api
_, errors, exit_status = mypy_api.run_dmypy(["status"])
if exit_status != 0:
log.info(
"restarting dmypy from status: %s message: %s via api",
exit_status,
errors.strip(),
)
mypy_api.run_dmypy(["restart"])
mypy_api.run_dmypy(["restart"])

# run to use existing daemon or restart if required
args = ["run", "--"] + apply_overrides(args, overrides)
# 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, **windows_flag
)
report = completed_process.stdout.decode()
errors = completed_process.stderr.decode()
exit_status = completed_process.returncode
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, exit_status = mypy_api.run_dmypy(args)

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

diagnostics = []

# Expose generic mypy error on the first line.
if errors:
diagnostics.append(
{
"source": "mypy",
"range": {
"start": {"line": 0, "character": 0},
# Client is supposed to clip end column to line length.
"end": {"line": 0, "character": 1000},
},
"message": errors,
# Error if exited with error or warning.
"severity": 1 if exit_status != 0 else 2,
}
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, **windows_flag
)
report = completed_process.stdout.decode()
errors = completed_process.stderr.decode()
exit_status = completed_process.returncode
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, exit_status = mypy_api.run_dmypy(args)

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

diagnostics = []

# Expose generic mypy error on the first line.
if errors:
diagnostics.append(
{
"source": "mypy",
"range": {
"start": {"line": 0, "character": 0},
# Client is supposed to clip end column to line length.
"end": {"line": 0, "character": 1000},
},
"message": errors,
# Error if exited with error or warning.
"severity": 1 if exit_status != 0 else 2,
}
)

for line in report.splitlines():
log.debug("parsing: line = %r", line)
diag = parse_line(line, document)
if diag:
diagnostics.append(diag)
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))
log.info("pylsp-mypy len(diagnostics) = %s", len(diagnostics))

last_diagnostics[document.path] = diagnostics
return diagnostics
last_diagnostics[document.path] = diagnostics
return diagnostics


@hookimpl
Expand Down

0 comments on commit 7335813

Please sign in to comment.