1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
# Copyright 2017-2020 Palantir Technologies, Inc.
# Copyright 2021- Python Language Server Contributors.
import logging
import pycodestyle
from pylsp import hookimpl, lsp
from pylsp._utils import get_eol_chars
try:
from autopep8 import continued_indentation as autopep8_c_i
except ImportError:
pass
else:
# Check if autopep8's continued_indentation implementation
# is overriding pycodestyle's and if so, re-register
# the check using pycodestyle's implementation as expected
if autopep8_c_i in pycodestyle._checks["logical_line"]:
del pycodestyle._checks["logical_line"][autopep8_c_i]
pycodestyle.register_check(pycodestyle.continued_indentation)
log = logging.getLogger(__name__)
@hookimpl
def pylsp_lint(workspace, document):
with workspace.report_progress("lint: pycodestyle"):
config = workspace._config
settings = config.plugin_settings("pycodestyle", document_path=document.path)
log.debug("Got pycodestyle settings: %s", settings)
opts = {
"exclude": settings.get("exclude"),
"filename": settings.get("filename"),
"hang_closing": settings.get("hangClosing"),
"ignore": settings.get("ignore"),
"max_line_length": settings.get("maxLineLength"),
"indent_size": settings.get("indentSize"),
"select": settings.get("select"),
}
kwargs = {k: v for k, v in opts.items() if v}
styleguide = pycodestyle.StyleGuide(kwargs)
# Use LF to lint file because other line endings can give false positives.
# See spyder-ide/spyder#19565 for context.
source = document.source
eol_chars = get_eol_chars(source)
if eol_chars in ["\r", "\r\n"]:
source = source.replace(eol_chars, "\n")
lines = source.splitlines(keepends=True)
else:
lines = document.lines
c = pycodestyle.Checker(
filename=document.path,
lines=lines,
options=styleguide.options,
report=PyCodeStyleDiagnosticReport(styleguide.options),
)
c.check_all()
diagnostics = c.report.diagnostics
return diagnostics
class PyCodeStyleDiagnosticReport(pycodestyle.BaseReport):
def __init__(self, options) -> None:
self.diagnostics = []
super().__init__(options=options)
def error(self, line_number, offset, text, check):
code = text[:4]
if self._ignore_code(code):
return
# Don't care about expected errors or warnings
if code in self.expected:
return
# PyCodeStyle will sometimes give you an error the line after the end of the file
# e.g. no newline at end of file
# In that case, the end offset should just be some number ~100
# (because why not? There's nothing to underline anyways)
err_range = {
"start": {"line": line_number - 1, "character": offset},
"end": {
# FIXME: It's a little naiive to mark until the end of the line, can we not easily do better?
"line": line_number - 1,
"character": 100
if line_number > len(self.lines)
else len(self.lines[line_number - 1]),
},
}
diagnostic = {
"source": "pycodestyle",
"range": err_range,
"message": text,
"code": code,
# Are style errors really ever errors?
"severity": _get_severity(code),
}
if code.startswith("W6"):
diagnostic["tags"] = [lsp.DiagnosticTag.Deprecated]
self.diagnostics.append(diagnostic)
def _get_severity(code):
# Are style errors ever really errors?
if code[0] == "E" or code[0] == "W":
return lsp.DiagnosticSeverity.Warning
# If no severity is specified, why wouldn't this be informational only?
return lsp.DiagnosticSeverity.Information
|