Skip to content

Commit e550765

Browse files
committed
Merge branch 'master' into 91-dev-deps
2 parents c73b7da + 045bffd commit e550765

26 files changed

Lines changed: 327 additions & 22 deletions
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# -*- test-case-name: twistedchecker.test -*-
2+
# Copyright (c) Twisted Matrix Laboratories.
3+
# See LICENSE for details.
14
"""
25
Checkers of Twistedchecker.
3-
"""
6+
"""

twistedchecker/checkers/comment.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class CommentChecker(BaseChecker):
88
"""
99
A checker for checking comment issues.
1010
11-
A good comment should begin with one whiespace and
11+
A good comment should begin with one whitespace and
1212
with first letter capitalized.
1313
"""
1414
msgs = {
@@ -31,8 +31,19 @@ def visit_module(self, node):
3131
# Failed to open the module
3232
return
3333
isFirstLineOfComment = True
34+
isDocString = False
3435
lines = node.file_stream.readlines()
3536
for linenum, line in enumerate(lines):
37+
if line.strip().startswith('"""'):
38+
# This is a simple assumption than docstring are delimited
39+
# with triple double quotes on a single line.
40+
# Should do the job for Twisted code.
41+
isDocString = not isDocString
42+
43+
if isDocString:
44+
# We ignore comments in docstrings.
45+
continue
46+
3647
matchedComment = COMMENT_RGX.search(STRING_RGX.sub('', line))
3748
if matchedComment:
3849
if isFirstLineOfComment:

twistedchecker/checkers/docstring.py

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from logilab.astng import node_classes
1212
from logilab.astng import scoped_nodes
13+
from logilab.astng.exceptions import InferenceError
1314

1415
from pylint.interfaces import IASTNGChecker
1516
from pylint.checkers.base import DocStringChecker as PylintDocStringChecker
@@ -35,6 +36,40 @@ class or inner function.
3536

3637

3738

