Skip to content

Commit 7a1eca1

Browse files
authored
Catch and ignore parser errors for pydocstyle linting (#208)
* Catch and ignore parser errors for pydocstyle linting * Fix pydocstyle empty file * Fix linting
1 parent c665291 commit 7a1eca1

File tree

7 files changed

+98
-57
lines changed

7 files changed

+98
-57
lines changed

pyls/config/source.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def _get_opt(config, key, option, opt_type):
7575
if opt_type == list:
7676
return _parse_list_opt(config.get(key, opt_key))
7777

78-
raise ValueError("Unknown option type: %s", opt_type)
78+
raise ValueError("Unknown option type: %s" % opt_type)
7979

8080

8181
def _parse_list_opt(string):

pyls/plugins/mccabe_lint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def pyls_lint(config, document):
1919
tree = compile(document.source, document.path, "exec", ast.PyCF_ONLY_AST)
2020
except SyntaxError:
2121
# We'll let the other linters point this one out
22-
return
22+
return None
2323

2424
visitor = mccabe.PathGraphingAstVisitor()
2525
visitor.preorder(tree, visitor)

pyls/plugins/pydocstyle_lint.py

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,39 @@ def pyls_lint(document):
2929
document.source, filename, ignore_decorators=ignore_decorators
3030
)
3131

32-
for err in errors:
33-
if err.code not in checked_codes:
34-
continue
35-
36-
lineno = err.definition.start - 1
37-
line = document.lines[lineno]
38-
character = len(line) - len(line.lstrip())
39-
diags.append({
40-
'source': 'pydocstyle',
41-
'code': err.code,
42-
'message': err.message,
43-
'severity': lsp.DiagnosticSeverity.Warning,
44-
'range': {
45-
'start': {'line': lineno, 'character': character},
46-
'end': {'line': lineno, 'character': len(document.lines[lineno])}
47-
}
48-
})
32+
try:
33+
for error in errors:
34+
if error.code not in checked_codes:
35+
continue
36+
diags.append(_parse_diagnostic(document, error))
37+
except pydocstyle.parser.ParseError:
38+
# In the case we cannot parse the Python file, just continue
39+
pass
4940

5041
return diags
42+
43+
44+
def _parse_diagnostic(document, error):
45+
log.info("Got error: %s", error)
46+
lineno = error.definition.start - 1
47+
line = document.lines[0] if document.lines else ""
48+
49+
start_character = len(line) - len(line.lstrip())
50+
end_character = len(line)
51+
52+
return {
53+
'source': 'pydocstyle',
54+
'code': error.code,
55+
'message': error.message,
56+
'severity': lsp.DiagnosticSeverity.Warning,
57+
'range': {
58+
'start': {
59+
'line': lineno,
60+
'character': start_character
61+
},
62+
'end': {
63+
'line': lineno,
64+
'character': end_character
65+
}
66+
}
67+
}

pyls/plugins/rope_completion.py

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Copyright 2017 Palantir Technologies, Inc.
22
import logging
3-
from pyls.lsp import CompletionItemKind
4-
from pyls import hookimpl
5-
63
from rope.contrib.codeassist import code_assist, sorted_proposals
74

5+
from pyls import hookimpl, lsp
6+
7+
88
log = logging.getLogger(__name__)
99

1010

