]> crepu.dev Git - config.git/blame_incremental - djavu-asus/emacs/elpy/rpc-venv/lib/python3.11/site-packages/parso/python/pep8.py
Reorganización de directorios
[config.git] / djavu-asus / emacs / elpy / rpc-venv / lib / python3.11 / site-packages / parso / python / pep8.py
... / ...
CommitLineData
1import re
2from contextlib import contextmanager
3from typing import Tuple
4
5from parso.python.errors import ErrorFinder, ErrorFinderConfig
6from parso.normalizer import Rule
7from parso.python.tree import Flow, Scope
8
9
10_IMPORT_TYPES = ('import_name', 'import_from')
11_SUITE_INTRODUCERS = ('classdef', 'funcdef', 'if_stmt', 'while_stmt',
12 'for_stmt', 'try_stmt', 'with_stmt')
13_NON_STAR_TYPES = ('term', 'import_from', 'power')
14_OPENING_BRACKETS = '(', '[', '{'
15_CLOSING_BRACKETS = ')', ']', '}'
16_FACTOR = '+', '-', '~'
17_ALLOW_SPACE = '*', '+', '-', '**', '/', '//', '@'
18_BITWISE_OPERATOR = '<<', '>>', '|', '&', '^'
19_NEEDS_SPACE: Tuple[str, ...] = (
20 '=', '%', '->',
21 '<', '>', '==', '>=', '<=', '<>', '!=',
22 '+=', '-=', '*=', '@=', '/=', '%=', '&=', '|=', '^=', '<<=',
23 '>>=', '**=', '//=')
24_NEEDS_SPACE += _BITWISE_OPERATOR
25_IMPLICIT_INDENTATION_TYPES = ('dictorsetmaker', 'argument')
26_POSSIBLE_SLICE_PARENTS = ('subscript', 'subscriptlist', 'sliceop')
27
28
29class IndentationTypes:
30 VERTICAL_BRACKET = object()
31 HANGING_BRACKET = object()
32 BACKSLASH = object()
33 SUITE = object()
34 IMPLICIT = object()
35
36
37class IndentationNode(object):
38 type = IndentationTypes.SUITE
39
40 def __init__(self, config, indentation, parent=None):
41 self.bracket_indentation = self.indentation = indentation
42 self.parent = parent
43
44 def __repr__(self):
45 return '<%s>' % self.__class__.__name__
46
47 def get_latest_suite_node(self):
48 n = self
49 while n is not None:
50 if n.type == IndentationTypes.SUITE:
51 return n
52
53 n = n.parent
54
55
56class BracketNode(IndentationNode):
57 def __init__(self, config, leaf, parent, in_suite_introducer=False):
58 self.leaf = leaf
59
60 # Figure out here what the indentation is. For chained brackets
61 # we can basically use the previous indentation.
62 previous_leaf = leaf
63 n = parent
64 if n.type == IndentationTypes.IMPLICIT:
65 n = n.parent
66 while True:
67 if hasattr(n, 'leaf') and previous_leaf.line != n.leaf.line:
68 break
69
70 previous_leaf = previous_leaf.get_previous_leaf()
71 if not isinstance(n, BracketNode) or previous_leaf != n.leaf:
72 break
73 n = n.parent
74 parent_indentation = n.indentation
75
76 next_leaf = leaf.get_next_leaf()
77 if '\n' in next_leaf.prefix or '\r' in next_leaf.prefix:
78 # This implies code like:
79 # foobarbaz(
80 # a,
81 # b,
82 # )
83 self.bracket_indentation = parent_indentation \
84 + config.closing_bracket_hanging_indentation
85 self.indentation = parent_indentation + config.indentation
86 self.type = IndentationTypes.HANGING_BRACKET
87 else:
88 # Implies code like:
89 # foobarbaz(
90 # a,
91 # b,
92 # )
93 expected_end_indent = leaf.end_pos[1]
94 if '\t' in config.indentation:
95 self.indentation = None
96 else:
97 self.indentation = ' ' * expected_end_indent
98 self.bracket_indentation = self.indentation
99 self.type = IndentationTypes.VERTICAL_BRACKET
100
101 if in_suite_introducer and parent.type == IndentationTypes.SUITE \
102 and self.indentation == parent_indentation + config.indentation:
103 self.indentation += config.indentation
104 # The closing bracket should have the same indentation.
105 self.bracket_indentation = self.indentation
106 self.parent = parent
107
108
109class ImplicitNode(BracketNode):
110 """
111 Implicit indentation after keyword arguments, default arguments,
112 annotations and dict values.
113 """
114 def __init__(self, config, leaf, parent):
115 super().__init__(config, leaf, parent)
116 self.type = IndentationTypes.IMPLICIT
117
118 next_leaf = leaf.get_next_leaf()
119 if leaf == ':' and '\n' not in next_leaf.prefix and '\r' not in next_leaf.prefix:
120 self.indentation += ' '
121
122
123class BackslashNode(IndentationNode):
124 type = IndentationTypes.BACKSLASH
125
126 def __init__(self, config, parent_indentation, containing_leaf, spacing, parent=None):
127 expr_stmt = containing_leaf.search_ancestor('expr_stmt')
128 if expr_stmt is not None:
129 equals = expr_stmt.children[-2]
130
131 if '\t' in config.indentation:
132 # TODO unite with the code of BracketNode
133 self.indentation = None
134 else:
135 # If the backslash follows the equals, use normal indentation
136 # otherwise it should align with the equals.
137 if equals.end_pos == spacing.start_pos:
138 self.indentation = parent_indentation + config.indentation
139 else:
140 # +1 because there is a space.
141 self.indentation = ' ' * (equals.end_pos[1] + 1)
142 else:
143 self.indentation = parent_indentation + config.indentation
144 self.bracket_indentation = self.indentation
145 self.parent = parent
146
147
148def _is_magic_name(name):
149 return name.value.startswith('__') and name.value.endswith('__')
150
151
152class PEP8Normalizer(ErrorFinder):
153 def __init__(self, *args, **kwargs):
154 super().__init__(*args, **kwargs)
155 self._previous_part = None
156 self._previous_leaf = None
157 self._on_newline = True
158 self._newline_count = 0
159 self._wanted_newline_count = None
160 self._max_new_lines_in_prefix = 0
161 self._new_statement = True
162 self._implicit_indentation_possible = False
163 # The top of stack of the indentation nodes.
164 self._indentation_tos = self._last_indentation_tos = \
165 IndentationNode(self._config, indentation='')
166 self._in_suite_introducer = False
167
168 if ' ' in self._config.indentation:
169 self._indentation_type = 'spaces'
170 self._wrong_indentation_char = '\t'
171 else:
172 self._indentation_type = 'tabs'
173 self._wrong_indentation_char = ' '
174
175 @contextmanager
176 def visit_node(self, node):
177 with super().visit_node(node):
178 with self._visit_node(node):
179 yield
180
181 @contextmanager
182 def _visit_node(self, node):
183 typ = node.type
184
185 if typ in 'import_name':
186 names = node.get_defined_names()
187 if len(names) > 1:
188 for name in names[:1]:
189 self.add_issue(name, 401, 'Multiple imports on one line')
190 elif typ == 'lambdef':
191 expr_stmt = node.parent
192 # Check if it's simply defining a single name, not something like
193 # foo.bar or x[1], where using a lambda could make more sense.
194 if expr_stmt.type == 'expr_stmt' and any(n.type == 'name'
195 for n in expr_stmt.children[:-2:2]):
196 self.add_issue(node, 731, 'Do not assign a lambda expression, use a def')
197 elif typ == 'try_stmt':
198 for child in node.children:
199 # Here we can simply check if it's an except, because otherwise
200 # it would be an except_clause.
201 if child.type == 'keyword' and child.value == 'except':
202 self.add_issue(child, 722, 'Do not use bare except, specify exception instead')
203 elif typ == 'comparison':
204 for child in node.children:
205 if child.type not in ('atom_expr', 'power'):
206 continue
207 if len(child.children) > 2:
208 continue
209 trailer = child.children[1]
210 atom = child.children[0]
211 if trailer.type == 'trailer' and atom.type == 'name' \
212 and atom.value == 'type':
213 self.add_issue(node, 721, "Do not compare types, use 'isinstance()")
214 break
215 elif typ == 'file_input':
216 endmarker = node.children[-1]
217 prev = endmarker.get_previous_leaf()
218 prefix = endmarker.prefix
219 if (not prefix.endswith('\n') and not prefix.endswith('\r') and (
220 prefix or prev is None or prev.value not in {'\n', '\r\n', '\r'})):
221 self.add_issue(endmarker, 292, "No newline at end of file")
222
223 if typ in _IMPORT_TYPES:
224 simple_stmt = node.parent
225 module = simple_stmt.parent
226 if module.type == 'file_input':
227 index = module.children.index(simple_stmt)
228 for child in module.children[:index]:
229 children = [child]
230 if child.type == 'simple_stmt':
231 # Remove the newline.
232 children = child.children[:-1]
233
234 found_docstring = False
235 for c in children:
236 if c.type == 'string' and not found_docstring:
237 continue
238 found_docstring = True
239
240 if c.type == 'expr_stmt' and \
241 all(_is_magic_name(n) for n in c.get_defined_names()):
242 continue
243
244 if c.type in _IMPORT_TYPES or isinstance(c, Flow):
245 continue
246
247 self.add_issue(node, 402, 'Module level import not at top of file')
248 break
249 else:
250 continue
251 break
252
253 implicit_indentation_possible = typ in _IMPLICIT_INDENTATION_TYPES
254 in_introducer = typ in _SUITE_INTRODUCERS
255 if in_introducer:
256 self._in_suite_introducer = True
257 elif typ == 'suite':
258 if self._indentation_tos.type == IndentationTypes.BACKSLASH:
259 self._indentation_tos = self._indentation_tos.parent
260
261 self._indentation_tos = IndentationNode(
262 self._config,
263 self._indentation_tos.indentation + self._config.indentation,
264 parent=self._indentation_tos
265 )
266 elif implicit_indentation_possible:
267 self._implicit_indentation_possible = True
268 yield
269 if typ == 'suite':
270 assert self._indentation_tos.type == IndentationTypes.SUITE
271 self._indentation_tos = self._indentation_tos.parent
272 # If we dedent, no lines are needed anymore.
273 self._wanted_newline_count = None
274 elif implicit_indentation_possible:
275 self._implicit_indentation_possible = False
276 if self._indentation_tos.type == IndentationTypes.IMPLICIT:
277 self._indentation_tos = self._indentation_tos.parent
278 elif in_introducer:
279 self._in_suite_introducer = False
280 if typ in ('classdef', 'funcdef'):
281 self._wanted_newline_count = self._get_wanted_blank_lines_count()
282
283 def _check_tabs_spaces(self, spacing):
284 if self._wrong_indentation_char in spacing.value:
285 self.add_issue(spacing, 101, 'Indentation contains ' + self._indentation_type)
286 return True
287 return False
288
289 def _get_wanted_blank_lines_count(self):
290 suite_node = self._indentation_tos.get_latest_suite_node()
291 return int(suite_node.parent is None) + 1
292
293 def _reset_newlines(self, spacing, leaf, is_comment=False):
294 self._max_new_lines_in_prefix = \
295 max(self._max_new_lines_in_prefix, self._newline_count)
296
297 wanted = self._wanted_newline_count
298 if wanted is not None:
299 # Need to substract one
300 blank_lines = self._newline_count - 1
301 if wanted > blank_lines and leaf.type != 'endmarker':
302 # In case of a comment we don't need to add the issue, yet.
303 if not is_comment:
304 # TODO end_pos wrong.
305 code = 302 if wanted == 2 else 301
306 message = "expected %s blank line, found %s" \
307 % (wanted, blank_lines)
308 self.add_issue(spacing, code, message)
309 self._wanted_newline_count = None
310 else:
311 self._wanted_newline_count = None
312
313 if not is_comment:
314 wanted = self._get_wanted_blank_lines_count()
315 actual = self._max_new_lines_in_prefix - 1
316
317 val = leaf.value
318 needs_lines = (
319 val == '@' and leaf.parent.type == 'decorator'
320 or (
321 val == 'class'
322 or val == 'async' and leaf.get_next_leaf() == 'def'
323 or val == 'def' and self._previous_leaf != 'async'
324 ) and leaf.parent.parent.type != 'decorated'
325 )
326 if needs_lines and actual < wanted:
327 func_or_cls = leaf.parent
328 suite = func_or_cls.parent
329 if suite.type == 'decorated':
330 suite = suite.parent
331
332 # The first leaf of a file or a suite should not need blank
333 # lines.
334 if suite.children[int(suite.type == 'suite')] != func_or_cls:
335 code = 302 if wanted == 2 else 301
336 message = "expected %s blank line, found %s" \
337 % (wanted, actual)
338 self.add_issue(spacing, code, message)
339
340 self._max_new_lines_in_prefix = 0
341
342 self._newline_count = 0
343
344 def visit_leaf(self, leaf):
345 super().visit_leaf(leaf)
346 for part in leaf._split_prefix():
347 if part.type == 'spacing':
348 # This part is used for the part call after for.
349 break
350 self._visit_part(part, part.create_spacing_part(), leaf)
351
352 self._analyse_non_prefix(leaf)
353 self._visit_part(leaf, part, leaf)
354
355 # Cleanup
356 self._last_indentation_tos = self._indentation_tos
357
358 self._new_statement = leaf.type == 'newline'
359
360 # TODO does this work? with brackets and stuff?
361 if leaf.type == 'newline' and \
362 self._indentation_tos.type == IndentationTypes.BACKSLASH:
363 self._indentation_tos = self._indentation_tos.parent
364
365 if leaf.value == ':' and leaf.parent.type in _SUITE_INTRODUCERS:
366 self._in_suite_introducer = False
367 elif leaf.value == 'elif':
368 self._in_suite_introducer = True
369
370 if not self._new_statement:
371 self._reset_newlines(part, leaf)
372 self._max_blank_lines = 0
373
374 self._previous_leaf = leaf
375
376 return leaf.value
377
378 def _visit_part(self, part, spacing, leaf):
379 value = part.value
380 type_ = part.type
381 if type_ == 'error_leaf':
382 return
383
384 if value == ',' and part.parent.type == 'dictorsetmaker':
385 self._indentation_tos = self._indentation_tos.parent
386
387 node = self._indentation_tos
388
389 if type_ == 'comment':
390 if value.startswith('##'):
391 # Whole blocks of # should not raise an error.
392 if value.lstrip('#'):
393 self.add_issue(part, 266, "Too many leading '#' for block comment.")
394 elif self._on_newline:
395 if not re.match(r'#:? ', value) and not value == '#' \
396 and not (value.startswith('#!') and part.start_pos == (1, 0)):
397 self.add_issue(part, 265, "Block comment should start with '# '")
398 else:
399 if not re.match(r'#:? [^ ]', value):
400 self.add_issue(part, 262, "Inline comment should start with '# '")
401
402 self._reset_newlines(spacing, leaf, is_comment=True)
403 elif type_ == 'newline':
404 if self._newline_count > self._get_wanted_blank_lines_count():
405 self.add_issue(part, 303, "Too many blank lines (%s)" % self._newline_count)
406 elif leaf in ('def', 'class') \
407 and leaf.parent.parent.type == 'decorated':
408 self.add_issue(part, 304, "Blank lines found after function decorator")
409
410 self._newline_count += 1
411
412 if type_ == 'backslash':
413 # TODO is this enough checking? What about ==?
414 if node.type != IndentationTypes.BACKSLASH:
415 if node.type != IndentationTypes.SUITE:
416 self.add_issue(part, 502, 'The backslash is redundant between brackets')
417 else:
418 indentation = node.indentation
419 if self._in_suite_introducer and node.type == IndentationTypes.SUITE:
420 indentation += self._config.indentation
421
422 self._indentation_tos = BackslashNode(
423 self._config,
424 indentation,
425 part,
426 spacing,
427 parent=self._indentation_tos
428 )
429 elif self._on_newline:
430 indentation = spacing.value
431 if node.type == IndentationTypes.BACKSLASH \
432 and self._previous_part.type == 'newline':
433 self._indentation_tos = self._indentation_tos.parent
434
435 if not self._check_tabs_spaces(spacing):
436 should_be_indentation = node.indentation
437 if type_ == 'comment':
438 # Comments can be dedented. So we have to care for that.
439 n = self._last_indentation_tos
440 while True:
441 if len(indentation) > len(n.indentation):
442 break
443
444 should_be_indentation = n.indentation
445
446 self._last_indentation_tos = n
447 if n == node:
448 break
449 n = n.parent
450
451 if self._new_statement:
452 if type_ == 'newline':
453 if indentation:
454 self.add_issue(spacing, 291, 'Trailing whitespace')
455 elif indentation != should_be_indentation:
456 s = '%s %s' % (len(self._config.indentation), self._indentation_type)
457 self.add_issue(part, 111, 'Indentation is not a multiple of ' + s)
458 else:
459 if value in '])}':
460 should_be_indentation = node.bracket_indentation
461 else:
462 should_be_indentation = node.indentation
463 if self._in_suite_introducer and indentation == \
464 node.get_latest_suite_node().indentation \
465 + self._config.indentation:
466 self.add_issue(part, 129, "Line with same indent as next logical block")
467 elif indentation != should_be_indentation:
468 if not self._check_tabs_spaces(spacing) and part.value not in \
469 {'\n', '\r\n', '\r'}:
470 if value in '])}':
471 if node.type == IndentationTypes.VERTICAL_BRACKET:
472 self.add_issue(
473 part,
474 124,
475 "Closing bracket does not match visual indentation"
476 )
477 else:
478 self.add_issue(
479 part,
480 123,
481 "Losing bracket does not match "
482 "indentation of opening bracket's line"
483 )
484 else:
485 if len(indentation) < len(should_be_indentation):
486 if node.type == IndentationTypes.VERTICAL_BRACKET:
487 self.add_issue(
488 part,
489 128,
490 'Continuation line under-indented for visual indent'
491 )
492 elif node.type == IndentationTypes.BACKSLASH:
493 self.add_issue(
494 part,
495 122,
496 'Continuation line missing indentation or outdented'
497 )
498 elif node.type == IndentationTypes.IMPLICIT:
499 self.add_issue(part, 135, 'xxx')
500 else:
501 self.add_issue(
502 part,
503 121,
504 'Continuation line under-indented for hanging indent'
505 )
506 else:
507 if node.type == IndentationTypes.VERTICAL_BRACKET:
508 self.add_issue(
509 part,
510 127,
511 'Continuation line over-indented for visual indent'
512 )
513 elif node.type == IndentationTypes.IMPLICIT:
514 self.add_issue(part, 136, 'xxx')
515 else:
516 self.add_issue(
517 part,
518 126,
519 'Continuation line over-indented for hanging indent'
520 )
521 else:
522 self._check_spacing(part, spacing)
523
524 self._check_line_length(part, spacing)
525 # -------------------------------
526 # Finalizing. Updating the state.
527 # -------------------------------
528 if value and value in '()[]{}' and type_ != 'error_leaf' \
529 and part.parent.type != 'error_node':
530 if value in _OPENING_BRACKETS:
531 self._indentation_tos = BracketNode(
532 self._config, part,
533 parent=self._indentation_tos,
534 in_suite_introducer=self._in_suite_introducer
535 )
536 else:
537 assert node.type != IndentationTypes.IMPLICIT
538 self._indentation_tos = self._indentation_tos.parent
539 elif value in ('=', ':') and self._implicit_indentation_possible \
540 and part.parent.type in _IMPLICIT_INDENTATION_TYPES:
541 indentation = node.indentation
542 self._indentation_tos = ImplicitNode(
543 self._config, part, parent=self._indentation_tos
544 )
545
546 self._on_newline = type_ in ('newline', 'backslash', 'bom')
547
548 self._previous_part = part
549 self._previous_spacing = spacing
550
551 def _check_line_length(self, part, spacing):
552 if part.type == 'backslash':
553 last_column = part.start_pos[1] + 1
554 else:
555 last_column = part.end_pos[1]
556 if last_column > self._config.max_characters \
557 and spacing.start_pos[1] <= self._config.max_characters:
558 # Special case for long URLs in multi-line docstrings or comments,
559 # but still report the error when the 72 first chars are whitespaces.
560 report = True
561 if part.type == 'comment':
562 splitted = part.value[1:].split()
563 if len(splitted) == 1 \
564 and (part.end_pos[1] - len(splitted[0])) < 72:
565 report = False
566 if report:
567 self.add_issue(
568 part,
569 501,
570 'Line too long (%s > %s characters)' %
571 (last_column, self._config.max_characters),
572 )
573
574 def _check_spacing(self, part, spacing):
575 def add_if_spaces(*args):
576 if spaces:
577 return self.add_issue(*args)
578
579 def add_not_spaces(*args):
580 if not spaces:
581 return self.add_issue(*args)
582
583 spaces = spacing.value
584 prev = self._previous_part
585 if prev is not None and prev.type == 'error_leaf' or part.type == 'error_leaf':
586 return
587
588 type_ = part.type
589 if '\t' in spaces:
590 self.add_issue(spacing, 223, 'Used tab to separate tokens')
591 elif type_ == 'comment':
592 if len(spaces) < self._config.spaces_before_comment:
593 self.add_issue(spacing, 261, 'At least two spaces before inline comment')
594 elif type_ == 'newline':
595 add_if_spaces(spacing, 291, 'Trailing whitespace')
596 elif len(spaces) > 1:
597 self.add_issue(spacing, 221, 'Multiple spaces used')
598 else:
599 if prev in _OPENING_BRACKETS:
600 message = "Whitespace after '%s'" % part.value
601 add_if_spaces(spacing, 201, message)
602 elif part in _CLOSING_BRACKETS:
603 message = "Whitespace before '%s'" % part.value
604 add_if_spaces(spacing, 202, message)
605 elif part in (',', ';') or part == ':' \
606 and part.parent.type not in _POSSIBLE_SLICE_PARENTS:
607 message = "Whitespace before '%s'" % part.value
608 add_if_spaces(spacing, 203, message)
609 elif prev == ':' and prev.parent.type in _POSSIBLE_SLICE_PARENTS:
610 pass # TODO
611 elif prev in (',', ';', ':'):
612 add_not_spaces(spacing, 231, "missing whitespace after '%s'")
613 elif part == ':': # Is a subscript
614 # TODO
615 pass
616 elif part in ('*', '**') and part.parent.type not in _NON_STAR_TYPES \
617 or prev in ('*', '**') \
618 and prev.parent.type not in _NON_STAR_TYPES:
619 # TODO
620 pass
621 elif prev in _FACTOR and prev.parent.type == 'factor':
622 pass
623 elif prev == '@' and prev.parent.type == 'decorator':
624 pass # TODO should probably raise an error if there's a space here
625 elif part in _NEEDS_SPACE or prev in _NEEDS_SPACE:
626 if part == '=' and part.parent.type in ('argument', 'param') \
627 or prev == '=' and prev.parent.type in ('argument', 'param'):
628 if part == '=':
629 param = part.parent
630 else:
631 param = prev.parent
632 if param.type == 'param' and param.annotation:
633 add_not_spaces(spacing, 252, 'Expected spaces around annotation equals')
634 else:
635 add_if_spaces(
636 spacing,
637 251,
638 'Unexpected spaces around keyword / parameter equals'
639 )
640 elif part in _BITWISE_OPERATOR or prev in _BITWISE_OPERATOR:
641 add_not_spaces(
642 spacing,
643 227,
644 'Missing whitespace around bitwise or shift operator'
645 )
646 elif part == '%' or prev == '%':
647 add_not_spaces(spacing, 228, 'Missing whitespace around modulo operator')
648 else:
649 message_225 = 'Missing whitespace between tokens'
650 add_not_spaces(spacing, 225, message_225)
651 elif type_ == 'keyword' or prev.type == 'keyword':
652 add_not_spaces(spacing, 275, 'Missing whitespace around keyword')
653 else:
654 prev_spacing = self._previous_spacing
655 if prev in _ALLOW_SPACE and spaces != prev_spacing.value \
656 and '\n' not in self._previous_leaf.prefix \
657 and '\r' not in self._previous_leaf.prefix:
658 message = "Whitespace before operator doesn't match with whitespace after"
659 self.add_issue(spacing, 229, message)
660
661 if spaces and part not in _ALLOW_SPACE and prev not in _ALLOW_SPACE:
662 message_225 = 'Missing whitespace between tokens'
663 # self.add_issue(spacing, 225, message_225)
664 # TODO why only brackets?
665 if part in _OPENING_BRACKETS:
666 message = "Whitespace before '%s'" % part.value
667 add_if_spaces(spacing, 211, message)
668
669 def _analyse_non_prefix(self, leaf):
670 typ = leaf.type
671 if typ == 'name' and leaf.value in ('l', 'O', 'I'):
672 if leaf.is_definition():
673 message = "Do not define %s named 'l', 'O', or 'I' one line"
674 if leaf.parent.type == 'class' and leaf.parent.name == leaf:
675 self.add_issue(leaf, 742, message % 'classes')
676 elif leaf.parent.type == 'function' and leaf.parent.name == leaf:
677 self.add_issue(leaf, 743, message % 'function')
678 else:
679 self.add_issuadd_issue(741, message % 'variables', leaf)
680 elif leaf.value == ':':
681 if isinstance(leaf.parent, (Flow, Scope)) and leaf.parent.type != 'lambdef':
682 next_leaf = leaf.get_next_leaf()
683 if next_leaf.type != 'newline':
684 if leaf.parent.type == 'funcdef':
685 self.add_issue(next_leaf, 704, 'Multiple statements on one line (def)')
686 else:
687 self.add_issue(next_leaf, 701, 'Multiple statements on one line (colon)')
688 elif leaf.value == ';':
689 if leaf.get_next_leaf().type in ('newline', 'endmarker'):
690 self.add_issue(leaf, 703, 'Statement ends with a semicolon')
691 else:
692 self.add_issue(leaf, 702, 'Multiple statements on one line (semicolon)')
693 elif leaf.value in ('==', '!='):
694 comparison = leaf.parent
695 index = comparison.children.index(leaf)
696 left = comparison.children[index - 1]
697 right = comparison.children[index + 1]
698 for node in left, right:
699 if node.type == 'keyword' or node.type == 'name':
700 if node.value == 'None':
701 message = "comparison to None should be 'if cond is None:'"
702 self.add_issue(leaf, 711, message)
703 break
704 elif node.value in ('True', 'False'):
705 message = "comparison to False/True should be " \
706 "'if cond is True:' or 'if cond:'"
707 self.add_issue(leaf, 712, message)
708 break
709 elif leaf.value in ('in', 'is'):
710 comparison = leaf.parent
711 if comparison.type == 'comparison' and comparison.parent.type == 'not_test':
712 if leaf.value == 'in':
713 self.add_issue(leaf, 713, "test for membership should be 'not in'")
714 else:
715 self.add_issue(leaf, 714, "test for object identity should be 'is not'")
716 elif typ == 'string':
717 # Checking multiline strings
718 for i, line in enumerate(leaf.value.splitlines()[1:]):
719 indentation = re.match(r'[ \t]*', line).group(0)
720 start_pos = leaf.line + i, len(indentation)
721 # TODO check multiline indentation.
722 start_pos
723 elif typ == 'endmarker':
724 if self._newline_count >= 2:
725 self.add_issue(leaf, 391, 'Blank line at end of file')
726
727 def add_issue(self, node, code, message):
728 if self._previous_leaf is not None:
729 if self._previous_leaf.search_ancestor('error_node') is not None:
730 return
731 if self._previous_leaf.type == 'error_leaf':
732 return
733 if node.search_ancestor('error_node') is not None:
734 return
735 if code in (901, 903):
736 # 901 and 903 are raised by the ErrorFinder.
737 super().add_issue(node, code, message)
738 else:
739 # Skip ErrorFinder here, because it has custom behavior.
740 super(ErrorFinder, self).add_issue(node, code, message)
741
742
743class PEP8NormalizerConfig(ErrorFinderConfig):
744 normalizer_class = PEP8Normalizer
745 """
746 Normalizing to PEP8. Not really implemented, yet.
747 """
748 def __init__(self, indentation=' ' * 4, hanging_indentation=None,
749 max_characters=79, spaces_before_comment=2):
750 self.indentation = indentation
751 if hanging_indentation is None:
752 hanging_indentation = indentation
753 self.hanging_indentation = hanging_indentation
754 self.closing_bracket_hanging_indentation = ''
755 self.break_after_binary = False
756 self.max_characters = max_characters
757 self.spaces_before_comment = spaces_before_comment
758
759
760# TODO this is not yet ready.
761# @PEP8Normalizer.register_rule(type='endmarker')
762class BlankLineAtEnd(Rule):
763 code = 392
764 message = 'Blank line at end of file'
765
766 def is_issue(self, leaf):
767 return self._newline_count >= 2