39+
def _getDecoratorsName(node):
40+
"""
41+
Return a list with names of decorators attached to this node.
42+
43+
@param node: current node of pylint
44+
"""
45+
try:
46+
return node.decoratornames()
47+
except InferenceError:
48+
# For setter properties pylint fails so we use a custom code.
49+
decorators = []
50+
for decorator in node.decorators.nodes:
51+
decorators.append(decorator.as_string())
52+
return decorators
53+
54+
55+
def _isSetter(node_type, node):
56+
"""
57+
Determine whether the given node is a setter property.
58+
59+
@param node_type: The type of the node to inspect.
60+
@param node: The L{logilab.astng.bases.NodeNG} to inspect.
61+
62+
@return: a boolean indicating if the given node is a setter.
63+
"""
64+
if node_type not in ['function', 'method']:
65+
return False
66+
67+
for name in _getDecoratorsName(node):
68+
if '.setter' in name:
69+
return True
70+
return False
71+
72+
3873
class DocstringChecker(PylintDocStringChecker):
3974
"""
4075
A checker for checking docstrings.
@@ -114,9 +149,16 @@ def _check_docstring(self, node_type, node):
114149
docstring = node.doc
115150
if docstring is None:
116151
# The node does not have a docstring.
117-
# But do not check things inside a function or method.
118-
if not _isInner(node):
119-
self.add_message('W9208', node=node)
152+
if _isInner(node):
153+
# Do not check things inside a function or method.
154+
return
155+
156+
if _isSetter(node_type, node):
157+
# Setters don't need a docstring as they are documented in
158+
# the getter.
159+
return
160+
161+
self.add_message('W9208', node=node)
120162
return
121163
elif not docstring.strip():
122164
# Empty docstring.
@@ -206,15 +248,46 @@ def _checkEpytext(self, node_type, node, linenoDocstring):
206248
# The first argument usually named 'self'.
207249
argnames = (node.argnames()[1:] if node_type == 'method'
208250
else node.argnames())
251+
252+
if _isSetter(node_type, node):
253+
# For setter methods we remove the `value` argument as it
254+
# does not need to be documented.
255+
try:
256+
argnames.remove('value')
257+
except ValueError:
258+
# No `value` in arguments.
259+
pass
260+
209261
for argname in argnames:
210262
if not re.search(r"@param\s+%s\s*:" % argname, node.doc):
211263
self.add_message('W9202', line=linenoDocstring,
212264
node=node, args=argname)
213265
if not re.search(r"@type\s+%s\s*:" % argname, node.doc):
214266
self.add_message('W9203', line=linenoDocstring,
215267
node=node, args=argname)
268+
269+
self._checkReturnValueEpytext(node, linenoDocstring)
270+
271+
272+
def _checkReturnValueEpytext(self, node, linenoDocstring):
273+
"""
274+
Check if return value is documented.
275+
276+
@param node: current node of pylint
277+
@param linenoDocstring: linenumber of docstring
278+
"""
279+
# Getter properties don't need to document their return value,
280+
# but then need to have a return value.
281+
if '__builtin__.property' in _getDecoratorsName(node):
282+
if self._hasReturnValue(node):
283+
# Getter properties don't need a docstring.
284+
return
285+
216286
# Check for return value.
217287
if self._hasReturnValue(node):
288+
if node.name.startswith('test_'):
289+
# Ignore return documentation for test methods.
290+
return
218291
if not re.search(r"@return[s]{0,1}\s*:", node.doc):
219292
self.add_message('W9204', line=linenoDocstring, node=node)
220293
if not re.search(r"@rtype\s*:", node.doc):

twistedchecker/checkers/header.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,9 @@ def _checkTestReference(self, text, node):
6666
@param text: codes of the module
6767
@param node: node of the module
6868
"""
69+
if '.test.' in node.name or '.test_' in node.name:
70+
# Test packages or test modules don't need references to tests.
71+
return
72+
6973
if not re.search(self.patternTestReference, text):
7074
self.add_message('W9002', node=node)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""
2+
Extension of pylint format checkers.
3+
"""
4+
from pylint.checkers.format import FormatChecker
5+
6+
7+
def check_lines(self, lines, i):
8+
"""
9+
check lines have less than a maximum number of characters.
10+
11+
It ignored lines with long URLs.
12+
"""
13+
maxChars = self.config.max_line_length
14+
for line in lines.splitlines():
15+
if len(line) > maxChars:
16+
if 'http://' in line or 'https://' in line:
17+
continue
18+
self.add_message('C0301', line=i, args=(len(line), maxChars))
19+
i += 1
20+
21+
22+
23+
def patch():
24+
"""
25+
pylint thinks that its default checkers are so special that anybody
26+
wants them so there is no clean way to prevent them from being loaded
27+
or to unregister them.
28+
"""
29+
FormatChecker.check_lines = check_lines

twistedchecker/checkers/pep8format.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,16 @@ def isClassDefDecorator(thing):
278278
# There should only be 1 line or less between docstrings and
279279
# the next function
280280
if previous_is_comment:
281+
# Previous is a comment so it has one extra indentation.
282+
at_same_indent = previous_indent_level - 4 == indent_level
283+
if (
284+
at_same_indent and
285+
logical_line.startswith('def ') and
286+
blank_before == 2
287+
):
288+
# This look like a previous method with a docstring
289+
# and empty body.
290+
return
281291
if blank_before > 1:
282292
yield 0, (
283293
"E305 too many blank lines after docstring "

twistedchecker/configuration/pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function-rgx=((([a-z])|([a-z]+_[a-z]))[a-zA-Z0-9]+)$|(__[a-z]+__)$|(\_.+)$|(test
6363
# Attribute names should be in mixed case, with the first letter lower case,
6464
# each word separated by having its first letter capitalized just like method names.
6565
# And all private names should begin with an underscore.
66-
attr-rgx=((([a-z_])|([a-z]+_[a-z]))[a-zA-Z0-9]+)$
66+
attr-rgx=(((([a-z_])|([a-z]+_[a-z]))[a-zA-Z0-9]+)|(([A-Z_][A-Z0-9_]*)))$
6767

6868
# Classes are to be named in mixed case, with the first letter capitalized,
6969
# each word separated by having its first letter capitalized.

twistedchecker/core/runner.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import twistedchecker
1515
from twistedchecker.reporters.limited import LimitedReporter
1616
from twistedchecker.core.exceptionfinder import findAllExceptions
17+
from twistedchecker.checkers import patch_pylint_format
18+
1719

1820
class Runner():
1921
"""
@@ -125,6 +127,9 @@ def registerCheckers(self):
125127
126128
@return: a list of allowed messages
127129
"""
130+
# We patch the default pylint format checker.
131+
patch_pylint_format.patch()
132+
128133
# add checkers for python 3
129134
cfgfile = self.linter.cfgfile_parser
130135
if (cfgfile.has_option("TWISTEDCHECKER", "check-python3") and
@@ -316,6 +321,8 @@ def run(self, args):
316321
exitCode = 1 if diffCount else 0
317322
sys.exit(exitCode)
318323

324+
sys.exit(self.linter.msg_status)
325+
319326

320327
def prepareDiff(self):
321328
"""

twistedchecker/functionaltests/attributename_fail.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,4 @@ def __init__(self):
1515
"""
1616
self.foo_bar_zar = None
1717
self.FooBarZar = None
18-
self.FOOBARZAR = None
1918
self.a = None
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
19:C0103
21
18:C0103
32
16:C0103
43
17:C0103

0 commit comments

Comments
 (0)