@@ -62,36 +62,36 @@ def _sort_text(definition):
6262
def _kind(d):
6363
""" Return the VSCode type """
6464
MAP = {
65-
'none': CompletionItemKind.Value,
66-
'type': CompletionItemKind.Class,
67-
'tuple': CompletionItemKind.Class,
68-
'dict': CompletionItemKind.Class,
69-
'dictionary': CompletionItemKind.Class,
70-
'function': CompletionItemKind.Function,
71-
'lambda': CompletionItemKind.Function,
72-
'generator': CompletionItemKind.Function,
73-
'class': CompletionItemKind.Class,
74-
'instance': CompletionItemKind.Reference,
75-
'method': CompletionItemKind.Method,
76-
'builtin': CompletionItemKind.Class,
77-
'builtinfunction': CompletionItemKind.Function,
78-
'module': CompletionItemKind.Module,
79-
'file': CompletionItemKind.File,
80-
'xrange': CompletionItemKind.Class,
81-
'slice': CompletionItemKind.Class,
82-
'traceback': CompletionItemKind.Class,
83-
'frame': CompletionItemKind.Class,
84-
'buffer': CompletionItemKind.Class,
85-
'dictproxy': CompletionItemKind.Class,
86-
'funcdef': CompletionItemKind.Function,
87-
'property': CompletionItemKind.Property,
88-
'import': CompletionItemKind.Module,
89-
'keyword': CompletionItemKind.Keyword,
90-
'constant': CompletionItemKind.Variable,
91-
'variable': CompletionItemKind.Variable,
92-
'value': CompletionItemKind.Value,
93-
'param': CompletionItemKind.Variable,
94-
'statement': CompletionItemKind.Keyword,
65+
'none': lsp.CompletionItemKind.Value,
66+
'type': lsp.CompletionItemKind.Class,
67+
'tuple': lsp.CompletionItemKind.Class,
68+
'dict': lsp.CompletionItemKind.Class,
69+
'dictionary': lsp.CompletionItemKind.Class,
70+
'function': lsp.CompletionItemKind.Function,
71+
'lambda': lsp.CompletionItemKind.Function,
72+
'generator': lsp.CompletionItemKind.Function,
73+
'class': lsp.CompletionItemKind.Class,
74+
'instance': lsp.CompletionItemKind.Reference,
75+
'method': lsp.CompletionItemKind.Method,
76+
'builtin': lsp.CompletionItemKind.Class,
77+
'builtinfunction': lsp.CompletionItemKind.Function,
78+
'module': lsp.CompletionItemKind.Module,
79+
'file': lsp.CompletionItemKind.File,
80+
'xrange': lsp.CompletionItemKind.Class,
81+
'slice': lsp.CompletionItemKind.Class,
82+
'traceback': lsp.CompletionItemKind.Class,
83+
'frame': lsp.CompletionItemKind.Class,
84+
'buffer': lsp.CompletionItemKind.Class,
85+
'dictproxy': lsp.CompletionItemKind.Class,
86+
'funcdef': lsp.CompletionItemKind.Function,
87+
'property': lsp.CompletionItemKind.Property,
88+
'import': lsp.CompletionItemKind.Module,
89+
'keyword': lsp.CompletionItemKind.Keyword,
90+
'constant': lsp.CompletionItemKind.Variable,
91+
'variable': lsp.CompletionItemKind.Variable,
92+
'value': lsp.CompletionItemKind.Value,
93+
'param': lsp.CompletionItemKind.Variable,
94+
'statement': lsp.CompletionItemKind.Keyword,
9595
}
9696

9797
return MAP.get(d.type)

pyls/plugins/symbols.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ def _container(definition):
4040
if parent.parent():
4141
return parent.name
4242
except: # pylint: disable=bare-except
43-
pass
43+
return None
44+
45+
return None
4446

4547

4648
def _range(definition):

pyls/server.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,5 @@ def _content_length(line):
123123
return int(value)
124124
except ValueError:
125125
raise ValueError("Invalid Content-Length header: {}".format(value))
126+
127+
return None

test/plugins/test_pydocstyle_lint.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Copyright 2017 Palantir Technologies, Inc.
2-
from pyls import uris
2+
from pyls import lsp, uris
33
from pyls.workspace import Document
44
from pyls.plugins import pydocstyle_lint
55

@@ -20,7 +20,27 @@ def test_pydocstyle():
2020
assert all([d['source'] == 'pydocstyle' for d in diags])
2121

2222
# One we're expecting is:
23-
msg = 'D100: Missing docstring in public module'
24-
unused_import = [d for d in diags if d['message'] == msg][0]
23+
assert diags[0] == {
24+
'code': 'D100',
25+
'message': 'D100: Missing docstring in public module',
26+
'severity': lsp.DiagnosticSeverity.Warning,
27+
'range': {
28+
'start': {'line': 0, 'character': 0},
29+
'end': {'line': 0, 'character': 11},
30+
},
31+
'source': 'pydocstyle'
32+
}
33+
34+
35+
def test_pydocstyle_empty_source():
36+
doc = Document(DOC_URI, "")
37+
diags = pydocstyle_lint.pyls_lint(doc)
38+
assert diags[0]['message'] == 'D100: Missing docstring in public module'
39+
assert len(diags) == 1
40+
2541

26-
assert unused_import['range']['start'] == {'line': 0, 'character': 0}
42+
def test_pydocstyle_invalid_source():
43+
doc = Document(DOC_URI, "bad syntax")
44+
diags = pydocstyle_lint.pyls_lint(doc)
45+
# We're unable to parse the file, so can't get any pydocstyle diagnostics
46+
assert not diags

0 commit comments

Comments
 (0)